@covibes/zeroshot 5.2.1 → 5.4.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +174 -189
  2. package/README.md +226 -195
  3. package/cli/commands/providers.js +149 -0
  4. package/cli/index.js +3145 -2366
  5. package/cli/lib/first-run.js +40 -3
  6. package/cli/message-formatters-normal.js +28 -6
  7. package/cluster-templates/base-templates/debug-workflow.json +24 -78
  8. package/cluster-templates/base-templates/full-workflow.json +99 -316
  9. package/cluster-templates/base-templates/single-worker.json +23 -15
  10. package/cluster-templates/base-templates/worker-validator.json +105 -36
  11. package/cluster-templates/conductor-bootstrap.json +9 -7
  12. package/lib/docker-config.js +14 -1
  13. package/lib/git-remote-utils.js +165 -0
  14. package/lib/id-detector.js +10 -7
  15. package/lib/provider-defaults.js +62 -0
  16. package/lib/provider-detection.js +59 -0
  17. package/lib/provider-names.js +57 -0
  18. package/lib/settings/claude-auth.js +78 -0
  19. package/lib/settings.js +298 -15
  20. package/lib/stream-json-parser.js +4 -238
  21. package/package.json +27 -6
  22. package/scripts/setup-merge-queue.sh +170 -0
  23. package/scripts/validate-templates.js +100 -0
  24. package/src/agent/agent-config.js +140 -63
  25. package/src/agent/agent-context-builder.js +336 -165
  26. package/src/agent/agent-hook-executor.js +337 -67
  27. package/src/agent/agent-lifecycle.js +386 -287
  28. package/src/agent/agent-stuck-detector.js +7 -7
  29. package/src/agent/agent-task-executor.js +944 -683
  30. package/src/agent/output-extraction.js +217 -0
  31. package/src/agent/output-reformatter.js +175 -0
  32. package/src/agent/schema-utils.js +146 -0
  33. package/src/agent-wrapper.js +112 -31
  34. package/src/agents/git-pusher-template.js +285 -0
  35. package/src/claude-task-runner.js +145 -44
  36. package/src/config-router.js +13 -13
  37. package/src/config-validator.js +1049 -563
  38. package/src/input-helpers.js +65 -0
  39. package/src/isolation-manager.js +499 -320
  40. package/src/issue-providers/README.md +305 -0
  41. package/src/issue-providers/azure-devops-provider.js +273 -0
  42. package/src/issue-providers/base-provider.js +232 -0
  43. package/src/issue-providers/github-provider.js +179 -0
  44. package/src/issue-providers/gitlab-provider.js +241 -0
  45. package/src/issue-providers/index.js +196 -0
  46. package/src/issue-providers/jira-provider.js +239 -0
  47. package/src/ledger.js +50 -11
  48. package/src/lib/safe-exec.js +88 -0
  49. package/src/orchestrator.js +1348 -757
  50. package/src/preflight.js +306 -149
  51. package/src/process-metrics.js +98 -56
  52. package/src/providers/anthropic/cli-builder.js +73 -0
  53. package/src/providers/anthropic/index.js +204 -0
  54. package/src/providers/anthropic/models.js +23 -0
  55. package/src/providers/anthropic/output-parser.js +177 -0
  56. package/src/providers/base-provider.js +251 -0
  57. package/src/providers/capabilities.js +60 -0
  58. package/src/providers/google/cli-builder.js +55 -0
  59. package/src/providers/google/index.js +116 -0
  60. package/src/providers/google/models.js +24 -0
  61. package/src/providers/google/output-parser.js +101 -0
  62. package/src/providers/index.js +91 -0
  63. package/src/providers/openai/cli-builder.js +133 -0
  64. package/src/providers/openai/index.js +136 -0
  65. package/src/providers/openai/models.js +21 -0
  66. package/src/providers/openai/output-parser.js +143 -0
  67. package/src/providers/opencode/cli-builder.js +42 -0
  68. package/src/providers/opencode/index.js +103 -0
  69. package/src/providers/opencode/models.js +55 -0
  70. package/src/providers/opencode/output-parser.js +122 -0
  71. package/src/schemas/sub-cluster.js +68 -39
  72. package/src/status-footer.js +94 -48
  73. package/src/sub-cluster-wrapper.js +92 -36
  74. package/src/task-runner.js +8 -6
  75. package/src/template-resolver.js +12 -9
  76. package/src/tui/data-poller.js +123 -99
  77. package/src/tui/formatters.js +4 -3
  78. package/src/tui/keybindings.js +259 -318
  79. package/src/tui/layout.js +20 -3
  80. package/src/tui/renderer.js +11 -21
  81. package/task-lib/attachable-watcher.js +150 -111
  82. package/task-lib/claude-recovery.js +156 -0
  83. package/task-lib/commands/episodes.js +105 -0
  84. package/task-lib/commands/list.js +3 -3
  85. package/task-lib/commands/logs.js +231 -189
  86. package/task-lib/commands/resume.js +3 -2
  87. package/task-lib/commands/run.js +12 -3
  88. package/task-lib/commands/schedules.js +111 -61
  89. package/task-lib/config.js +0 -2
  90. package/task-lib/runner.js +128 -50
  91. package/task-lib/scheduler.js +3 -3
  92. package/task-lib/store.js +464 -152
  93. package/task-lib/tui/formatters.js +94 -45
  94. package/task-lib/tui/renderer.js +13 -3
  95. package/task-lib/tui.js +73 -32
  96. package/task-lib/watcher.js +157 -100
  97. package/src/agents/git-pusher-agent.json +0 -20
  98. package/src/github.js +0 -103
