@the-open-engine/zeroshot 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 (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,439 @@
1
+ import { existsSync, statSync, readFileSync, openSync, readSync, closeSync } from 'fs';
2
+ import chalk from 'chalk';
3
+ import { getTask } from '../store.js';
4
+ import { isProcessRunning } from '../runner.js';
5
+ import { createRequire } from 'module';
6
+
7
+ // Import cluster's stream parser (shared between task and cluster)
8
+ const require = createRequire(import.meta.url);
9
+ const { parseChunk } = require('../../lib/stream-json-parser');
10
+
11
+ // Tool icons for different tool types
12
+ const TOOL_ICONS = {
13
+ Read: '📖',
14
+ Write: '📝',
15
+ Edit: '✏️',
16
+ Bash: '💻',
17
+ Glob: '🔍',
18
+ Grep: '🔎',
19
+ WebFetch: '🌐',
20
+ WebSearch: '🔎',
21
+ Task: '🤖',
22
+ TodoWrite: '📋',
23
+ AskUserQuestion: '❓',
24
+ BashOutput: '📤',
25
+ KillShell: '🔪',
26
+ };
27
+
28
+ function getToolIcon(toolName) {
29
+ return TOOL_ICONS[toolName] || '🔧';
30
+ }
31
+
32
+ // Format tool call input for display
33
+ const TOOL_CALL_FORMATTERS = {
34
+ Bash: (input) => (input.command ? `$ ${input.command}` : ''),
35
+ Read: (input) => formatFilePathTail(input.file_path),
36
+ Write: (input) => formatFilePathTail(input.file_path, '→ '),
37
+ Edit: (input) => formatFilePathTail(input.file_path),
38
+ Glob: (input) => input.pattern || '',
39
+ Grep: (input) => (input.pattern ? `/${input.pattern}/` : ''),
40
+ WebFetch: (input) => (input.url ? input.url.substring(0, 50) : ''),
41
+ WebSearch: (input) => (input.query ? `"${input.query}"` : ''),
42
+ Task: (input) => input.description || '',
43
+ TodoWrite: (input) => formatTodoSummary(input),
44
+ AskUserQuestion: (input) => formatQuestionSummary(input),
45
+ };
46
+
47
+ function formatToolCall(toolName, input) {
48
+ if (!input) return '';
49
+
50
+ const formatter = TOOL_CALL_FORMATTERS[toolName] || formatUnknownToolCall;
51
+ return formatter(input);
52
+ }
53
+
54
+ function formatFilePathTail(filePath, prefix = '') {
55
+ if (!filePath) return '';
56
+ return `${prefix}${filePath.split('/').slice(-2).join('/')}`;
57
+ }
58
+
59
+ function formatTodoSummary(input) {
60
+ const todos = input.todos;
61
+ if (!Array.isArray(todos)) return '';
62
+
63
+ const statusCounts = {};
64
+ for (const todo of todos) {
65
+ statusCounts[todo.status] = (statusCounts[todo.status] || 0) + 1;
66
+ }
67
+
68
+ const parts = Object.entries(statusCounts).map(
69
+ ([status, count]) => `${count} ${status.replace('_', ' ')}`
70
+ );
71
+
72
+ return `${todos.length} todo${todos.length === 1 ? '' : 's'} (${parts.join(', ')})`;
73
+ }
74
+
75
+ function formatQuestionSummary(input) {
76
+ const questions = input.questions;
77
+ if (!Array.isArray(questions) || questions.length === 0) {
78
+ return '';
79
+ }
80
+
81
+ const preview = questions[0].question.substring(0, 50);
82
+ const suffix = questions[0].question.length > 50 ? '...' : '';
83
+ return questions.length > 1
84
+ ? `${questions.length} questions: "${preview}..."`
85
+ : `"${preview}${suffix}"`;
86
+ }
87
+
88
+ function formatUnknownToolCall(input) {
89
+ const keys = Object.keys(input);
90
+ if (keys.length === 0) {
91
+ return '';
92
+ }
93
+
94
+ const value = String(input[keys[0]]);
95
+ const preview = value.substring(0, 40);
96
+ return preview.length < value.length ? `${preview}...` : preview;
97
+ }
98
+
99
+ // Format tool result for display
100
+ function formatToolResult(content, isError, toolName, toolInput) {
101
+ if (!content) return isError ? 'error' : 'done';
102
+
103
+ // For errors, show full message
104
+ if (isError) {
105
+ const firstLine = content.split('\n')[0].substring(0, 80);
106
+ return chalk.red(firstLine);
107
+ }
108
+
109
+ // For TodoWrite, show the actual todo items
110
+ if (toolName === 'TodoWrite' && toolInput?.todos && Array.isArray(toolInput.todos)) {
111
+ const todos = toolInput.todos;
112
+ if (todos.length === 0) return chalk.dim('no todos');
113
+
114
+ // Helper to get status icon
115
+ const getStatusIcon = (todoStatus) => {
116
+ if (todoStatus === 'completed') return '✓';
117
+ if (todoStatus === 'in_progress') return '⧗';
118
+ return '○';
119
+ };
120
+
121
+ if (todos.length === 1) {
122
+ const status = getStatusIcon(todos[0].status);
123
+ const suffix = todos[0].content.length > 50 ? '...' : '';
124
+ return chalk.dim(`${status} ${todos[0].content.substring(0, 50)}${suffix}`);
125
+ }
126
+ // Multiple todos - show first one as preview
127
+ const status = getStatusIcon(todos[0].status);
128
+ return chalk.dim(
129
+ `${status} ${todos[0].content.substring(0, 40)}... (+${todos.length - 1} more)`
130
+ );
131
+ }
132
+
133
+ // For success, show summary
134
+ const lines = content.split('\n').filter((l) => l.trim());
135
+ if (lines.length === 0) return 'done';
136
+ if (lines.length === 1) {
137
+ const line = lines[0].substring(0, 60);
138
+ return chalk.dim(line.length < lines[0].length ? line + '...' : line);
139
+ }
140
+ // Multiple lines - show count
141
+ return chalk.dim(`${lines.length} lines`);
142
+ }
143
+
144
+ // Line buffer for accumulating text that streams without newlines
145
+ let lineBuffer = '';
146
+ let currentToolCall = null;
147
+
148
+ function resetState() {
149
+ lineBuffer = '';
150
+ currentToolCall = null;
151
+ }
152
+
153
+ // Flush pending text in buffer
154
+ function flushLineBuffer() {
155
+ if (lineBuffer.trim()) {
156
+ process.stdout.write(lineBuffer);
157
+ if (!lineBuffer.endsWith('\n')) {
158
+ process.stdout.write('\n');
159
+ }
160
+ }
161
+ lineBuffer = '';
162
+ }
163
+
164
+ // Accumulate text and print complete lines
165
+ function accumulateText(text) {
166
+ lineBuffer += text;
167
+
168
+ // Print complete lines, keep incomplete in buffer
169
+ const lines = lineBuffer.split('\n');
170
+ if (lines.length > 1) {
171
+ // Print all complete lines
172
+ for (let i = 0; i < lines.length - 1; i++) {
173
+ console.log(lines[i]);
174
+ }
175
+ // Keep incomplete line in buffer
176
+ lineBuffer = lines[lines.length - 1];
177
+ }
178
+ }
179
+
180
+ // Process parsed events and output formatted content
181
+ const EVENT_HANDLERS = {
182
+ text: handleTextEvent,
183
+ thinking: handleThinkingEvent,
184
+ thinking_start: handleThinkingEvent,
185
+ tool_start: handleToolStart,
186
+ tool_call: handleToolCall,
187
+ tool_input: handleToolInput,
188
+ tool_result: handleToolResult,
189
+ result: handleResult,
190
+ block_end: handleBlockEnd,
191
+ multi: handleMulti,
192
+ };
193
+
194
+ function processEvent(event) {
195
+ const handler = EVENT_HANDLERS[event.type];
196
+ if (handler) {
197
+ handler(event);
198
+ }
199
+ }
200
+
201
+ function handleTextEvent(event) {
202
+ accumulateText(event.text);
203
+ }
204
+
205
+ function handleThinkingEvent(event) {
206
+ if (event.text) {
207
+ console.log(chalk.dim.italic(event.text));
208
+ return;
209
+ }
210
+
211
+ if (event.type === 'thinking_start') {
212
+ console.log(chalk.dim.italic('💭 thinking...'));
213
+ }
214
+ }
215
+
216
+ function handleToolStart() {
217
+ flushLineBuffer();
218
+ }
219
+
220
+ function handleToolCall(event) {
221
+ flushLineBuffer();
222
+ const icon = getToolIcon(event.toolName);
223
+ const toolDesc = formatToolCall(event.toolName, event.input);
224
+ console.log(`${icon} ${chalk.cyan(event.toolName)} ${chalk.dim(toolDesc)}`);
225
+ currentToolCall = { toolName: event.toolName, input: event.input };
226
+ }
227
+
228
+ function handleToolInput() {
229
+ // Streaming tool input JSON - skip (shown in tool_call)
230
+ }
231
+
232
+ function handleToolResult(event) {
233
+ const status = event.isError ? chalk.red('✗') : chalk.green('✓');
234
+ const resultDesc = formatToolResult(
235
+ event.content,
236
+ event.isError,
237
+ currentToolCall?.toolName,
238
+ currentToolCall?.input
239
+ );
240
+ console.log(` ${status} ${resultDesc}`);
241
+ }
242
+
243
+ function handleResult(event) {
244
+ flushLineBuffer();
245
+ if (event.error) {
246
+ console.log(chalk.red(`\n✗ ERROR: ${event.error}`));
247
+ return;
248
+ }
249
+
250
+ console.log(chalk.green(`\n✓ Completed`));
251
+ if (event.cost) {
252
+ console.log(chalk.dim(` Cost: $${event.cost.toFixed(4)}`));
253
+ }
254
+ if (event.duration) {
255
+ const mins = Math.floor(event.duration / 60000);
256
+ const secs = Math.floor((event.duration % 60000) / 1000);
257
+ console.log(chalk.dim(` Duration: ${mins}m ${secs}s`));
258
+ }
259
+ }
260
+
261
+ function handleBlockEnd() {
262
+ // Content block ended
263
+ }
264
+
265
+ function handleMulti(event) {
266
+ if (event.events) {
267
+ for (const subEvent of event.events) {
268
+ processEvent(subEvent);
269
+ }
270
+ }
271
+ }
272
+
273
+ // Parse a raw log line (may have timestamp prefix)
274
+ function parseLogLine(line) {
275
+ let trimmed = line.trim();
276
+ if (!trimmed) return [];
277
+
278
+ // Strip timestamp prefix if present: [1234567890]{...} -> {...}
279
+ const timestampMatch = trimmed.match(/^\[\d+\](.*)$/);
280
+ if (timestampMatch) {
281
+ trimmed = timestampMatch[1];
282
+ }
283
+
284
+ // Non-JSON lines output as-is
285
+ if (!trimmed.startsWith('{')) {
286
+ return [{ type: 'text', text: trimmed + '\n' }];
287
+ }
288
+
289
+ // Parse JSON using cluster's parser
290
+ return parseChunk(trimmed);
291
+ }
292
+
293
+ export async function showLogs(taskId, options = {}) {
294
+ const task = getTask(taskId);
295
+
296
+ if (!task) {
297
+ console.log(chalk.red(`Task not found: ${taskId}`));
298
+ process.exit(1);
299
+ }
300
+
301
+ if (!existsSync(task.logFile)) {
302
+ console.log(chalk.yellow('Log file not found (task may still be starting).'));
303
+ return;
304
+ }
305
+
306
+ const lines = options.lines || 50;
307
+ const follow = options.follow;
308
+ const watch = options.watch;
309
+
310
+ if (watch) {
311
+ throw new Error('Task log TUI is unavailable in this release. Use `zeroshot logs <id> -f`.');
312
+ } else if (follow) {
313
+ // Stream logs continuously (like tail -f) - show last N lines first
314
+ await tailFollow(task.logFile, task.pid, lines);
315
+ } else {
316
+ await tailLines(task.logFile, lines);
317
+ }
318
+ }
319
+
320
+ function parseEventsFromLines(lines) {
321
+ const events = [];
322
+ for (const line of lines) {
323
+ events.push(...parseLogLine(line));
324
+ }
325
+ return events;
326
+ }
327
+
328
+ function renderEvents(events) {
329
+ for (const event of events) {
330
+ processEvent(event);
331
+ }
332
+ }
333
+
334
+ function renderLines(lines) {
335
+ for (const line of lines) {
336
+ renderEvents(parseLogLine(line));
337
+ }
338
+ }
339
+
340
+ function readFileSlice(file, start, end) {
341
+ const length = Math.max(0, end - start);
342
+ if (!length) return '';
343
+
344
+ const buffer = Buffer.alloc(length);
345
+ const fd = openSync(file, 'r');
346
+ readSync(fd, buffer, 0, buffer.length, start);
347
+ closeSync(fd);
348
+
349
+ return buffer.toString();
350
+ }
351
+
352
+ function shouldStopFollowing(noChangeCount, pid) {
353
+ return noChangeCount >= 10 && pid && !isProcessRunning(pid);
354
+ }
355
+
356
+ function handleFinalContent(file, lastSize, interval) {
357
+ const finalSize = statSync(file).size;
358
+ if (finalSize > lastSize) {
359
+ const finalContent = readFileSlice(file, lastSize, finalSize);
360
+ renderLines(finalContent.split('\n'));
361
+ flushLineBuffer();
362
+ }
363
+
364
+ console.log(chalk.dim('\n--- Task completed ---'));
365
+ clearInterval(interval);
366
+ process.exit(0);
367
+ }
368
+
369
+ function handleNewContent(file, lastSize, currentSize) {
370
+ const newContent = readFileSlice(file, lastSize, currentSize);
371
+ if (newContent) {
372
+ renderLines(newContent.split('\n'));
373
+ flushLineBuffer();
374
+ }
375
+ return currentSize;
376
+ }
377
+
378
+ function tailLines(file, n) {
379
+ resetState();
380
+ const rawContent = readFileSync(file, 'utf-8');
381
+ const rawLines = rawContent.split('\n');
382
+
383
+ // Parse and process all events
384
+ const allEvents = parseEventsFromLines(rawLines);
385
+ // Tail to last n events
386
+ renderEvents(allEvents.slice(-n));
387
+ flushLineBuffer();
388
+ }
389
+
390
+ async function tailFollow(file, pid, lines = 50) {
391
+ resetState();
392
+ // First, output last N events (like tail -f behavior)
393
+ const rawContent = readFileSync(file, 'utf-8');
394
+ const rawLines = rawContent.split('\n');
395
+
396
+ // Parse all events first
397
+ const allEvents = parseEventsFromLines(rawLines);
398
+ // Output only the last N events
399
+ renderEvents(allEvents.slice(-lines));
400
+ flushLineBuffer();
401
+
402
+ // Poll for changes (more reliable than fs.watch)
403
+ let lastSize = statSync(file).size;
404
+ let noChangeCount = 0;
405
+
406
+ const interval = setInterval(() => {
407
+ try {
408
+ const currentSize = statSync(file).size;
409
+
410
+ if (currentSize > lastSize) {
411
+ lastSize = handleNewContent(file, lastSize, currentSize);
412
+ noChangeCount = 0;
413
+ return;
414
+ }
415
+
416
+ noChangeCount++;
417
+
418
+ // Check if process is still running after 5 seconds of no output
419
+ if (shouldStopFollowing(noChangeCount, pid)) {
420
+ handleFinalContent(file, lastSize, interval);
421
+ }
422
+ } catch (err) {
423
+ // File might have been deleted
424
+ console.log(chalk.red(`\nError reading log: ${err.message}`));
425
+ clearInterval(interval);
426
+ process.exit(1);
427
+ }
428
+ }, 500); // Poll every 500ms
429
+
430
+ // Keep alive until Ctrl+C
431
+ process.on('SIGINT', () => {
432
+ clearInterval(interval);
433
+ console.log(chalk.dim('\nStopped following.'));
434
+ process.exit(0);
435
+ });
436
+
437
+ // Keep process running
438
+ await new Promise(() => {});
439
+ }
@@ -0,0 +1,42 @@
1
+ import chalk from 'chalk';
2
+ import { getTask } from '../store.js';
3
+ import { spawnTask } from '../runner.js';
4
+
5
+ export async function resumeTask(taskId, newPrompt) {
6
+ const task = getTask(taskId);
7
+
8
+ if (!task) {
9
+ console.log(chalk.red(`Task not found: ${taskId}`));
10
+ process.exit(1);
11
+ }
12
+
13
+ if (task.status === 'running') {
14
+ console.log(
15
+ chalk.yellow(`Task is still running. Use 'zeroshot logs -f ${taskId}' to follow output.`)
16
+ );
17
+ return;
18
+ }
19
+
20
+ const prompt = newPrompt || 'Continue from where you left off. Complete the task.';
21
+
22
+ console.log(chalk.dim(`Resuming task ${taskId}...`));
23
+ console.log(chalk.dim(`Original prompt: ${task.prompt}`));
24
+ console.log(chalk.dim(`Resume prompt: ${prompt}`));
25
+
26
+ const newTask = await spawnTask(prompt, {
27
+ cwd: task.cwd,
28
+ continue: true, // Use --continue to load most recent session in that directory
29
+ sessionId: task.sessionId,
30
+ provider: task.provider,
31
+ });
32
+
33
+ console.log(chalk.green(`\n✓ Resumed as new task: ${chalk.cyan(newTask.id)}`));
34
+ console.log(chalk.dim(` PID: ${newTask.pid}`));
35
+ console.log(chalk.dim(` Log: ${newTask.logFile}`));
36
+
37
+ console.log(chalk.dim('\nCommands:'));
38
+ console.log(chalk.dim(` zeroshot logs -f ${newTask.id} # Follow output`));
39
+ console.log();
40
+
41
+ return newTask;
42
+ }
@@ -0,0 +1,57 @@
1
+ import chalk from 'chalk';
2
+ import { spawnTask } from '../runner.js';
3
+
4
+ export async function runTask(prompt, options = {}) {
5
+ if (!prompt || prompt.trim().length === 0) {
6
+ console.log(chalk.red('Error: Prompt is required'));
7
+ process.exit(1);
8
+ }
9
+
10
+ const outputFormat = options.outputFormat || 'stream-json';
11
+ const jsonSchema = options.jsonSchema;
12
+ const silentJsonOutput = options.silentJsonOutput || false;
13
+
14
+ console.log(chalk.dim('Spawning task...'));
15
+ if (options.provider) {
16
+ console.log(chalk.dim(` Provider: ${options.provider}`));
17
+ }
18
+ if (options.model) {
19
+ console.log(chalk.dim(` Model: ${options.model}`));
20
+ }
21
+ if (options.modelLevel) {
22
+ console.log(chalk.dim(` Level: ${options.modelLevel}`));
23
+ }
24
+ if (jsonSchema && outputFormat === 'json') {
25
+ console.log(chalk.dim(` JSON Schema: enforced`));
26
+ if (silentJsonOutput) {
27
+ console.log(chalk.dim(` Silent mode: log contains ONLY final JSON`));
28
+ }
29
+ }
30
+
31
+ const task = await spawnTask(prompt, {
32
+ cwd: options.cwd || process.cwd(),
33
+ model: options.model,
34
+ modelLevel: options.modelLevel,
35
+ reasoningEffort: options.reasoningEffort,
36
+ provider: options.provider,
37
+ resume: options.resume,
38
+ continue: options.continue,
39
+ outputFormat,
40
+ jsonSchema,
41
+ silentJsonOutput,
42
+ });
43
+
44
+ console.log(chalk.green(`\n✓ Task spawned: ${chalk.cyan(task.id)}`));
45
+ console.log(chalk.dim(` Log: ${task.logFile}`));
46
+ console.log(chalk.dim(` CWD: ${task.cwd}`));
47
+
48
+ console.log(chalk.dim('\nCommands:'));
49
+ console.log(chalk.dim(` zeroshot attach ${task.id} # Attach to task (Ctrl+B d to detach)`));
50
+ console.log(chalk.dim(` zeroshot logs ${task.id} # View output`));
51
+ console.log(chalk.dim(` zeroshot logs -f ${task.id} # Follow output`));
52
+ console.log(chalk.dim(` zeroshot status ${task.id} # Check status`));
53
+ console.log(chalk.dim(` zeroshot kill ${task.id} # Stop task`));
54
+ console.log();
55
+
56
+ return task;
57
+ }
@@ -0,0 +1,105 @@
1
+ import chalk from 'chalk';
2
+ import { addSchedule, generateScheduleId, ensureDirs } from '../store.js';
3
+ import { parseInterval, calculateNextRun, getDaemonStatus } from '../scheduler.js';
4
+ import { fork } from 'child_process';
5
+ import { join, dirname } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ export function createSchedule(prompt, options = {}) {
11
+ if (!prompt || prompt.trim().length === 0) {
12
+ console.log(chalk.red('Error: Prompt is required'));
13
+ process.exit(1);
14
+ }
15
+
16
+ if (!options.every && !options.cron) {
17
+ console.log(chalk.red('Error: Either --every or --cron is required'));
18
+ console.log(chalk.dim(' Examples:'));
19
+ console.log(chalk.dim(' zeroshot schedule "backup" --every 1d'));
20
+ console.log(chalk.dim(' zeroshot schedule "cleanup" --cron "0 2 * * *"'));
21
+ process.exit(1);
22
+ }
23
+
24
+ ensureDirs();
25
+
26
+ let interval = null;
27
+ let cron = null;
28
+
29
+ if (options.every) {
30
+ interval = parseInterval(options.every);
31
+ if (!interval) {
32
+ console.log(chalk.red(`Error: Invalid interval format "${options.every}"`));
33
+ console.log(chalk.dim(' Valid formats: 30s, 5m, 2h, 1d, 1w'));
34
+ process.exit(1);
35
+ }
36
+ }
37
+
38
+ if (options.cron) {
39
+ cron = options.cron;
40
+ // Basic validation
41
+ if (cron.trim().split(/\s+/).length !== 5) {
42
+ console.log(chalk.red(`Error: Invalid cron format "${options.cron}"`));
43
+ console.log(chalk.dim(' Format: minute hour day month weekday'));
44
+ console.log(chalk.dim(' Example: "0 2 * * *" (daily at 2am)'));
45
+ process.exit(1);
46
+ }
47
+ }
48
+
49
+ const schedule = {
50
+ id: generateScheduleId(),
51
+ prompt: prompt.slice(0, 200) + (prompt.length > 200 ? '...' : ''),
52
+ fullPrompt: prompt,
53
+ cwd: options.cwd || process.cwd(),
54
+ interval,
55
+ cron,
56
+ nextRunAt: null,
57
+ lastRunAt: null,
58
+ lastTaskId: null,
59
+ enabled: true,
60
+ createdAt: new Date().toISOString(),
61
+ updatedAt: new Date().toISOString(),
62
+ };
63
+
64
+ // Calculate first run time
65
+ const nextRun = calculateNextRun(schedule);
66
+ schedule.nextRunAt = nextRun ? nextRun.toISOString() : null;
67
+
68
+ addSchedule(schedule);
69
+
70
+ console.log(chalk.green(`\n✓ Schedule created: ${chalk.cyan(schedule.id)}`));
71
+ console.log(chalk.dim(` Prompt: ${schedule.prompt}`));
72
+ console.log(chalk.dim(` CWD: ${schedule.cwd}`));
73
+
74
+ if (interval) {
75
+ const humanInterval = options.every;
76
+ console.log(chalk.dim(` Interval: every ${humanInterval}`));
77
+ }
78
+ if (cron) {
79
+ console.log(chalk.dim(` Cron: ${cron}`));
80
+ }
81
+ console.log(chalk.dim(` Next run: ${nextRun ? nextRun.toISOString() : 'N/A'}`));
82
+
83
+ // Check if scheduler is running, start if not
84
+ const status = getDaemonStatus();
85
+ if (!status.running) {
86
+ console.log(chalk.yellow('\n⚠ Scheduler daemon is not running'));
87
+ console.log(chalk.dim(' Starting scheduler daemon...'));
88
+
89
+ // Fork scheduler as detached process
90
+ const scheduler = fork(join(__dirname, '..', 'scheduler.js'), [], {
91
+ detached: true,
92
+ stdio: 'ignore',
93
+ });
94
+ scheduler.unref();
95
+
96
+ console.log(chalk.green(' ✓ Scheduler daemon started'));
97
+ }
98
+
99
+ console.log(chalk.dim('\nCommands:'));
100
+ console.log(chalk.dim(` zeroshot schedules # List all schedules`));
101
+ console.log(chalk.dim(` zeroshot unschedule ${schedule.id} # Remove this schedule`));
102
+ console.log();
103
+
104
+ return schedule;
105
+ }