ai-cli-mcp 2.12.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +25 -0
- package/CHANGELOG.md +20 -0
- package/README.ja.md +20 -5
- package/README.md +20 -6
- package/dist/__tests__/app-cli.test.js +34 -2
- package/dist/__tests__/cli-bin-smoke.test.js +4 -0
- package/dist/__tests__/cli-builder.test.js +37 -0
- package/dist/__tests__/cli-process-service.test.js +180 -5
- package/dist/__tests__/cli-utils.test.js +31 -0
- package/dist/__tests__/mcp-contract.test.js +287 -9
- package/dist/__tests__/parsers.test.js +37 -1
- package/dist/__tests__/process-management.test.js +2 -1
- package/dist/app/cli.js +8 -6
- package/dist/app/mcp.js +16 -8
- package/dist/cli-builder.js +14 -0
- package/dist/cli-parse.js +8 -5
- package/dist/cli-process-service.js +13 -23
- package/dist/cli-utils.js +17 -0
- package/dist/cli.js +4 -3
- package/dist/model-catalog.js +4 -1
- package/dist/parsers.js +55 -0
- package/dist/process-result.js +51 -0
- package/dist/process-service.js +11 -22
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/server.json +1 -1
- package/src/__tests__/app-cli.test.ts +43 -1
- package/src/__tests__/cli-bin-smoke.test.ts +4 -0
- package/src/__tests__/cli-builder.test.ts +47 -0
- package/src/__tests__/cli-process-service.test.ts +200 -5
- package/src/__tests__/cli-utils.test.ts +34 -0
- package/src/__tests__/mcp-contract.test.ts +325 -9
- package/src/__tests__/parsers.test.ts +44 -1
- package/src/__tests__/process-management.test.ts +2 -1
- package/src/app/cli.ts +9 -7
- package/src/app/mcp.ts +17 -8
- package/src/cli-builder.ts +18 -3
- package/src/cli-parse.ts +8 -5
- package/src/cli-process-service.ts +12 -23
- package/src/cli-utils.ts +21 -1
- package/src/cli.ts +4 -3
- package/src/model-catalog.ts +5 -1
- package/src/parsers.ts +61 -0
- package/src/process-result.ts +79 -0
- package/src/process-service.ts +11 -24
- package/src/server.ts +1 -1
package/src/cli-parse.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseClaudeOutput, parseCodexOutput, parseGeminiOutput } from './parsers.js';
|
|
2
|
+
import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput } from './parsers.js';
|
|
3
3
|
|
|
4
|
-
const AGENTS = ['claude', 'codex', 'gemini'] as const;
|
|
4
|
+
const AGENTS = ['claude', 'codex', 'gemini', 'forge'] as const;
|
|
5
5
|
type Agent = typeof AGENTS[number];
|
|
6
6
|
|
|
7
|
-
const USAGE = `Usage: npm run -s cli.run.parse -- --agent <claude|codex|gemini>
|
|
7
|
+
const USAGE = `Usage: npm run -s cli.run.parse -- --agent <claude|codex|gemini|forge>
|
|
8
8
|
|
|
9
9
|
Reads raw CLI output from stdin and outputs parsed JSON to stdout.
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
-
--agent Agent type: claude, codex, or
|
|
12
|
+
--agent Agent type: claude, codex, gemini, or forge (required)
|
|
13
13
|
--help Show this help message
|
|
14
14
|
|
|
15
15
|
Examples:
|
|
@@ -62,7 +62,7 @@ async function main(): Promise<void> {
|
|
|
62
62
|
|
|
63
63
|
const agent = args.agent as Agent;
|
|
64
64
|
if (!agent || !AGENTS.includes(agent)) {
|
|
65
|
-
process.stderr.write(`Error: --agent is required (claude, codex, or
|
|
65
|
+
process.stderr.write(`Error: --agent is required (claude, codex, gemini, or forge)\n\n`);
|
|
66
66
|
process.stderr.write(USAGE);
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
|
@@ -85,6 +85,9 @@ async function main(): Promise<void> {
|
|
|
85
85
|
case 'gemini':
|
|
86
86
|
parsed = parseGeminiOutput(input);
|
|
87
87
|
break;
|
|
88
|
+
case 'forge':
|
|
89
|
+
parsed = parseForgeOutput(input);
|
|
90
|
+
break;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
process.stdout.write(JSON.stringify(parsed, null, 2) + '\n');
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { homedir } from 'node:os';
|
|
17
17
|
import { buildCliCommand, type BuildCliCommandOptions } from './cli-builder.js';
|
|
18
|
-
import { findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
19
|
-
import { parseClaudeOutput, parseCodexOutput, parseGeminiOutput } from './parsers.js';
|
|
18
|
+
import { findClaudeCli, findCodexCli, findForgeCli, findGeminiCli } from './cli-utils.js';
|
|
19
|
+
import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput } from './parsers.js';
|
|
20
|
+
import { buildProcessResult } from './process-result.js';
|
|
20
21
|
import type { AgentType, ProcessListItem } from './process-service.js';
|
|
21
22
|
|
|
22
23
|
interface StoredProcess {
|
|
@@ -78,6 +79,7 @@ export class CliProcessService {
|
|
|
78
79
|
claude: findClaudeCli(),
|
|
79
80
|
codex: findCodexCli(),
|
|
80
81
|
gemini: findGeminiCli(),
|
|
82
|
+
forge: findForgeCli(),
|
|
81
83
|
};
|
|
82
84
|
mkdirSync(this.stateDir, { recursive: true });
|
|
83
85
|
}
|
|
@@ -177,10 +179,12 @@ export class CliProcessService {
|
|
|
177
179
|
agentOutput = parseClaudeOutput(stdout);
|
|
178
180
|
} else if (refreshed.toolType === 'gemini') {
|
|
179
181
|
agentOutput = parseGeminiOutput(stdout);
|
|
182
|
+
} else if (refreshed.toolType === 'forge') {
|
|
183
|
+
agentOutput = parseForgeOutput(stdout);
|
|
180
184
|
}
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
|
|
187
|
+
return buildProcessResult({
|
|
184
188
|
pid,
|
|
185
189
|
agent: refreshed.toolType,
|
|
186
190
|
status: refreshed.status,
|
|
@@ -189,27 +193,12 @@ export class CliProcessService {
|
|
|
189
193
|
workFolder: refreshed.workFolder,
|
|
190
194
|
prompt: refreshed.prompt,
|
|
191
195
|
model: refreshed.model,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (!verbose && agentOutput.tools) {
|
|
196
|
-
const { tools, ...rest } = agentOutput;
|
|
197
|
-
response.agentOutput = rest;
|
|
198
|
-
} else {
|
|
199
|
-
response.agentOutput = agentOutput;
|
|
200
|
-
}
|
|
201
|
-
if (agentOutput.session_id) {
|
|
202
|
-
response.session_id = agentOutput.session_id;
|
|
203
|
-
}
|
|
204
|
-
} else {
|
|
205
|
-
response.stdout = stdout;
|
|
206
|
-
response.stderr = stderr;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return response;
|
|
196
|
+
stdout,
|
|
197
|
+
stderr,
|
|
198
|
+
}, agentOutput, verbose);
|
|
210
199
|
}
|
|
211
200
|
|
|
212
|
-
async waitForProcesses(pids: number[], timeoutSeconds = 180): Promise<any[]> {
|
|
201
|
+
async waitForProcesses(pids: number[], timeoutSeconds = 180, verbose = false): Promise<any[]> {
|
|
213
202
|
const start = Date.now();
|
|
214
203
|
for (const pid of pids) {
|
|
215
204
|
this.readProcess(pid);
|
|
@@ -218,7 +207,7 @@ export class CliProcessService {
|
|
|
218
207
|
while (true) {
|
|
219
208
|
const statuses = pids.map((pid) => this.refreshStatus(this.readProcess(pid)).status);
|
|
220
209
|
if (statuses.every((status) => status !== 'running')) {
|
|
221
|
-
return Promise.all(pids.map((pid) => this.getProcessResult(pid,
|
|
210
|
+
return Promise.all(pids.map((pid) => this.getProcessResult(pid, verbose)));
|
|
222
211
|
}
|
|
223
212
|
|
|
224
213
|
if (Date.now() - start >= timeoutSeconds * 1000) {
|
package/src/cli-utils.ts
CHANGED
|
@@ -148,7 +148,7 @@ function isExecutableFile(filePath: string): boolean {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
type CliBinaryName = 'claude' | 'codex' | 'gemini';
|
|
151
|
+
type CliBinaryName = 'claude' | 'codex' | 'gemini' | 'forge';
|
|
152
152
|
|
|
153
153
|
function getCliBinaryConfig(name: CliBinaryName): {
|
|
154
154
|
envVarName: string;
|
|
@@ -174,6 +174,15 @@ function getCliBinaryConfig(name: CliBinaryName): {
|
|
|
174
174
|
};
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
if (name === 'forge') {
|
|
178
|
+
return {
|
|
179
|
+
envVarName: 'FORGE_CLI_NAME',
|
|
180
|
+
customCliName: process.env.FORGE_CLI_NAME,
|
|
181
|
+
defaultCliName: 'forge',
|
|
182
|
+
localInstallPath: join(homedir(), '.forge', 'local', 'forge'),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
177
186
|
return {
|
|
178
187
|
envVarName: 'GEMINI_CLI_NAME',
|
|
179
188
|
customCliName: process.env.GEMINI_CLI_NAME,
|
|
@@ -190,11 +199,13 @@ export function getCliDoctorStatus(): {
|
|
|
190
199
|
claude: CliBinaryStatus;
|
|
191
200
|
codex: CliBinaryStatus;
|
|
192
201
|
gemini: CliBinaryStatus;
|
|
202
|
+
forge: CliBinaryStatus;
|
|
193
203
|
} {
|
|
194
204
|
return {
|
|
195
205
|
claude: getCliBinaryStatus('claude'),
|
|
196
206
|
codex: getCliBinaryStatus('codex'),
|
|
197
207
|
gemini: getCliBinaryStatus('gemini'),
|
|
208
|
+
forge: getCliBinaryStatus('forge'),
|
|
198
209
|
};
|
|
199
210
|
}
|
|
200
211
|
|
|
@@ -218,6 +229,15 @@ export function findCodexCli(): string {
|
|
|
218
229
|
return getCliCommandOrThrow(status);
|
|
219
230
|
}
|
|
220
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Determine the Forge CLI command/path.
|
|
234
|
+
*/
|
|
235
|
+
export function findForgeCli(): string {
|
|
236
|
+
debugLog('[Debug] Attempting to find Forge CLI...');
|
|
237
|
+
const status = getCliBinaryStatus('forge');
|
|
238
|
+
return getCliCommandOrThrow(status);
|
|
239
|
+
}
|
|
240
|
+
|
|
221
241
|
/**
|
|
222
242
|
* Determine the Claude CLI command/path.
|
|
223
243
|
* 1. Checks for CLAUDE_CLI_NAME environment variable:
|
package/src/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { buildCliCommand } from './cli-builder.js';
|
|
4
|
-
import { findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
4
|
+
import { findClaudeCli, findCodexCli, findForgeCli, findGeminiCli } from './cli-utils.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Minimal argv parser. No external dependencies.
|
|
@@ -35,12 +35,12 @@ function parseArgs(argv: string[]): Record<string, string> {
|
|
|
35
35
|
const USAGE = `Usage: npm run -s cli.run -- --model <model> --workFolder <path> --prompt "..." [options]
|
|
36
36
|
|
|
37
37
|
Options:
|
|
38
|
-
--model Model name or alias (e.g. sonnet, opus, gpt-5.2-codex, gemini-2.5-pro)
|
|
38
|
+
--model Model name or alias (e.g. sonnet, opus, gpt-5.2-codex, gemini-2.5-pro, forge)
|
|
39
39
|
--workFolder Working directory (absolute path)
|
|
40
40
|
--prompt Prompt string (mutually exclusive with --prompt_file)
|
|
41
41
|
--prompt_file Path to a file containing the prompt
|
|
42
42
|
--session_id Session ID to resume
|
|
43
|
-
--reasoning_effort Claude/Codex: Claude=low|medium|high, Codex=low|medium|high|xhigh
|
|
43
|
+
--reasoning_effort Claude/Codex only: Claude=low|medium|high, Codex=low|medium|high|xhigh
|
|
44
44
|
--help Show this help message
|
|
45
45
|
|
|
46
46
|
Raw CLI output goes to stdout. Use cli.run.parse to parse the output:
|
|
@@ -73,6 +73,7 @@ async function main(): Promise<void> {
|
|
|
73
73
|
claude: findClaudeCli(),
|
|
74
74
|
codex: findCodexCli(),
|
|
75
75
|
gemini: findGeminiCli(),
|
|
76
|
+
forge: findForgeCli(),
|
|
76
77
|
};
|
|
77
78
|
|
|
78
79
|
// Build command
|
package/src/model-catalog.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const GEMINI_MODELS = [
|
|
|
19
19
|
'gemini-3-pro-preview',
|
|
20
20
|
'gemini-3-flash-preview',
|
|
21
21
|
] as const;
|
|
22
|
+
export const FORGE_MODELS = ['forge'] as const;
|
|
22
23
|
|
|
23
24
|
export const MODEL_ALIASES: Record<string, string> = {
|
|
24
25
|
'claude-ultra': 'opus',
|
|
@@ -38,11 +39,12 @@ export function getSupportedModelsDescription(): string {
|
|
|
38
39
|
...CLAUDE_MODELS.map((model) => `"${model}"`),
|
|
39
40
|
...CODEX_MODELS.map((model) => `"${model}"`),
|
|
40
41
|
...GEMINI_MODELS.map((model) => `"${model}"`),
|
|
42
|
+
...FORGE_MODELS.map((model) => `"${model}"`),
|
|
41
43
|
].join(', ');
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export function getModelParameterDescription(): string {
|
|
45
|
-
return `The model to use. Aliases: "claude-ultra" (auto high effort), "codex-ultra" (auto xhigh reasoning), "gemini-ultra". Standard: ${[...CLAUDE_MODELS, ...CODEX_MODELS, ...GEMINI_MODELS].map((model) => `"${model}"`).join(', ')}.`;
|
|
47
|
+
return `The model to use. Aliases: "claude-ultra" (auto high effort), "codex-ultra" (auto xhigh reasoning), "gemini-ultra". Standard: ${[...CLAUDE_MODELS, ...CODEX_MODELS, ...GEMINI_MODELS, ...FORGE_MODELS].map((model) => `"${model}"`).join(', ')}. "forge" is a provider key, not a Forge model family selector.`;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export function getModelsPayload(): {
|
|
@@ -50,11 +52,13 @@ export function getModelsPayload(): {
|
|
|
50
52
|
claude: ReadonlyArray<string>;
|
|
51
53
|
codex: ReadonlyArray<string>;
|
|
52
54
|
gemini: ReadonlyArray<string>;
|
|
55
|
+
forge: ReadonlyArray<string>;
|
|
53
56
|
} {
|
|
54
57
|
return {
|
|
55
58
|
aliases: MODEL_ALIAS_DETAILS,
|
|
56
59
|
claude: CLAUDE_MODELS,
|
|
57
60
|
codex: CODEX_MODELS,
|
|
58
61
|
gemini: GEMINI_MODELS,
|
|
62
|
+
forge: FORGE_MODELS,
|
|
59
63
|
};
|
|
60
64
|
}
|
package/src/parsers.ts
CHANGED
|
@@ -167,3 +167,64 @@ export function parseGeminiOutput(stdout: string): any {
|
|
|
167
167
|
return null;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Parse Forge output framed by Initialize/Continue/Finished markers.
|
|
173
|
+
*/
|
|
174
|
+
export function parseForgeOutput(stdout: string): any {
|
|
175
|
+
if (!stdout) return null;
|
|
176
|
+
|
|
177
|
+
const lines = stdout.split('\n');
|
|
178
|
+
const markerPattern = /^● \[[^\]]+\] (Initialize|Continue|Finished) (\S+)\s*$/;
|
|
179
|
+
let collecting = false;
|
|
180
|
+
let currentConversationId: string | null = null;
|
|
181
|
+
let currentBody: string[] = [];
|
|
182
|
+
let lastConversationId: string | null = null;
|
|
183
|
+
let lastMessage: string | null = null;
|
|
184
|
+
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
const match = line.match(markerPattern);
|
|
187
|
+
if (match) {
|
|
188
|
+
const [, action, conversationId] = match;
|
|
189
|
+
lastConversationId = conversationId;
|
|
190
|
+
|
|
191
|
+
if (action === 'Initialize' || action === 'Continue') {
|
|
192
|
+
collecting = true;
|
|
193
|
+
currentConversationId = conversationId;
|
|
194
|
+
currentBody = [];
|
|
195
|
+
} else if (collecting && currentConversationId === conversationId) {
|
|
196
|
+
const message = currentBody.join('\n').trim();
|
|
197
|
+
if (message) {
|
|
198
|
+
lastMessage = message;
|
|
199
|
+
}
|
|
200
|
+
collecting = false;
|
|
201
|
+
currentConversationId = null;
|
|
202
|
+
currentBody = [];
|
|
203
|
+
}
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (collecting) {
|
|
208
|
+
currentBody.push(line);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (collecting) {
|
|
213
|
+
const message = currentBody.join('\n').trim();
|
|
214
|
+
if (message) {
|
|
215
|
+
lastMessage = message;
|
|
216
|
+
}
|
|
217
|
+
if (currentConversationId) {
|
|
218
|
+
lastConversationId = currentConversationId;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!lastMessage && !lastConversationId) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
message: lastMessage,
|
|
228
|
+
session_id: lastConversationId,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { AgentType, ProcessStatus } from './process-service.js';
|
|
2
|
+
|
|
3
|
+
interface ProcessResultContext {
|
|
4
|
+
pid: number;
|
|
5
|
+
agent: AgentType;
|
|
6
|
+
status: ProcessStatus;
|
|
7
|
+
exitCode?: number;
|
|
8
|
+
startTime: string;
|
|
9
|
+
workFolder: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function compactAgentOutput(agentOutput: any): any | null {
|
|
17
|
+
if (!agentOutput || typeof agentOutput !== 'object') {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { tools: _tools, ...rest } = agentOutput;
|
|
22
|
+
const compact = Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== undefined && value !== null));
|
|
23
|
+
return Object.keys(compact).length > 0 ? compact : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function hasMeaningfulParsedOutput(agentOutput: any): boolean {
|
|
27
|
+
if (!agentOutput || typeof agentOutput !== 'object') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Object.entries(agentOutput).some(([key, value]) => {
|
|
32
|
+
if (value === undefined || value === null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (key === 'session_id') {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (key === 'tools') {
|
|
41
|
+
return Array.isArray(value) ? value.length > 0 : true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildProcessResult(context: ProcessResultContext, agentOutput: any, verbose = false): any {
|
|
49
|
+
const response: any = {
|
|
50
|
+
pid: context.pid,
|
|
51
|
+
agent: context.agent,
|
|
52
|
+
status: context.status,
|
|
53
|
+
exitCode: context.exitCode ?? null,
|
|
54
|
+
model: context.model ?? null,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (verbose) {
|
|
58
|
+
response.startTime = context.startTime;
|
|
59
|
+
response.workFolder = context.workFolder;
|
|
60
|
+
response.prompt = context.prompt;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (agentOutput?.session_id) {
|
|
64
|
+
response.session_id = agentOutput.session_id;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const shapedAgentOutput = verbose ? agentOutput : compactAgentOutput(agentOutput);
|
|
68
|
+
|
|
69
|
+
if (hasMeaningfulParsedOutput(shapedAgentOutput)) {
|
|
70
|
+
response.agentOutput = shapedAgentOutput;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!response.agentOutput) {
|
|
74
|
+
response.stdout = context.stdout;
|
|
75
|
+
response.stderr = context.stderr;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return response;
|
|
79
|
+
}
|
package/src/process-service.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from 'node:child_process';
|
|
2
2
|
import { buildCliCommand, type BuildCliCommandOptions } from './cli-builder.js';
|
|
3
|
-
import { parseClaudeOutput, parseCodexOutput, parseGeminiOutput } from './parsers.js';
|
|
3
|
+
import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput } from './parsers.js';
|
|
4
|
+
import { buildProcessResult } from './process-result.js';
|
|
4
5
|
|
|
5
|
-
export type AgentType = 'claude' | 'codex' | 'gemini';
|
|
6
|
+
export type AgentType = 'claude' | 'codex' | 'gemini' | 'forge';
|
|
6
7
|
export type ProcessStatus = 'running' | 'completed' | 'failed';
|
|
7
8
|
|
|
8
9
|
interface TrackedProcess {
|
|
@@ -144,10 +145,12 @@ export class ProcessService {
|
|
|
144
145
|
agentOutput = parseClaudeOutput(process.stdout);
|
|
145
146
|
} else if (process.toolType === 'gemini') {
|
|
146
147
|
agentOutput = parseGeminiOutput(process.stdout);
|
|
148
|
+
} else if (process.toolType === 'forge') {
|
|
149
|
+
agentOutput = parseForgeOutput(process.stdout);
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
return buildProcessResult({
|
|
151
154
|
pid,
|
|
152
155
|
agent: process.toolType,
|
|
153
156
|
status: process.status,
|
|
@@ -156,28 +159,12 @@ export class ProcessService {
|
|
|
156
159
|
workFolder: process.workFolder,
|
|
157
160
|
prompt: process.prompt,
|
|
158
161
|
model: process.model,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!verbose && agentOutput.tools) {
|
|
163
|
-
const { tools, ...rest } = agentOutput;
|
|
164
|
-
response.agentOutput = rest;
|
|
165
|
-
} else {
|
|
166
|
-
response.agentOutput = agentOutput;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (agentOutput.session_id) {
|
|
170
|
-
response.session_id = agentOutput.session_id;
|
|
171
|
-
}
|
|
172
|
-
} else {
|
|
173
|
-
response.stdout = process.stdout;
|
|
174
|
-
response.stderr = process.stderr;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return response;
|
|
162
|
+
stdout: process.stdout,
|
|
163
|
+
stderr: process.stderr,
|
|
164
|
+
}, agentOutput, verbose);
|
|
178
165
|
}
|
|
179
166
|
|
|
180
|
-
async waitForProcesses(pids: number[], timeoutSeconds = 180): Promise<any[]> {
|
|
167
|
+
async waitForProcesses(pids: number[], timeoutSeconds = 180, verbose = false): Promise<any[]> {
|
|
181
168
|
for (const pid of pids) {
|
|
182
169
|
if (!this.processManager.has(pid)) {
|
|
183
170
|
throw new Error(`Process with PID ${pid} not found`);
|
|
@@ -209,7 +196,7 @@ export class ProcessService {
|
|
|
209
196
|
|
|
210
197
|
try {
|
|
211
198
|
await Promise.race([Promise.all(waitPromises), timeoutPromise]);
|
|
212
|
-
return pids.map((pid) => this.getProcessResult(pid,
|
|
199
|
+
return pids.map((pid) => this.getProcessResult(pid, verbose));
|
|
213
200
|
} finally {
|
|
214
201
|
if (timeoutHandle) {
|
|
215
202
|
clearTimeout(timeoutHandle);
|
package/src/server.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { debugLog, findClaudeCli, findCodexCli, findGeminiCli } from './cli-utils.js';
|
|
2
|
+
export { debugLog, findClaudeCli, findCodexCli, findForgeCli, findGeminiCli } from './cli-utils.js';
|
|
3
3
|
export { resolveModelAlias } from './cli-builder.js';
|
|
4
4
|
export { ClaudeCodeServer, runMcpServer, spawnAsync } from './app/mcp.js';
|
|
5
5
|
|