@@ -0,0 +1,133 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ /**
6
+ * OpenAI structured outputs have strict schema requirements:
7
+ * 1. additionalProperties: false on ALL object schemas
8
+ * 2. 'required' must include ALL keys in 'properties'
9
+ * This function recursively enforces both constraints.
10
+ * @param {Object} schema - JSON Schema object
11
+ * @returns {Object} - Modified schema compliant with OpenAI structured output requirements
12
+ */
13
+ function enforceStrictSchema(schema) {
14
+ if (!schema || typeof schema !== 'object') return schema;
15
+
16
+ const result = { ...schema };
17
+
18
+ // Add additionalProperties: false to object types
19
+ // OpenAI also requires 'required' to include ALL property keys
20
+ if (result.type === 'object') {
21
+ result.additionalProperties = false;
22
+ if (result.properties) {
23
+ result.required = Object.keys(result.properties);
24
+ }
25
+ }
26
+
27
+ // Recurse into properties
28
+ if (result.properties) {
29
+ result.properties = {};
30
+ for (const [key, value] of Object.entries(schema.properties)) {
31
+ result.properties[key] = enforceStrictSchema(value);
32
+ }
33
+ }
34
+
35
+ // Recurse into items (arrays)
36
+ if (result.items) {
37
+ result.items = enforceStrictSchema(schema.items);
38
+ }
39
+
40
+ // Recurse into anyOf/oneOf/allOf
41
+ for (const key of ['anyOf', 'oneOf', 'allOf']) {
42
+ if (Array.isArray(result[key])) {
43
+ result[key] = result[key].map(enforceStrictSchema);
44
+ }
45
+ }
46
+
47
+ // Recurse into additionalProperties if it's a schema
48
+ if (result.additionalProperties && typeof result.additionalProperties === 'object') {
49
+ result.additionalProperties = enforceStrictSchema(result.additionalProperties);
50
+ }
51
+
52
+ return result;
53
+ }
54
+
55
+ function buildCommand(context, options = {}) {
56
+ const { modelSpec, outputFormat, jsonSchema, cwd, autoApprove, cliFeatures = {} } = options;
57
+
58
+ const args = ['exec'];
59
+ const cleanup = []; // Files to cleanup after command completes
60
+
61
+ if ((outputFormat === 'stream-json' || outputFormat === 'json') && cliFeatures.supportsJson) {
62
+ args.push('--json');
63
+ }
64
+
65
+ if (modelSpec?.model) {
66
+ args.push('-m', modelSpec.model);
67
+ }
68
+
69
+ if (modelSpec?.reasoningEffort && cliFeatures.supportsConfigOverride) {
70
+ args.push('--config', `model_reasoning_effort="${modelSpec.reasoningEffort}"`);
71
+ }
72
+
73
+ if (cwd && cliFeatures.supportsCwd) {
74
+ args.push('-C', cwd);
75
+ }
76
+
77
+ if (autoApprove && cliFeatures.supportsAutoApprove) {
78
+ args.push('--dangerously-bypass-approvals-and-sandbox');
79
+ }
80
+
81
+ // Always skip git repo check - zeroshot runs non-interactively and may run in any directory
82
+ if (cliFeatures.supportsSkipGitRepoCheck !== false) {
83
+ args.push('--skip-git-repo-check');
84
+ }
85
+
86
+ // Augment context with schema if CLI doesn't support native --output-schema
87
+ let finalContext = context;
88
+ if (jsonSchema && cliFeatures.supportsOutputSchema) {
89
+ // CRITICAL: Codex --output-schema takes a FILE PATH, not a JSON string
90
+ // Write schema to temp file and pass the path
91
+ // Codex requires additionalProperties: false on all object schemas
92
+ const parsedSchema = typeof jsonSchema === 'string' ? JSON.parse(jsonSchema) : jsonSchema;
93
+ const strictSchema = enforceStrictSchema(parsedSchema);
94
+ const schemaStr = JSON.stringify(strictSchema, null, 2);
95
+ const schemaFile = path.join(
96
+ os.tmpdir(),
97
+ `zeroshot-schema-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
98
+ );
99
+ fs.writeFileSync(schemaFile, schemaStr);
100
+ cleanup.push(schemaFile);
101
+ args.push('--output-schema', schemaFile);
102
+ } else if (jsonSchema) {
103
+ // CRITICAL: Inject schema into prompt when CLI doesn't support --output-schema
104
+ // Without this, model outputs free-form text instead of JSON
105
+ const schemaStr =
106
+ typeof jsonSchema === 'string' ? jsonSchema : JSON.stringify(jsonSchema, null, 2);
107
+ finalContext =
108
+ context +
109
+ `\n\n## OUTPUT FORMAT (CRITICAL - REQUIRED)
110
+
111
+ You MUST respond with a JSON object that exactly matches this schema. NO markdown, NO explanation, NO code blocks. ONLY the raw JSON object.
112
+
113
+ Schema:
114
+ \`\`\`json
115
+ ${schemaStr}
116
+ \`\`\`
117
+
118
+ Your response must be ONLY valid JSON. Start with { and end with }. Nothing else.`;
119
+ }
120
+
121
+ args.push(finalContext);
122
+
123
+ return {
124
+ binary: 'codex',
125
+ args,
126
+ env: {},
127
+ cleanup, // Temp files to delete after command completes
128
+ };
129
+ }
130
+
131
+ module.exports = {
132
+ buildCommand,
133
+ };
@@ -0,0 +1,136 @@
1
+ const BaseProvider = require('../base-provider');
2
+ const { commandExists, getCommandPath, getHelpOutput } = require('../../../lib/provider-detection');
3
+ const { buildCommand } = require('./cli-builder');
4
+ const { parseEvent } = require('./output-parser');
5
+ const {
6
+ MODEL_CATALOG,
7
+ LEVEL_MAPPING,
8
+ DEFAULT_LEVEL,
9
+ DEFAULT_MAX_LEVEL,
10
+ DEFAULT_MIN_LEVEL,
11
+ } = require('./models');
12
+
13
+ const warned = new Set();
14
+
15
+ class OpenAIProvider extends BaseProvider {
16
+ constructor() {
17
+ super({ name: 'codex', displayName: 'Codex', cliCommand: 'codex' });
18
+ this._cliFeatures = null;
19
+ this._unknownEventCounts = new Map();
20
+ }
21
+
22
+ // SDK not implemented - uses CLI only
23
+ // See BaseProvider for SDK extension point documentation
24
+
25
+ isAvailable() {
26
+ return commandExists(this.cliCommand);
27
+ }
28
+
29
+ getCliPath() {
30
+ return getCommandPath(this.cliCommand) || this.cliCommand;
31
+ }
32
+
33
+ getInstallInstructions() {
34
+ return 'npm install -g @openai/codex';
35
+ }
36
+
37
+ getAuthInstructions() {
38
+ return 'codex login';
39
+ }
40
+
41
+ getCliFeatures() {
42
+ if (this._cliFeatures) return this._cliFeatures;
43
+ // CRITICAL: Check 'codex exec --help' not 'codex --help'
44
+ // The --output-schema flag is on the exec subcommand, not the main command
45
+ const help = getHelpOutput(this.cliCommand, ['exec']);
46
+ const unknown = !help;
47
+
48
+ const features = {
49
+ supportsJson: unknown ? true : /--json\b/.test(help),
50
+ supportsOutputSchema: unknown ? true : /--output-schema\b/.test(help),
51
+ supportsAutoApprove: unknown
52
+ ? true
53
+ : /--dangerously-bypass-approvals-and-sandbox\b/.test(help),
54
+ supportsCwd: unknown ? true : /\s-C\b/.test(help) || /--cwd\b/.test(help),
55
+ supportsConfigOverride: unknown ? true : /--config\b/.test(help),
56
+ supportsModel: unknown ? true : /\s-m\b/.test(help) || /--model\b/.test(help),
57
+ supportsSkipGitRepoCheck: unknown ? true : /--skip-git-repo-check\b/.test(help),
58
+ unknown,
59
+ };
60
+
61
+ this._cliFeatures = features;
62
+ return features;
63
+ }
64
+
65
+ getCredentialPaths() {
66
+ return ['~/.config/codex', '~/.codex'];
67
+ }
68
+
69
+ buildCommand(context, options) {
70
+ const cliFeatures = options.cliFeatures || {};
71
+
72
+ if (options.autoApprove && cliFeatures.supportsAutoApprove === false) {
73
+ this._warnOnce(
74
+ 'codex-auto-approve',
75
+ 'Codex CLI does not support auto-approve; continuing without bypass flag.'
76
+ );
77
+ }
78
+
79
+ if (options.jsonSchema && cliFeatures.supportsOutputSchema === false) {
80
+ this._warnOnce(
81
+ 'codex-jsonschema',
82
+ 'Codex CLI does not support --output-schema; skipping schema flag.'
83
+ );
84
+ }
85
+
86
+ if (options.modelSpec?.reasoningEffort && cliFeatures.supportsConfigOverride === false) {
87
+ this._warnOnce(
88
+ 'codex-reasoning',
89
+ 'Codex CLI does not support --config overrides; skipping reasoningEffort.'
90
+ );
91
+ }
92
+
93
+ return buildCommand(context, { ...options, cliFeatures });
94
+ }
95
+
96
+ parseEvent(line) {
97
+ return parseEvent(line, {
98
+ onUnknown: (type) => this._logUnknown(type),
99
+ });
100
+ }
101
+
102
+ getModelCatalog() {
103
+ return MODEL_CATALOG;
104
+ }
105
+
106
+ getLevelMapping() {
107
+ return LEVEL_MAPPING;
108
+ }
109
+
110
+ getDefaultLevel() {
111
+ return DEFAULT_LEVEL;
112
+ }
113
+
114
+ getDefaultMaxLevel() {
115
+ return DEFAULT_MAX_LEVEL;
116
+ }
117
+
118
+ getDefaultMinLevel() {
119
+ return DEFAULT_MIN_LEVEL;
120
+ }
121
+
122
+ _warnOnce(key, message) {
123
+ if (warned.has(key)) return;
124
+ warned.add(key);
125
+ console.warn(`⚠️ ${message}`);
126
+ }
127
+
128
+ _logUnknown(type) {
129
+ const current = this._unknownEventCounts.get(type) || 0;
130
+ if (current >= 5) return;
131
+ this._unknownEventCounts.set(type, current + 1);
132
+ console.debug(`[codex] Unknown event type: ${type}`);
133
+ }
134
+ }
135
+
136
+ module.exports = OpenAIProvider;
@@ -0,0 +1,21 @@
1
+ // Codex CLI - use null to let CLI pick its default model
2
+ // Levels vary by reasoning effort only
3
+ const MODEL_CATALOG = {};
4
+
5
+ const LEVEL_MAPPING = {
6
+ level1: { rank: 1, model: null, reasoningEffort: 'low' },
7
+ level2: { rank: 2, model: null, reasoningEffort: 'medium' },
8
+ level3: { rank: 3, model: null, reasoningEffort: 'high' },
9
+ };
10
+
11
+ const DEFAULT_LEVEL = 'level2';
12
+ const DEFAULT_MAX_LEVEL = 'level3';
13
+ const DEFAULT_MIN_LEVEL = 'level1';
14
+
15
+ module.exports = {
16
+ MODEL_CATALOG,
17
+ LEVEL_MAPPING,
18
+ DEFAULT_LEVEL,
19
+ DEFAULT_MAX_LEVEL,
20
+ DEFAULT_MIN_LEVEL,
21
+ };
@@ -0,0 +1,143 @@
1
+ function safeJsonParse(value, fallback) {
2
+ try {
3
+ return JSON.parse(value);
4
+ } catch {
5
+ return fallback;
6
+ }
7
+ }
8
+
9
+ function parseAssistantMessage(item) {
10
+ const events = [];
11
+ const content = Array.isArray(item.content)
12
+ ? item.content
13
+ : [{ type: 'text', text: item.content }];
14
+ const text = content
15
+ .filter((c) => c.type === 'text')
16
+ .map((c) => c.text)
17
+ .join('');
18
+ const thinking = content
19
+ .filter((c) => c.type === 'thinking' || c.type === 'reasoning')
20
+ .map((c) => c.text)
21
+ .join('');
22
+ if (text) events.push({ type: 'text', text });
23
+ if (thinking) events.push({ type: 'thinking', text: thinking });
24
+ return events;
25
+ }
26
+
27
+ function parseFunctionCall(item) {
28
+ const toolId = item.call_id || item.id || item.tool_call_id || item.tool_id;
29
+ const args =
30
+ typeof item.arguments === 'string' ? safeJsonParse(item.arguments, {}) : item.arguments || {};
31
+ return {
32
+ type: 'tool_call',
33
+ toolName: item.name,
34
+ toolId,
35
+ input: args,
36
+ };
37
+ }
38
+
39
+ function parseFunctionCallOutput(item) {
40
+ const toolId = item.call_id || item.id || item.tool_call_id || item.tool_id;
41
+ const content = item.output ?? item.result ?? item.content ?? '';
42
+ return {
43
+ type: 'tool_result',
44
+ toolId,
45
+ content,
46
+ isError: !!item.error,
47
+ };
48
+ }
49
+
50
+ function parseItem(item) {
51
+ const events = [];
52
+
53
+ // Handle assistant messages (Claude-style: type=message, role=assistant)
54
+ if (item.type === 'message' && item.role === 'assistant') {
55
+ events.push(...parseAssistantMessage(item));
56
+ }
57
+
58
+ // Handle agent messages (Codex-style: type=agent_message, text=string)
59
+ if (item.type === 'agent_message' && item.text) {
60
+ events.push({ type: 'text', text: item.text });
61
+ }
62
+
63
+ if (item.type === 'function_call') {
64
+ events.push(parseFunctionCall(item));
65
+ }
66
+
67
+ if (item.type === 'function_call_output') {
68
+ events.push(parseFunctionCallOutput(item));
69
+ }
70
+
71
+ if (events.length === 1) return events[0];
72
+ if (events.length > 1) return events;
73
+ return null;
74
+ }
75
+
76
+ function parseEvent(line, options = {}) {
77
+ let event;
78
+ try {
79
+ event = JSON.parse(line);
80
+ } catch {
81
+ return null;
82
+ }
83
+
84
+ switch (event.type) {
85
+ case 'thread.started':
86
+ case 'turn.started':
87
+ return null;
88
+
89
+ case 'item.created':
90
+ case 'item.completed':
91
+ return parseItem(event.item);
92
+
93
+ case 'turn.completed': {
94
+ const usage = event.usage || event.response?.usage || {};
95
+ return {
96
+ type: 'result',
97
+ success: true,
98
+ inputTokens: usage.input_tokens || 0,
99
+ outputTokens: usage.output_tokens || 0,
100
+ };
101
+ }
102
+
103
+ case 'turn.failed':
104
+ return {
105
+ type: 'result',
106
+ success: false,
107
+ error: event.error?.message || event.error || 'Turn failed',
108
+ };
109
+
110
+ default:
111
+ // Only log warnings for actual unknown string types, not malformed events
112
+ // (undefined/object types are just noise from non-standard CLI output)
113
+ if (options.onUnknown && typeof event.type === 'string') {
114
+ options.onUnknown(event.type, event);
115
+ }
116
+ return null;
117
+ }
118
+ }
119
+
120
+ function parseChunk(chunk, options = {}) {
121
+ const events = [];
122
+ const lines = chunk.split('\n');
123
+
124
+ for (const line of lines) {
125
+ if (!line.trim()) continue;
126
+ const event = parseEvent(line, options);
127
+ if (!event) continue;
128
+ if (Array.isArray(event)) {
129
+ events.push(...event);
130
+ } else {
131
+ events.push(event);
132
+ }
133
+ }
134
+
135
+ return events;
136
+ }
137
+
138
+ module.exports = {
139
+ parseEvent,
140
+ parseChunk,
141
+ parseItem,
142
+ safeJsonParse,
143
+ };
@@ -0,0 +1,42 @@
1
+ function buildCommand(context, options = {}) {
2
+ const { modelSpec, outputFormat, jsonSchema, cwd, cliFeatures = {} } = options;
3
+
4
+ let finalContext = context;
5
+ if (jsonSchema) {
6
+ const schemaStr =
7
+ typeof jsonSchema === 'string' ? jsonSchema : JSON.stringify(jsonSchema, null, 2);
8
+ finalContext =
9
+ context +
10
+ `\n\n## OUTPUT FORMAT (CRITICAL - REQUIRED)\n\nYou MUST respond with a JSON object that exactly matches this schema. NO markdown, NO explanation, NO code blocks. ONLY the raw JSON object.\n\nSchema:\n\`\`\`json\n${schemaStr}\n\`\`\`\n\nYour response must be ONLY valid JSON. Start with { and end with }. Nothing else.`;
11
+ }
12
+
13
+ const args = ['run'];
14
+
15
+ if ((outputFormat === 'stream-json' || outputFormat === 'json') && cliFeatures.supportsJson) {
16
+ args.push('--format', 'json');
17
+ }
18
+
19
+ if (modelSpec?.model) {
20
+ args.push('--model', modelSpec.model);
21
+ }
22
+
23
+ if (modelSpec?.reasoningEffort && cliFeatures.supportsVariant) {
24
+ args.push('--variant', modelSpec.reasoningEffort);
25
+ }
26
+
27
+ if (cwd && cliFeatures.supportsCwd) {
28
+ args.push('--cwd', cwd);
29
+ }
30
+
31
+ args.push(finalContext);
32
+
33
+ return {
34
+ binary: 'opencode',
35
+ args,
36
+ env: {},
37
+ };
38
+ }
39
+
40
+ module.exports = {
41
+ buildCommand,
42
+ };
@@ -0,0 +1,103 @@
1
+ const BaseProvider = require('../base-provider');
2
+ const { commandExists, getCommandPath, getHelpOutput } = require('../../../lib/provider-detection');
3
+ const { buildCommand } = require('./cli-builder');
4
+ const { parseEvent } = require('./output-parser');
5
+ const {
6
+ MODEL_CATALOG,
7
+ LEVEL_MAPPING,
8
+ DEFAULT_LEVEL,
9
+ DEFAULT_MAX_LEVEL,
10
+ DEFAULT_MIN_LEVEL,
11
+ } = require('./models');
12
+
13
+ const warned = new Set();
14
+
15
+ class OpencodeProvider extends BaseProvider {
16
+ constructor() {
17
+ super({ name: 'opencode', displayName: 'Opencode', cliCommand: 'opencode' });
18
+ this._cliFeatures = null;
19
+ }
20
+
21
+ isAvailable() {
22
+ return commandExists(this.cliCommand);
23
+ }
24
+
25
+ getCliPath() {
26
+ return getCommandPath(this.cliCommand) || this.cliCommand;
27
+ }
28
+
29
+ getInstallInstructions() {
30
+ return 'See https://opencode.ai for installation instructions.';
31
+ }
32
+
33
+ getAuthInstructions() {
34
+ return 'opencode auth login';
35
+ }
36
+
37
+ getCliFeatures() {
38
+ if (this._cliFeatures) return this._cliFeatures;
39
+ const help = getHelpOutput(this.cliCommand, ['run']);
40
+ const unknown = !help;
41
+
42
+ const features = {
43
+ supportsJson: unknown ? true : /--format\b/.test(help),
44
+ supportsModel: unknown ? true : /--model\b/.test(help),
45
+ supportsVariant: unknown ? true : /--variant\b/.test(help),
46
+ supportsCwd: unknown ? false : /--cwd\b/.test(help),
47
+ supportsAutoApprove: false,
48
+ unknown,
49
+ };
50
+
51
+ this._cliFeatures = features;
52
+ return features;
53
+ }
54
+
55
+ getCredentialPaths() {
56
+ return ['~/.local/share/opencode'];
57
+ }
58
+
59
+ buildCommand(context, options) {
60
+ const cliFeatures = options.cliFeatures || {};
61
+
62
+ if (options.modelSpec?.reasoningEffort && cliFeatures.supportsVariant === false) {
63
+ this._warnOnce(
64
+ 'opencode-variant',
65
+ 'Opencode CLI does not support --variant; skipping reasoningEffort.'
66
+ );
67
+ }
68
+
69
+ return buildCommand(context, { ...options, cliFeatures });
70
+ }
71
+
72
+ parseEvent(line) {
73
+ return parseEvent(line);
74
+ }
75
+
76
+ getModelCatalog() {
77
+ return MODEL_CATALOG;
78
+ }
79
+
80
+ getLevelMapping() {
81
+ return LEVEL_MAPPING;
82
+ }
83
+
84
+ getDefaultLevel() {
85
+ return DEFAULT_LEVEL;
86
+ }
87
+
88
+ getDefaultMaxLevel() {
89
+ return DEFAULT_MAX_LEVEL;
90
+ }
91
+
92
+ getDefaultMinLevel() {
93
+ return DEFAULT_MIN_LEVEL;
94
+ }
95
+
96
+ _warnOnce(key, message) {
97
+ if (warned.has(key)) return;
98
+ warned.add(key);
99
+ console.warn(`⚠️ ${message}`);
100
+ }
101
+ }
102
+
103
+ module.exports = OpencodeProvider;
@@ -0,0 +1,55 @@
1
+ const MODEL_CATALOG = {
2
+ 'opencode/big-pickle': { rank: 1 },
3
+ 'opencode/glm-4.7-free': { rank: 1 },
4
+ 'opencode/gpt-5-nano': { rank: 1 },
5
+ 'opencode/grok-code': { rank: 1 },
6
+ 'opencode/minimax-m2.1-free': { rank: 1 },
7
+ 'google/gemini-1.5-flash': { rank: 1 },
8
+ 'google/gemini-1.5-flash-8b': { rank: 1 },
9
+ 'google/gemini-1.5-pro': { rank: 1 },
10
+ 'google/gemini-2.0-flash': { rank: 1 },
11
+ 'google/gemini-2.0-flash-lite': { rank: 1 },
12
+ 'google/gemini-2.5-flash': { rank: 1 },
13
+ 'google/gemini-2.5-flash-image': { rank: 1 },
14
+ 'google/gemini-2.5-flash-image-preview': { rank: 1 },
15
+ 'google/gemini-2.5-flash-lite': { rank: 1 },
16
+ 'google/gemini-2.5-flash-lite-preview-06-17': { rank: 1 },
17
+ 'google/gemini-2.5-flash-lite-preview-09-2025': { rank: 1 },
18
+ 'google/gemini-2.5-flash-preview-04-17': { rank: 1 },
19
+ 'google/gemini-2.5-flash-preview-05-20': { rank: 1 },
20
+ 'google/gemini-2.5-flash-preview-09-2025': { rank: 1 },
21
+ 'google/gemini-2.5-flash-preview-tts': { rank: 1 },
22
+ 'google/gemini-2.5-pro': { rank: 1 },
23
+ 'google/gemini-2.5-pro-preview-05-06': { rank: 1 },
24
+ 'google/gemini-2.5-pro-preview-06-05': { rank: 1 },
25
+ 'google/gemini-2.5-pro-preview-tts': { rank: 1 },
26
+ 'google/gemini-3-flash-preview': { rank: 1 },
27
+ 'google/gemini-3-pro-preview': { rank: 1 },
28
+ 'google/gemini-embedding-001': { rank: 1 },
29
+ 'google/gemini-flash-latest': { rank: 1 },
30
+ 'google/gemini-flash-lite-latest': { rank: 1 },
31
+ 'google/gemini-live-2.5-flash': { rank: 1 },
32
+ 'google/gemini-live-2.5-flash-preview-native-audio': { rank: 1 },
33
+ 'openai/gpt-5.1-codex-max': { rank: 1 },
34
+ 'openai/gpt-5.1-codex-mini': { rank: 1 },
35
+ 'openai/gpt-5.2': { rank: 1 },
36
+ 'openai/gpt-5.2-codex': { rank: 1 },
37
+ };
38
+
39
+ const LEVEL_MAPPING = {
40
+ level1: { rank: 1, model: null, reasoningEffort: 'low' },
41
+ level2: { rank: 2, model: null, reasoningEffort: 'medium' },
42
+ level3: { rank: 3, model: null, reasoningEffort: 'high' },
43
+ };
44
+
45
+ const DEFAULT_LEVEL = 'level2';
46
+ const DEFAULT_MAX_LEVEL = 'level3';
47
+ const DEFAULT_MIN_LEVEL = 'level1';
48
+
49
+ module.exports = {
50
+ MODEL_CATALOG,
51
+ LEVEL_MAPPING,
52
+ DEFAULT_LEVEL,
53
+ DEFAULT_MAX_LEVEL,
54
+ DEFAULT_MIN_LEVEL,
55
+ };