ai-cli-mcp 2.19.0 → 2.20.1
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/CHANGELOG.md +26 -0
- package/README.ja.md +34 -8
- package/README.md +41 -8
- package/dist/app/cli.js +1 -0
- package/dist/app/mcp.js +64 -12
- package/dist/cli-builder.js +13 -6
- package/dist/cli-process-service.js +76 -91
- package/dist/cli-utils.js +6 -0
- package/dist/cli.js +1 -1
- package/dist/model-catalog.js +3 -2
- package/dist/parsers.js +8 -2
- package/package.json +27 -3
- package/server.json +3 -3
- package/.gemini/settings.json +0 -11
- package/.github/dependabot.yml +0 -28
- package/.github/pull_request_template.md +0 -28
- package/.github/workflows/ci.yml +0 -34
- package/.github/workflows/dependency-review.yml +0 -22
- package/.github/workflows/publish.yml +0 -89
- package/.github/workflows/test.yml +0 -20
- package/.github/workflows/watch-session-prs.yml +0 -276
- package/.husky/pre-commit +0 -1
- package/.mcp.json +0 -11
- package/.releaserc.json +0 -18
- package/.vscode/settings.json +0 -3
- package/CONTRIBUTING.md +0 -81
- package/dist/__tests__/app-cli.test.js +0 -392
- package/dist/__tests__/cli-bin-smoke.test.js +0 -101
- package/dist/__tests__/cli-builder.test.js +0 -442
- package/dist/__tests__/cli-process-service.test.js +0 -655
- package/dist/__tests__/cli-utils.test.js +0 -171
- package/dist/__tests__/e2e.test.js +0 -256
- package/dist/__tests__/edge-cases.test.js +0 -130
- package/dist/__tests__/error-cases.test.js +0 -292
- package/dist/__tests__/mcp-contract.test.js +0 -636
- package/dist/__tests__/mocks.js +0 -32
- package/dist/__tests__/model-alias.test.js +0 -36
- package/dist/__tests__/parsers.test.js +0 -646
- package/dist/__tests__/peek.test.js +0 -36
- package/dist/__tests__/process-management.test.js +0 -949
- package/dist/__tests__/server.test.js +0 -809
- package/dist/__tests__/setup.js +0 -11
- package/dist/__tests__/utils/claude-mock.js +0 -80
- package/dist/__tests__/utils/mcp-client.js +0 -121
- package/dist/__tests__/utils/opencode-mock.js +0 -91
- package/dist/__tests__/utils/persistent-mock.js +0 -28
- package/dist/__tests__/utils/test-helpers.js +0 -11
- package/dist/__tests__/validation.test.js +0 -308
- package/dist/__tests__/version-print.test.js +0 -65
- package/dist/__tests__/wait.test.js +0 -260
- package/docs/RELEASE_CHECKLIST.md +0 -65
- package/docs/cli-architecture.md +0 -275
- package/docs/concept.md +0 -154
- package/docs/development.md +0 -156
- package/docs/e2e-testing.md +0 -148
- package/docs/prd.md +0 -146
- package/docs/session-stacking.md +0 -67
- package/src/__tests__/app-cli.test.ts +0 -495
- package/src/__tests__/cli-bin-smoke.test.ts +0 -136
- package/src/__tests__/cli-builder.test.ts +0 -549
- package/src/__tests__/cli-process-service.test.ts +0 -759
- package/src/__tests__/cli-utils.test.ts +0 -200
- package/src/__tests__/e2e.test.ts +0 -311
- package/src/__tests__/edge-cases.test.ts +0 -176
- package/src/__tests__/error-cases.test.ts +0 -370
- package/src/__tests__/mcp-contract.test.ts +0 -755
- package/src/__tests__/mocks.ts +0 -35
- package/src/__tests__/model-alias.test.ts +0 -44
- package/src/__tests__/parsers.test.ts +0 -730
- package/src/__tests__/peek.test.ts +0 -44
- package/src/__tests__/process-management.test.ts +0 -1129
- package/src/__tests__/server.test.ts +0 -1020
- package/src/__tests__/setup.ts +0 -13
- package/src/__tests__/utils/claude-mock.ts +0 -87
- package/src/__tests__/utils/mcp-client.ts +0 -159
- package/src/__tests__/utils/opencode-mock.ts +0 -108
- package/src/__tests__/utils/persistent-mock.ts +0 -33
- package/src/__tests__/utils/test-helpers.ts +0 -13
- package/src/__tests__/validation.test.ts +0 -369
- package/src/__tests__/version-print.test.ts +0 -81
- package/src/__tests__/wait.test.ts +0 -302
- package/src/app/cli.ts +0 -424
- package/src/app/mcp.ts +0 -466
- package/src/bin/ai-cli-mcp.ts +0 -7
- package/src/bin/ai-cli.ts +0 -11
- package/src/cli-builder.ts +0 -274
- package/src/cli-parse.ts +0 -105
- package/src/cli-process-service.ts +0 -709
- package/src/cli-utils.ts +0 -258
- package/src/cli.ts +0 -124
- package/src/model-catalog.ts +0 -87
- package/src/parsers.ts +0 -965
- package/src/peek.ts +0 -95
- package/src/process-result.ts +0 -88
- package/src/process-service.ts +0 -368
- package/src/server.ts +0 -10
- package/tsconfig.json +0 -16
- package/vitest.config.e2e.ts +0 -27
- package/vitest.config.ts +0 -22
- package/vitest.config.unit.ts +0 -28
package/src/cli-builder.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { resolve as pathResolve, isAbsolute } from 'node:path';
|
|
3
|
-
import type { CliPaths } from './cli-utils.js';
|
|
4
|
-
import { MODEL_ALIASES } from './model-catalog.js';
|
|
5
|
-
|
|
6
|
-
export const ALLOWED_REASONING_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh']);
|
|
7
|
-
const CLAUDE_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
|
|
8
|
-
const OPENCODE_MODEL_ERROR = 'Invalid OpenCode model. Expected exact syntax oc-<provider/model>.';
|
|
9
|
-
|
|
10
|
-
type Agent = 'codex' | 'claude' | 'gemini' | 'forge' | 'opencode';
|
|
11
|
-
|
|
12
|
-
interface ModelSelection {
|
|
13
|
-
agent: Agent;
|
|
14
|
-
resolvedModel: string;
|
|
15
|
-
openCodeModel: string | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getStandardAgentForModel(model: string): Exclude<Agent, 'opencode'> {
|
|
19
|
-
if (model === 'forge') {
|
|
20
|
-
return 'forge';
|
|
21
|
-
}
|
|
22
|
-
if (model.startsWith('gpt-')) {
|
|
23
|
-
return 'codex';
|
|
24
|
-
}
|
|
25
|
-
if (model.startsWith('gemini')) {
|
|
26
|
-
return 'gemini';
|
|
27
|
-
}
|
|
28
|
-
return 'claude';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isPotentialOpenCodeExplicitModel(rawModel: string): boolean {
|
|
32
|
-
return rawModel.startsWith('oc-') || rawModel.trim().startsWith('oc-');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function extractOpenCodeModel(rawModel: string): string {
|
|
36
|
-
if (rawModel !== rawModel.trim()) {
|
|
37
|
-
throw new Error(OPENCODE_MODEL_ERROR);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!rawModel.startsWith('oc-')) {
|
|
41
|
-
throw new Error(OPENCODE_MODEL_ERROR);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const remainder = rawModel.slice(3);
|
|
45
|
-
const slashIndex = remainder.indexOf('/');
|
|
46
|
-
if (slashIndex === -1) {
|
|
47
|
-
throw new Error(OPENCODE_MODEL_ERROR);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const provider = remainder.slice(0, slashIndex);
|
|
51
|
-
const model = remainder.slice(slashIndex + 1);
|
|
52
|
-
if (!provider || !model) {
|
|
53
|
-
throw new Error(OPENCODE_MODEL_ERROR);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return remainder;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function resolveModelSelection(rawModel: string): ModelSelection {
|
|
60
|
-
if (rawModel === 'opencode') {
|
|
61
|
-
return {
|
|
62
|
-
agent: 'opencode',
|
|
63
|
-
resolvedModel: rawModel,
|
|
64
|
-
openCodeModel: null,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (isPotentialOpenCodeExplicitModel(rawModel)) {
|
|
69
|
-
return {
|
|
70
|
-
agent: 'opencode',
|
|
71
|
-
resolvedModel: rawModel,
|
|
72
|
-
openCodeModel: extractOpenCodeModel(rawModel),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const resolvedModel = resolveModelAlias(rawModel);
|
|
77
|
-
return {
|
|
78
|
-
agent: getStandardAgentForModel(resolvedModel),
|
|
79
|
-
resolvedModel,
|
|
80
|
-
openCodeModel: null,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function resolveModelAlias(model: string): string {
|
|
85
|
-
return MODEL_ALIASES[model] || model;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function getReasoningEffort(model: string, rawValue: unknown): string {
|
|
89
|
-
if (typeof rawValue !== 'string') {
|
|
90
|
-
return '';
|
|
91
|
-
}
|
|
92
|
-
const trimmed = rawValue.trim();
|
|
93
|
-
if (!trimmed) {
|
|
94
|
-
return '';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (model === 'opencode' || model.startsWith('oc-')) {
|
|
98
|
-
throw new Error('reasoning_effort is not supported for opencode.');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const normalized = trimmed.toLowerCase();
|
|
102
|
-
if (!ALLOWED_REASONING_EFFORTS.has(normalized)) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
`Invalid reasoning_effort: ${rawValue}. Allowed values: low, medium, high, xhigh.`
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
const agent = getStandardAgentForModel(model);
|
|
108
|
-
if (agent === 'forge') {
|
|
109
|
-
throw new Error('reasoning_effort is not supported for forge.');
|
|
110
|
-
}
|
|
111
|
-
if (agent === 'gemini') {
|
|
112
|
-
throw new Error(
|
|
113
|
-
'reasoning_effort is only supported for Claude and Codex models.'
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
if (agent === 'claude' && !CLAUDE_REASONING_EFFORTS.has(normalized)) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
'Claude reasoning_effort supports only low, medium, high.'
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
return normalized;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export interface CliCommand {
|
|
125
|
-
cliPath: string;
|
|
126
|
-
args: string[];
|
|
127
|
-
cwd: string;
|
|
128
|
-
agent: Agent;
|
|
129
|
-
prompt: string;
|
|
130
|
-
resolvedModel: string;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface BuildCliCommandOptions {
|
|
134
|
-
prompt?: string;
|
|
135
|
-
prompt_file?: string;
|
|
136
|
-
workFolder: string;
|
|
137
|
-
model?: string;
|
|
138
|
-
session_id?: string;
|
|
139
|
-
reasoning_effort?: string;
|
|
140
|
-
cliPaths: CliPaths;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function buildCliCommand(options: BuildCliCommandOptions): CliCommand {
|
|
144
|
-
if (!options.workFolder || typeof options.workFolder !== 'string') {
|
|
145
|
-
throw new Error('Missing or invalid required parameter: workFolder');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const hasPrompt = !!options.prompt && typeof options.prompt === 'string' && options.prompt.trim() !== '';
|
|
149
|
-
const hasPromptFile = !!options.prompt_file && typeof options.prompt_file === 'string' && options.prompt_file.trim() !== '';
|
|
150
|
-
|
|
151
|
-
if (!hasPrompt && !hasPromptFile) {
|
|
152
|
-
throw new Error('Either prompt or prompt_file must be provided');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (hasPrompt && hasPromptFile) {
|
|
156
|
-
throw new Error('Cannot specify both prompt and prompt_file. Please use only one.');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
let prompt: string;
|
|
160
|
-
if (hasPrompt) {
|
|
161
|
-
prompt = options.prompt!;
|
|
162
|
-
} else {
|
|
163
|
-
const promptFilePath = isAbsolute(options.prompt_file!)
|
|
164
|
-
? options.prompt_file!
|
|
165
|
-
: pathResolve(options.workFolder, options.prompt_file!);
|
|
166
|
-
|
|
167
|
-
if (!existsSync(promptFilePath)) {
|
|
168
|
-
throw new Error(`Prompt file does not exist: ${promptFilePath}`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
prompt = readFileSync(promptFilePath, 'utf-8');
|
|
173
|
-
} catch (error: any) {
|
|
174
|
-
throw new Error(`Failed to read prompt file: ${error.message}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const cwd = pathResolve(options.workFolder);
|
|
179
|
-
if (!existsSync(cwd)) {
|
|
180
|
-
throw new Error(`Working folder does not exist: ${options.workFolder}`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const rawModel = options.model || '';
|
|
184
|
-
const { agent, resolvedModel, openCodeModel } = resolveModelSelection(rawModel);
|
|
185
|
-
|
|
186
|
-
let reasoningEffortArg: string | undefined = options.reasoning_effort;
|
|
187
|
-
if (!reasoningEffortArg) {
|
|
188
|
-
if (rawModel === 'codex-ultra') {
|
|
189
|
-
reasoningEffortArg = 'xhigh';
|
|
190
|
-
} else if (rawModel === 'claude-ultra') {
|
|
191
|
-
reasoningEffortArg = 'high';
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const reasoningTargetModel = rawModel === 'opencode' || rawModel.startsWith('oc-')
|
|
196
|
-
? rawModel
|
|
197
|
-
: (resolvedModel || rawModel);
|
|
198
|
-
const reasoningEffort = getReasoningEffort(reasoningTargetModel, reasoningEffortArg);
|
|
199
|
-
|
|
200
|
-
let cliPath: string;
|
|
201
|
-
let args: string[];
|
|
202
|
-
|
|
203
|
-
if (agent === 'codex') {
|
|
204
|
-
cliPath = options.cliPaths.codex;
|
|
205
|
-
|
|
206
|
-
if (options.session_id && typeof options.session_id === 'string') {
|
|
207
|
-
args = ['exec', 'resume', options.session_id];
|
|
208
|
-
} else {
|
|
209
|
-
args = ['exec'];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (reasoningEffort) {
|
|
213
|
-
args.push('-c', `model_reasoning_effort=${reasoningEffort}`);
|
|
214
|
-
}
|
|
215
|
-
if (resolvedModel) {
|
|
216
|
-
args.push('--model', resolvedModel);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
args.push('--skip-git-repo-check', '--dangerously-bypass-approvals-and-sandbox', '--json', prompt);
|
|
220
|
-
} else if (agent === 'gemini') {
|
|
221
|
-
cliPath = options.cliPaths.gemini;
|
|
222
|
-
args = ['-y', '--output-format', 'stream-json'];
|
|
223
|
-
|
|
224
|
-
if (options.session_id && typeof options.session_id === 'string') {
|
|
225
|
-
args.push('-r', options.session_id);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (resolvedModel) {
|
|
229
|
-
args.push('--model', resolvedModel);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
args.push(prompt);
|
|
233
|
-
} else if (agent === 'forge') {
|
|
234
|
-
cliPath = options.cliPaths.forge;
|
|
235
|
-
args = ['-C', cwd];
|
|
236
|
-
|
|
237
|
-
if (options.session_id && typeof options.session_id === 'string') {
|
|
238
|
-
args.push('--conversation-id', options.session_id);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
args.push('-p', prompt);
|
|
242
|
-
} else if (agent === 'opencode') {
|
|
243
|
-
cliPath = options.cliPaths.opencode;
|
|
244
|
-
args = ['run', '--format', 'json', '--dir', cwd];
|
|
245
|
-
|
|
246
|
-
if (options.session_id && typeof options.session_id === 'string') {
|
|
247
|
-
args.push('--session', options.session_id);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (openCodeModel) {
|
|
251
|
-
args.push('--model', openCodeModel);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
args.push(prompt);
|
|
255
|
-
} else {
|
|
256
|
-
cliPath = options.cliPaths.claude;
|
|
257
|
-
args = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
|
|
258
|
-
|
|
259
|
-
if (options.session_id && typeof options.session_id === 'string') {
|
|
260
|
-
args.push('-r', options.session_id, '--fork-session');
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (reasoningEffort) {
|
|
264
|
-
args.push('--effort', reasoningEffort);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
args.push('-p', prompt);
|
|
268
|
-
if (resolvedModel) {
|
|
269
|
-
args.push('--model', resolvedModel);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return { cliPath, args, cwd, agent, prompt, resolvedModel };
|
|
274
|
-
}
|
package/src/cli-parse.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput } from './parsers.js';
|
|
3
|
-
|
|
4
|
-
const AGENTS = ['claude', 'codex', 'gemini', 'forge', 'opencode'] as const;
|
|
5
|
-
type Agent = typeof AGENTS[number];
|
|
6
|
-
|
|
7
|
-
const USAGE = `Usage: npm run -s cli.run.parse -- --agent <claude|codex|gemini|forge|opencode>
|
|
8
|
-
|
|
9
|
-
Reads raw CLI output from stdin and outputs parsed JSON to stdout.
|
|
10
|
-
|
|
11
|
-
Options:
|
|
12
|
-
--agent Agent type: claude, codex, gemini, forge, or opencode (required)
|
|
13
|
-
--help Show this help message
|
|
14
|
-
|
|
15
|
-
Examples:
|
|
16
|
-
npm run -s cli.run -- --model sonnet --workFolder /tmp --prompt "hi" > raw.txt
|
|
17
|
-
npm run -s cli.run.parse -- --agent claude < raw.txt
|
|
18
|
-
|
|
19
|
-
npm run -s cli.run -- --model opencode --workFolder /tmp --prompt "hi" > raw.txt
|
|
20
|
-
npm run -s cli.run.parse -- --agent opencode < raw.txt
|
|
21
|
-
|
|
22
|
-
# Or pipe directly
|
|
23
|
-
npm run -s cli.run -- --model sonnet --workFolder /tmp --prompt "hi" | npm run -s cli.run.parse -- --agent claude
|
|
24
|
-
`;
|
|
25
|
-
|
|
26
|
-
function parseArgs(argv: string[]): Record<string, string> {
|
|
27
|
-
const result: Record<string, string> = {};
|
|
28
|
-
for (let i = 0; i < argv.length; i++) {
|
|
29
|
-
const arg = argv[i];
|
|
30
|
-
if (!arg.startsWith('--')) continue;
|
|
31
|
-
|
|
32
|
-
const eqIdx = arg.indexOf('=');
|
|
33
|
-
if (eqIdx !== -1) {
|
|
34
|
-
result[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
35
|
-
} else {
|
|
36
|
-
const key = arg.slice(2);
|
|
37
|
-
const next = argv[i + 1];
|
|
38
|
-
if (next !== undefined && !next.startsWith('--')) {
|
|
39
|
-
result[key] = next;
|
|
40
|
-
i++;
|
|
41
|
-
} else {
|
|
42
|
-
result[key] = '';
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function readStdin(): Promise<string> {
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
const chunks: Buffer[] = [];
|
|
52
|
-
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
|
53
|
-
process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
54
|
-
process.stdin.on('error', reject);
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function main(): Promise<void> {
|
|
59
|
-
const args = parseArgs(process.argv.slice(2));
|
|
60
|
-
|
|
61
|
-
if ('help' in args) {
|
|
62
|
-
process.stdout.write(USAGE);
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const agent = args.agent as Agent;
|
|
67
|
-
if (!agent || !AGENTS.includes(agent)) {
|
|
68
|
-
process.stderr.write(`Error: --agent is required (claude, codex, gemini, forge, or opencode)\n\n`);
|
|
69
|
-
process.stderr.write(USAGE);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const input = await readStdin();
|
|
74
|
-
|
|
75
|
-
if (!input.trim()) {
|
|
76
|
-
process.stderr.write('Error: no input received from stdin\n');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let parsed: any = null;
|
|
81
|
-
switch (agent) {
|
|
82
|
-
case 'claude':
|
|
83
|
-
parsed = parseClaudeOutput(input);
|
|
84
|
-
break;
|
|
85
|
-
case 'codex':
|
|
86
|
-
parsed = parseCodexOutput(input);
|
|
87
|
-
break;
|
|
88
|
-
case 'gemini':
|
|
89
|
-
parsed = parseGeminiOutput(input);
|
|
90
|
-
break;
|
|
91
|
-
case 'forge':
|
|
92
|
-
parsed = parseForgeOutput(input);
|
|
93
|
-
break;
|
|
94
|
-
case 'opencode':
|
|
95
|
-
parsed = parseOpenCodeOutput(input);
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
process.stdout.write(JSON.stringify(parsed, null, 2) + '\n');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
main().catch((err) => {
|
|
103
|
-
process.stderr.write(`Fatal error: ${err.message}\n`);
|
|
104
|
-
process.exit(1);
|
|
105
|
-
});
|