@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
@@ -1,14 +1,17 @@
1
1
  /**
2
2
  * ClaudeTaskRunner - Production implementation of TaskRunner
3
3
  *
4
- * Executes Claude tasks by spawning the `zeroshot task run` CLI command,
4
+ * Executes provider tasks by spawning the `zeroshot task run` CLI command,
5
5
  * following logs, and assembling results.
6
6
  */
7
7
 
8
- const { spawn, exec, execSync } = require('child_process');
8
+ const { spawn } = require('child_process');
9
+ const { exec, execSync } = require('./lib/safe-exec'); // Enforces timeouts
9
10
  const fs = require('fs');
10
11
  const TaskRunner = require('./task-runner');
11
- const { getClaudeCommand } = require('../lib/settings');
12
+ const { loadSettings } = require('../lib/settings');
13
+ const { normalizeProviderName } = require('../lib/provider-names');
14
+ const { getProvider } = require('./providers');
12
15
 
13
16
  class ClaudeTaskRunner extends TaskRunner {
14
17
  /**
@@ -36,7 +39,7 @@ class ClaudeTaskRunner extends TaskRunner {
36
39
  }
37
40
 
38
41
  /**
39
- * Execute a Claude task via zeroshot CLI
42
+ * Execute a task via zeroshot CLI
40
43
  *
41
44
  * @param {string} context - Full prompt/context
42
45
  * @param {{agentId?: string, model?: string, outputFormat?: string, jsonSchema?: any, strictSchema?: boolean, cwd?: string, isolation?: any}} options - Execution options
@@ -45,7 +48,11 @@ class ClaudeTaskRunner extends TaskRunner {
45
48
  async run(context, options = {}) {
46
49
  const {
47
50
  agentId = 'unknown',
48
- model = 'sonnet',
51
+ provider,
52
+ model = null,
53
+ modelLevel = null,
54
+ modelSpec: explicitModelSpec = null,
55
+ reasoningEffort = null,
49
56
  outputFormat = 'stream-json',
50
57
  jsonSchema = null,
51
58
  strictSchema = false, // false = live streaming (default), true = CLI schema enforcement (no streaming)
@@ -53,23 +60,110 @@ class ClaudeTaskRunner extends TaskRunner {
53
60
  isolation = null,
54
61
  } = options;
55
62
 
63
+ const settings = loadSettings();
64
+ const providerName = normalizeProviderName(provider || settings.defaultProvider || 'claude');
65
+ const { providerModule, providerSettings, levelOverrides } = this._getProviderContext(
66
+ providerName,
67
+ settings
68
+ );
69
+ const resolvedModelSpec = this._resolveModelSpec({
70
+ explicitModelSpec,
71
+ model,
72
+ reasoningEffort,
73
+ modelLevel,
74
+ providerModule,
75
+ providerSettings,
76
+ levelOverrides,
77
+ });
78
+
56
79
  // Isolation mode delegates to separate method
57
80
  if (isolation?.enabled) {
58
- return this._runIsolated(context, options);
81
+ return this._runIsolated(context, {
82
+ ...options,
83
+ provider: providerName,
84
+ modelSpec: resolvedModelSpec,
85
+ });
59
86
  }
60
87
 
61
88
  const ctPath = 'zeroshot';
62
89
 
63
- // Build args.
90
+ const runOutputFormat = this._resolveOutputFormat({
91
+ outputFormat,
92
+ jsonSchema,
93
+ strictSchema,
94
+ });
95
+ const args = this._buildRunArgs({
96
+ context,
97
+ providerName,
98
+ runOutputFormat,
99
+ resolvedModelSpec,
100
+ jsonSchema,
101
+ });
102
+
103
+ // Spawn and get task ID
104
+ const spawnEnv = this._buildSpawnEnv(providerName, resolvedModelSpec);
105
+
106
+ const taskId = await this._spawnAndGetTaskId(ctPath, args, cwd, spawnEnv, agentId);
107
+
108
+ this._log(`📋 [${agentId}]: Following zeroshot logs for ${taskId}`);
109
+
110
+ // Wait for task registration
111
+ await this._waitForTaskReady(ctPath, taskId);
112
+
113
+ // Follow logs until completion
114
+ return this._followLogs(ctPath, taskId, agentId);
115
+ }
116
+
117
+ _getProviderContext(providerName, settings) {
118
+ const providerModule = getProvider(providerName);
119
+ const providerSettings = settings.providerSettings?.[providerName] || {};
120
+ const levelOverrides = providerSettings.levelOverrides || {};
121
+ return { providerModule, providerSettings, levelOverrides };
122
+ }
123
+
124
+ _resolveModelSpec({
125
+ explicitModelSpec,
126
+ model,
127
+ reasoningEffort,
128
+ modelLevel,
129
+ providerModule,
130
+ providerSettings,
131
+ levelOverrides,
132
+ }) {
133
+ if (explicitModelSpec) {
134
+ return explicitModelSpec;
135
+ }
136
+
137
+ if (model) {
138
+ return { model, reasoningEffort };
139
+ }
140
+
141
+ const level = modelLevel || providerSettings.defaultLevel || providerModule.getDefaultLevel();
142
+ let resolvedModelSpec = providerModule.resolveModelSpec(level, levelOverrides);
143
+ if (reasoningEffort) {
144
+ resolvedModelSpec = { ...resolvedModelSpec, reasoningEffort };
145
+ }
146
+
147
+ return resolvedModelSpec;
148
+ }
149
+
150
+ _resolveOutputFormat({ outputFormat, jsonSchema, strictSchema }) {
64
151
  // json output does not stream; if a jsonSchema is configured we run stream-json
65
152
  // for live logs and validate/parse JSON after completion.
66
153
  // Set strictSchema=true to disable live streaming and use CLI's native schema enforcement.
67
- const desiredOutputFormat = outputFormat;
68
- const runOutputFormat =
69
- jsonSchema && desiredOutputFormat === 'json' && !strictSchema
70
- ? 'stream-json'
71
- : desiredOutputFormat;
72
- const args = ['task', 'run', '--output-format', runOutputFormat];
154
+ return jsonSchema && outputFormat === 'json' && !strictSchema ? 'stream-json' : outputFormat;
155
+ }
156
+
157
+ _buildRunArgs({ context, providerName, runOutputFormat, resolvedModelSpec, jsonSchema }) {
158
+ const args = ['task', 'run', '--output-format', runOutputFormat, '--provider', providerName];
159
+
160
+ if (resolvedModelSpec?.model) {
161
+ args.push('--model', resolvedModelSpec.model);
162
+ }
163
+
164
+ if (resolvedModelSpec?.reasoningEffort) {
165
+ args.push('--reasoning-effort', resolvedModelSpec.reasoningEffort);
166
+ }
73
167
 
74
168
  // Pass schema to CLI only when using json output (strictSchema=true or no conflict)
75
169
  if (jsonSchema && runOutputFormat === 'json') {
@@ -78,35 +172,34 @@ class ClaudeTaskRunner extends TaskRunner {
78
172
 
79
173
  args.push(context);
80
174
 
81
- // Spawn and get task ID
82
- const taskId = await this._spawnAndGetTaskId(ctPath, args, cwd, model, agentId);
83
-
84
- this._log(`📋 [${agentId}]: Following zeroshot logs for ${taskId}`);
175
+ return args;
176
+ }
85
177
 
86
- // Wait for task registration
87
- await this._waitForTaskReady(ctPath, taskId);
178
+ _buildSpawnEnv(providerName, resolvedModelSpec) {
179
+ const spawnEnv = {
180
+ ...process.env,
181
+ };
182
+ if (providerName === 'claude' && resolvedModelSpec?.model) {
183
+ spawnEnv.ANTHROPIC_MODEL = resolvedModelSpec.model;
184
+ }
88
185
 
89
- // Follow logs until completion
90
- return this._followLogs(ctPath, taskId, agentId);
186
+ return spawnEnv;
91
187
  }
92
188
 
93
189
  /**
94
190
  * @param {string} ctPath
95
191
  * @param {string[]} args
96
192
  * @param {string} cwd
97
- * @param {string} model
193
+ * @param {Object} spawnEnv
98
194
  * @param {string} _agentId
99
195
  * @returns {Promise<string>}
100
196
  */
101
- _spawnAndGetTaskId(ctPath, args, cwd, model, _agentId) {
197
+ _spawnAndGetTaskId(ctPath, args, cwd, spawnEnv, _agentId) {
102
198
  return new Promise((resolve, reject) => {
103
199
  const proc = spawn(ctPath, args, {
104
200
  cwd,
105
201
  stdio: ['ignore', 'pipe', 'pipe'],
106
- env: {
107
- ...process.env,
108
- ANTHROPIC_MODEL: model,
109
- },
202
+ env: spawnEnv,
110
203
  });
111
204
 
112
205
  let stdout = '';
@@ -367,7 +460,8 @@ class ClaudeTaskRunner extends TaskRunner {
367
460
  _runIsolated(context, options) {
368
461
  const {
369
462
  agentId = 'unknown',
370
- model = 'sonnet',
463
+ provider = 'claude',
464
+ modelSpec = null,
371
465
  outputFormat = 'stream-json',
372
466
  jsonSchema = null,
373
467
  strictSchema = false,
@@ -377,47 +471,54 @@ class ClaudeTaskRunner extends TaskRunner {
377
471
 
378
472
  this._log(`📦 [${agentId}]: Running task in isolated container...`);
379
473
 
380
- // Determine output format: stream-json for live logs unless strictSchema=true
381
474
  const desiredOutputFormat = outputFormat;
382
475
  const runOutputFormat =
383
476
  jsonSchema && desiredOutputFormat === 'json' && !strictSchema
384
477
  ? 'stream-json'
385
478
  : desiredOutputFormat;
386
479
 
387
- // Get configured Claude command (supports custom commands like 'ccr code')
388
- const { command: claudeCmd, args: claudeExtraArgs } = getClaudeCommand();
389
-
390
480
  const command = [
391
- claudeCmd,
392
- ...claudeExtraArgs,
393
- '--print',
394
- '--dangerously-skip-permissions',
481
+ 'zeroshot',
482
+ 'task',
483
+ 'run',
395
484
  '--output-format',
396
485
  runOutputFormat,
486
+ '--provider',
487
+ provider,
397
488
  ];
398
489
 
399
- if (runOutputFormat === 'stream-json') {
400
- command.push('--verbose');
401
- command.push('--include-partial-messages');
490
+ if (modelSpec?.model) {
491
+ command.push('--model', modelSpec.model);
492
+ }
493
+
494
+ if (modelSpec?.reasoningEffort) {
495
+ command.push('--reasoning-effort', modelSpec.reasoningEffort);
402
496
  }
403
497
 
404
- // Pass schema to CLI only when using json output (strictSchema=true or no conflict)
405
498
  if (jsonSchema && runOutputFormat === 'json') {
406
499
  command.push('--json-schema', JSON.stringify(jsonSchema));
407
500
  }
408
501
 
409
- if (model) {
410
- command.push('--model', model);
502
+ let finalContext = context;
503
+ if (jsonSchema && desiredOutputFormat === 'json' && runOutputFormat === 'stream-json') {
504
+ finalContext += `\n\n## Output Format (REQUIRED)\n\nReturn a JSON object that matches this schema exactly.\n\nSchema:\n\`\`\`json\n${JSON.stringify(
505
+ jsonSchema,
506
+ null,
507
+ 2
508
+ )}\n\`\`\`\n`;
411
509
  }
412
510
 
413
- command.push(context);
511
+ command.push(finalContext);
414
512
 
415
513
  return new Promise((resolve, reject) => {
416
514
  let output = '';
417
515
  let resolved = false;
418
516
 
419
517
  const proc = manager.spawnInContainer(clusterId, command, {
420
- env: { ANTHROPIC_MODEL: model },
518
+ env:
519
+ provider === 'claude' && modelSpec?.model
520
+ ? { ANTHROPIC_MODEL: modelSpec.model, ZEROSHOT_BLOCK_ASK_USER: '1' }
521
+ : {},
421
522
  });
422
523
 
423
524
  proc.stdout.on('data', (/** @type {Buffer} */ data) => {
@@ -27,10 +27,10 @@ function getConfig(complexity, taskType) {
27
27
  return 'full-workflow';
28
28
  };
29
29
 
30
- const getModel = (role) => {
31
- if (complexity === 'CRITICAL' && role === 'planner') return 'opus';
32
- if (complexity === 'TRIVIAL') return 'haiku';
33
- return 'sonnet';
30
+ const getLevel = (role) => {
31
+ if (complexity === 'CRITICAL' && role === 'planner') return 'level3';
32
+ if (complexity === 'TRIVIAL') return 'level1';
33
+ return 'level2';
34
34
  };
35
35
 
36
36
  const getValidatorCount = () => {
@@ -59,18 +59,18 @@ function getConfig(complexity, taskType) {
59
59
  };
60
60
 
61
61
  if (base === 'single-worker') {
62
- params.worker_model = getModel('worker');
62
+ params.worker_level = getLevel('worker');
63
63
  } else if (base === 'worker-validator') {
64
- params.worker_model = getModel('worker');
65
- params.validator_model = getModel('validator');
64
+ params.worker_level = getLevel('worker');
65
+ params.validator_level = getLevel('validator');
66
66
  } else if (base === 'debug-workflow') {
67
- params.investigator_model = getModel('planner');
68
- params.fixer_model = getModel('worker');
69
- params.tester_model = getModel('validator');
67
+ params.investigator_level = getLevel('planner');
68
+ params.fixer_level = getLevel('worker');
69
+ params.tester_level = getLevel('validator');
70
70
  } else if (base === 'full-workflow') {
71
- params.planner_model = getModel('planner');
72
- params.worker_model = getModel('worker');
73
- params.validator_model = getModel('validator');
71
+ params.planner_level = getLevel('planner');
72
+ params.worker_level = getLevel('worker');
73
+ params.validator_level = getLevel('validator');
74
74
  params.validator_count = getValidatorCount();
75
75
  }
76
76