Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26,783 changes: 26,652 additions & 131 deletions optimus-plugin/dist/mcp-server.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions optimus-plugin/dist/mcp-server.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/managers/MemoryManager.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { PersistentAgentAdapter } from '../adapters/PersistentAgentAdapter';
import { debugLog } from '../debugLogger';

export class MemoryManager {
private static readonly memoryFileName = 'state/memory.md';
private static readonly optimusDir = '.optimus';

private getMemoryFilePath(): string | null {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { return null; }
return path.join(workspaceFolders[0].uri.fsPath, MemoryManager.optimusDir, MemoryManager.memoryFileName);
const rootPath = PersistentAgentAdapter.getWorkspacePath();
if (!rootPath) { return null; }
return path.join(rootPath, MemoryManager.optimusDir, MemoryManager.memoryFileName);
}

public readMemory(): string | null {
Expand Down
121 changes: 107 additions & 14 deletions src/managers/SharedTaskStateManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { PersistentAgentAdapter } from '../adapters/PersistentAgentAdapter';
import {
CompleteTurnInput,
Expand All @@ -11,15 +10,110 @@ import {
TurnRecord,
} from '../types/SharedTaskContext';

export interface StateStore {
get<T>(key: string, defaultValue?: T): T;
update(key: string, value: any): Promise<void>;
}

export interface OptimusConfig {
get<T>(key: string): T | undefined;
}

export class FileSystemStateStore implements StateStore {
private filePath: string;
private cache: Record<string, any> | undefined;

constructor(workspacePath: string) {
const dir = path.join(workspacePath, '.optimus', 'state');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
this.filePath = path.join(dir, 'state.json');
}

private load(): Record<string, any> {
if (this.cache) return this.cache;
try {
if (fs.existsSync(this.filePath)) {
this.cache = JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
} else {
this.cache = {};
}
} catch {
this.cache = {};
}
return this.cache!;
}

public get<T>(key: string, defaultValue?: T): T {
const data = this.load();
return data[key] !== undefined ? data[key] : (defaultValue as T);
}

public async update(key: string, value: any): Promise<void> {
const data = this.load();
data[key] = value;
fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), 'utf8');
this.cache = data;
}
}

export class FileSystemConfig implements OptimusConfig {
private filePath: string;
private cache: Record<string, any> | undefined;

constructor(workspacePath: string) {
this.filePath = path.join(workspacePath, 'package.json');
}

private load(): Record<string, any> {
if (this.cache) return this.cache;
try {
if (fs.existsSync(this.filePath)) {
const pkg = JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
this.cache = pkg.contributes?.configuration?.properties || {};
} else {
this.cache = {};
}
} catch {
this.cache = {};
}
return this.cache!;
}

public get<T>(key: string): T | undefined {
const data = this.load();
const settingsPath = path.join(PersistentAgentAdapter.getWorkspacePath(), '.vscode', 'settings.json');
try {
if (fs.existsSync(settingsPath)) {
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
if (settings[`optimusCode.${key}`] !== undefined) {
return settings[`optimusCode.${key}`] as T;
}
}
} catch {
// ignore
}
return data[`optimusCode.${key}`]?.default as T | undefined;
}
}

export class SharedTaskStateManager {
private static readonly storageKey = 'optimusTaskStates';
private static readonly maxTasks = 25;
private static readonly defaultCompactThreshold = 800000;

constructor(private readonly globalState: vscode.Memento) {}

private globalState: StateStore;
private config: OptimusConfig;

constructor(workspacePath?: string) {
const resolvedWorkspace = workspacePath || PersistentAgentAdapter.getWorkspacePath();
this.globalState = new FileSystemStateStore(resolvedWorkspace);
this.config = new FileSystemConfig(resolvedWorkspace);
}

public getCompactThreshold(): number {
const configured = vscode.workspace.getConfiguration('optimusCode').get<number>('compactThresholdTokens');
const configured = this.config.get<number>('compactThresholdTokens');
if (typeof configured !== 'number' || !Number.isFinite(configured) || configured < 1000) {
return SharedTaskStateManager.defaultCompactThreshold;
}
Expand Down Expand Up @@ -773,9 +867,8 @@ export class SharedTaskStateManager {

public readRulesMd(): string | null {
try {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { return null; }
const rootPath = workspaceFolders[0].uri.fsPath;
const rootPath = PersistentAgentAdapter.getWorkspacePath();
if (!rootPath) { return null; }
const rulesPath = path.join(rootPath, '.optimus', 'config', 'system-instructions.md');

let rulesContent = "";
Expand All @@ -790,7 +883,7 @@ export class SharedTaskStateManager {
}

// Inject available engines and models dynamically from settings
const modelsConfig = vscode.workspace.getConfiguration('optimusCode').get<any>('models');
const modelsConfig = this.config.get<any>('models');
if (modelsConfig) {
rulesContent += `\n\n## Available CLI Engines and Models (Dynamic)\n`;
if (modelsConfig.claude_code) {
Expand All @@ -809,9 +902,9 @@ export class SharedTaskStateManager {

public readMemoryMd(): string | null {
try {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { return null; }
const memPath = path.join(workspaceFolders[0].uri.fsPath, '.optimus', 'state', 'memory.md');
const rootPath = PersistentAgentAdapter.getWorkspacePath();
if (!rootPath) { return null; }
const memPath = path.join(rootPath, '.optimus', 'state', 'memory.md');
if (!fs.existsSync(memPath)) { return null; }
return fs.readFileSync(memPath, 'utf8');
} catch {
Expand All @@ -821,9 +914,9 @@ export class SharedTaskStateManager {

public writeMemoryMd(newContent: string): void {
try {
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) { return; }
const optimusDir = path.join(workspaceFolders[0].uri.fsPath, '.optimus');
const rootPath = PersistentAgentAdapter.getWorkspacePath();
if (!rootPath) { return; }
const optimusDir = path.join(rootPath, '.optimus');
if (!fs.existsSync(optimusDir)) { fs.mkdirSync(optimusDir, { recursive: true }); }
const memPath = path.join(optimusDir, 'state', 'memory.md');
fs.writeFileSync(memPath, newContent, 'utf8');
Expand Down
13 changes: 6 additions & 7 deletions src/mcp/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
},
required: ["owner", "repo", "pull_number"]
}
},,
},
{
name: "github_sync_board",
description: "Fetches open issues from a GitHub repository and dumps them into the local blackboard.",
Expand Down Expand Up @@ -194,7 +194,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
description: "An array of expert roles to spawn concurrently (e.g., ['security-expert', 'performance-tyrant'])",
},
},
required: ["proposal_path", "roles", "workspace_path"],
required: ["proposal_path", "roles"],
},
},
{
Expand Down Expand Up @@ -246,11 +246,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "dispatch_council") {
let { proposal_path, roles, workspace_path } = request.params.arguments as any;

if (!workspace_path || !proposal_path || !Array.isArray(roles) || roles.length === 0) {
if (!proposal_path || !Array.isArray(roles) || roles.length === 0) {
throw new McpError(ErrorCode.InvalidParams, "Invalid arguments: requires proposal_path and an array of roles");
}

const timestampId = Date.now();

// Resolve workspace root from the proposal_path instead of process.cwd().
// Global MCP servers boot in the user home directory, so we must calculate the project root dynamically.
Expand All @@ -265,6 +263,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
workspacePath = path.resolve(path.dirname(proposal_path));
}

const timestampId = Date.now();
const reviewsPath = path.join(workspacePath, ".optimus", "reviews", timestampId.toString());

fs.mkdirSync(reviewsPath, { recursive: true });
Expand Down Expand Up @@ -492,8 +491,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
throw new McpError(ErrorCode.InvalidParams, "Invalid arguments: requires workspace_path");
}

const t1Dir = path.join(workspace_path, ".optimus", "personas");
const t2Dir = path.join(__dirname, "..", "agents");
const t1Dir = path.join(workspace_path, ".optimus", "agents");
const t2Dir = path.join(__dirname, "..", "..", "optimus-plugin", "roles");

let roster = "📋 **Spartan Swarm Active Roster**\n\n";

Expand Down
Loading