@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.3

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 (265) hide show
  1. package/dist/bot/ai-client.d.ts +22 -2
  2. package/dist/bot/ai-client.d.ts.map +1 -1
  3. package/dist/bot/ai-client.js +168 -20
  4. package/dist/bot/ai-client.js.map +1 -1
  5. package/dist/bot/assistant-core.d.ts +25 -0
  6. package/dist/bot/assistant-core.d.ts.map +1 -0
  7. package/dist/bot/assistant-core.js +265 -0
  8. package/dist/bot/assistant-core.js.map +1 -0
  9. package/dist/bot/assistant-tools.d.ts +9 -0
  10. package/dist/bot/assistant-tools.d.ts.map +1 -0
  11. package/dist/bot/assistant-tools.js +602 -0
  12. package/dist/bot/assistant-tools.js.map +1 -0
  13. package/dist/bot/audit-logger.d.ts.map +1 -1
  14. package/dist/bot/audit-logger.js +9 -5
  15. package/dist/bot/audit-logger.js.map +1 -1
  16. package/dist/bot/audit-store.d.ts.map +1 -1
  17. package/dist/bot/audit-store.js +3 -11
  18. package/dist/bot/audit-store.js.map +1 -1
  19. package/dist/bot/bot-manager.d.ts +49 -0
  20. package/dist/bot/bot-manager.d.ts.map +1 -0
  21. package/dist/bot/bot-manager.js +279 -0
  22. package/dist/bot/bot-manager.js.map +1 -0
  23. package/dist/bot/child-process-tracker.d.ts +6 -0
  24. package/dist/bot/child-process-tracker.d.ts.map +1 -0
  25. package/dist/bot/child-process-tracker.js +35 -0
  26. package/dist/bot/child-process-tracker.js.map +1 -0
  27. package/dist/bot/cli-provider.d.ts.map +1 -1
  28. package/dist/bot/cli-provider.js +13 -8
  29. package/dist/bot/cli-provider.js.map +1 -1
  30. package/dist/bot/conversation-store.d.ts +40 -0
  31. package/dist/bot/conversation-store.d.ts.map +1 -0
  32. package/dist/bot/conversation-store.js +182 -0
  33. package/dist/bot/conversation-store.js.map +1 -0
  34. package/dist/bot/cost-store.d.ts.map +1 -1
  35. package/dist/bot/cost-store.js +10 -14
  36. package/dist/bot/cost-store.js.map +1 -1
  37. package/dist/bot/error-guide.d.ts +10 -0
  38. package/dist/bot/error-guide.d.ts.map +1 -0
  39. package/dist/bot/error-guide.js +34 -0
  40. package/dist/bot/error-guide.js.map +1 -0
  41. package/dist/bot/genesis-store.d.ts.map +1 -1
  42. package/dist/bot/genesis-store.js +11 -20
  43. package/dist/bot/genesis-store.js.map +1 -1
  44. package/dist/bot/index.d.ts +3 -0
  45. package/dist/bot/index.d.ts.map +1 -1
  46. package/dist/bot/index.js +3 -0
  47. package/dist/bot/index.js.map +1 -1
  48. package/dist/bot/knowledge-store.d.ts +17 -0
  49. package/dist/bot/knowledge-store.d.ts.map +1 -0
  50. package/dist/bot/knowledge-store.js +53 -0
  51. package/dist/bot/knowledge-store.js.map +1 -0
  52. package/dist/bot/pipeline-runner.d.ts.map +1 -1
  53. package/dist/bot/pipeline-runner.js +8 -1
  54. package/dist/bot/pipeline-runner.js.map +1 -1
  55. package/dist/bot/retry-utils.d.ts +19 -0
  56. package/dist/bot/retry-utils.d.ts.map +1 -0
  57. package/dist/bot/retry-utils.js +64 -0
  58. package/dist/bot/retry-utils.js.map +1 -0
  59. package/dist/bot/run-store.d.ts.map +1 -1
  60. package/dist/bot/run-store.js +2 -10
  61. package/dist/bot/run-store.js.map +1 -1
  62. package/dist/bot/runner.d.ts.map +1 -1
  63. package/dist/bot/runner.js +24 -3
  64. package/dist/bot/runner.js.map +1 -1
  65. package/dist/bot/safe-json.d.ts +32 -0
  66. package/dist/bot/safe-json.d.ts.map +1 -0
  67. package/dist/bot/safe-json.js +56 -0
  68. package/dist/bot/safe-json.js.map +1 -0
  69. package/dist/bot/safe-path.d.ts +18 -0
  70. package/dist/bot/safe-path.d.ts.map +1 -0
  71. package/dist/bot/safe-path.js +40 -0
  72. package/dist/bot/safe-path.js.map +1 -0
  73. package/dist/bot/session-state.d.ts.map +1 -1
  74. package/dist/bot/session-state.js +3 -1
  75. package/dist/bot/session-state.js.map +1 -1
  76. package/dist/bot/steering.js +1 -1
  77. package/dist/bot/steering.js.map +1 -1
  78. package/dist/bot/step-executor.d.ts +10 -5
  79. package/dist/bot/step-executor.d.ts.map +1 -1
  80. package/dist/bot/step-executor.js +252 -3
  81. package/dist/bot/step-executor.js.map +1 -1
  82. package/dist/bot/system-prompt.d.ts +1 -1
  83. package/dist/bot/system-prompt.d.ts.map +1 -1
  84. package/dist/bot/system-prompt.js +69 -43
  85. package/dist/bot/system-prompt.js.map +1 -1
  86. package/dist/bot/task-decomposer.d.ts +24 -0
  87. package/dist/bot/task-decomposer.d.ts.map +1 -0
  88. package/dist/bot/task-decomposer.js +75 -0
  89. package/dist/bot/task-decomposer.js.map +1 -0
  90. package/dist/bot/task-queue.d.ts +17 -4
  91. package/dist/bot/task-queue.d.ts.map +1 -1
  92. package/dist/bot/task-queue.js +102 -14
  93. package/dist/bot/task-queue.js.map +1 -1
  94. package/dist/bot/terminal-renderer.d.ts +60 -0
  95. package/dist/bot/terminal-renderer.d.ts.map +1 -0
  96. package/dist/bot/terminal-renderer.js +205 -0
  97. package/dist/bot/terminal-renderer.js.map +1 -0
  98. package/dist/bot/types.d.ts +7 -0
  99. package/dist/bot/types.d.ts.map +1 -1
  100. package/dist/bot/weaver-tools.d.ts +18 -0
  101. package/dist/bot/weaver-tools.d.ts.map +1 -0
  102. package/dist/bot/weaver-tools.js +215 -0
  103. package/dist/bot/weaver-tools.js.map +1 -0
  104. package/dist/cli-bridge.d.ts.map +1 -1
  105. package/dist/cli-bridge.js +10 -3
  106. package/dist/cli-bridge.js.map +1 -1
  107. package/dist/cli-handlers.d.ts +15 -1
  108. package/dist/cli-handlers.d.ts.map +1 -1
  109. package/dist/cli-handlers.js +742 -28
  110. package/dist/cli-handlers.js.map +1 -1
  111. package/dist/handlers/on-bot-completed.d.ts +21 -0
  112. package/dist/handlers/on-bot-completed.d.ts.map +1 -0
  113. package/dist/handlers/on-bot-completed.js +28 -0
  114. package/dist/handlers/on-bot-completed.js.map +1 -0
  115. package/dist/handlers/on-execution-failure.d.ts +23 -0
  116. package/dist/handlers/on-execution-failure.d.ts.map +1 -0
  117. package/dist/handlers/on-execution-failure.js +28 -0
  118. package/dist/handlers/on-execution-failure.js.map +1 -0
  119. package/dist/handlers/scheduled-run.d.ts +24 -0
  120. package/dist/handlers/scheduled-run.d.ts.map +1 -0
  121. package/dist/handlers/scheduled-run.js +25 -0
  122. package/dist/handlers/scheduled-run.js.map +1 -0
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +4 -0
  126. package/dist/index.js.map +1 -1
  127. package/dist/mcp-tools.js +2 -2
  128. package/dist/mcp-tools.js.map +1 -1
  129. package/dist/node-types/abort-task.d.ts.map +1 -1
  130. package/dist/node-types/abort-task.js +4 -3
  131. package/dist/node-types/abort-task.js.map +1 -1
  132. package/dist/node-types/agent-execute.d.ts +38 -0
  133. package/dist/node-types/agent-execute.d.ts.map +1 -0
  134. package/dist/node-types/agent-execute.js +256 -0
  135. package/dist/node-types/agent-execute.js.map +1 -0
  136. package/dist/node-types/bot-report.d.ts +5 -3
  137. package/dist/node-types/bot-report.d.ts.map +1 -1
  138. package/dist/node-types/bot-report.js +39 -7
  139. package/dist/node-types/bot-report.js.map +1 -1
  140. package/dist/node-types/build-context.d.ts +3 -3
  141. package/dist/node-types/build-context.d.ts.map +1 -1
  142. package/dist/node-types/build-context.js +108 -24
  143. package/dist/node-types/build-context.js.map +1 -1
  144. package/dist/node-types/detect-provider.d.ts +2 -2
  145. package/dist/node-types/detect-provider.d.ts.map +1 -1
  146. package/dist/node-types/detect-provider.js +3 -1
  147. package/dist/node-types/detect-provider.js.map +1 -1
  148. package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
  149. package/dist/node-types/exec-validate-retry.js +43 -6
  150. package/dist/node-types/exec-validate-retry.js.map +1 -1
  151. package/dist/node-types/execute-plan.d.ts.map +1 -1
  152. package/dist/node-types/execute-plan.js +31 -8
  153. package/dist/node-types/execute-plan.js.map +1 -1
  154. package/dist/node-types/execute-target.d.ts.map +1 -1
  155. package/dist/node-types/execute-target.js +3 -1
  156. package/dist/node-types/execute-target.js.map +1 -1
  157. package/dist/node-types/fix-errors.d.ts.map +1 -1
  158. package/dist/node-types/fix-errors.js +21 -5
  159. package/dist/node-types/fix-errors.js.map +1 -1
  160. package/dist/node-types/genesis-observe.d.ts.map +1 -1
  161. package/dist/node-types/genesis-observe.js +3 -1
  162. package/dist/node-types/genesis-observe.js.map +1 -1
  163. package/dist/node-types/genesis-report.js +4 -1
  164. package/dist/node-types/genesis-report.js.map +1 -1
  165. package/dist/node-types/git-ops.d.ts.map +1 -1
  166. package/dist/node-types/git-ops.js +98 -4
  167. package/dist/node-types/git-ops.js.map +1 -1
  168. package/dist/node-types/index.d.ts +2 -0
  169. package/dist/node-types/index.d.ts.map +1 -1
  170. package/dist/node-types/index.js +2 -0
  171. package/dist/node-types/index.js.map +1 -1
  172. package/dist/node-types/load-config.d.ts +2 -2
  173. package/dist/node-types/load-config.d.ts.map +1 -1
  174. package/dist/node-types/load-config.js.map +1 -1
  175. package/dist/node-types/plan-task.d.ts.map +1 -1
  176. package/dist/node-types/plan-task.js +14 -2
  177. package/dist/node-types/plan-task.js.map +1 -1
  178. package/dist/node-types/read-workflow.js +8 -2
  179. package/dist/node-types/read-workflow.js.map +1 -1
  180. package/dist/node-types/receive-task.d.ts.map +1 -1
  181. package/dist/node-types/receive-task.js +35 -26
  182. package/dist/node-types/receive-task.js.map +1 -1
  183. package/dist/node-types/send-notify.js +2 -1
  184. package/dist/node-types/send-notify.js.map +1 -1
  185. package/dist/node-types/validate-gate.d.ts +18 -0
  186. package/dist/node-types/validate-gate.d.ts.map +1 -0
  187. package/dist/node-types/validate-gate.js +96 -0
  188. package/dist/node-types/validate-gate.js.map +1 -0
  189. package/dist/workflows/genesis-task.d.ts +20 -12
  190. package/dist/workflows/genesis-task.d.ts.map +1 -1
  191. package/dist/workflows/genesis-task.js +20 -12
  192. package/dist/workflows/genesis-task.js.map +1 -1
  193. package/dist/workflows/weaver-agent.d.ts +35 -0
  194. package/dist/workflows/weaver-agent.d.ts.map +1 -0
  195. package/dist/workflows/weaver-agent.js +777 -0
  196. package/dist/workflows/weaver-agent.js.map +1 -0
  197. package/dist/workflows/weaver-bot-batch.d.ts +19 -26
  198. package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
  199. package/dist/workflows/weaver-bot-batch.js +1043 -27
  200. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  201. package/dist/workflows/weaver-bot.d.ts +21 -35
  202. package/dist/workflows/weaver-bot.d.ts.map +1 -1
  203. package/dist/workflows/weaver-bot.js +1119 -36
  204. package/dist/workflows/weaver-bot.js.map +1 -1
  205. package/flowweaver.manifest.json +113 -2
  206. package/package.json +5 -2
  207. package/src/bot/ai-client.ts +180 -19
  208. package/src/bot/assistant-core.ts +306 -0
  209. package/src/bot/assistant-tools.ts +605 -0
  210. package/src/bot/audit-logger.ts +6 -5
  211. package/src/bot/audit-store.ts +3 -12
  212. package/src/bot/bot-manager.ts +293 -0
  213. package/src/bot/child-process-tracker.ts +40 -0
  214. package/src/bot/cli-provider.ts +13 -8
  215. package/src/bot/conversation-store.ts +222 -0
  216. package/src/bot/cost-store.ts +11 -12
  217. package/src/bot/error-guide.ts +34 -0
  218. package/src/bot/genesis-store.ts +11 -17
  219. package/src/bot/index.ts +5 -0
  220. package/src/bot/knowledge-store.ts +59 -0
  221. package/src/bot/pipeline-runner.ts +7 -1
  222. package/src/bot/retry-utils.ts +76 -0
  223. package/src/bot/run-store.ts +2 -11
  224. package/src/bot/runner.ts +26 -3
  225. package/src/bot/safe-json.ts +76 -0
  226. package/src/bot/safe-path.ts +44 -0
  227. package/src/bot/session-state.ts +2 -1
  228. package/src/bot/steering.ts +1 -1
  229. package/src/bot/step-executor.ts +313 -5
  230. package/src/bot/system-prompt.ts +70 -47
  231. package/src/bot/task-decomposer.ts +100 -0
  232. package/src/bot/task-queue.ts +119 -15
  233. package/src/bot/terminal-renderer.ts +241 -0
  234. package/src/bot/types.ts +8 -0
  235. package/src/bot/weaver-tools.ts +225 -0
  236. package/src/cli-bridge.ts +14 -3
  237. package/src/cli-handlers.ts +760 -29
  238. package/src/handlers/on-bot-completed.ts +48 -0
  239. package/src/handlers/on-execution-failure.ts +42 -0
  240. package/src/handlers/scheduled-run.ts +42 -0
  241. package/src/index.ts +5 -0
  242. package/src/mcp-tools.ts +2 -2
  243. package/src/node-types/abort-task.ts +5 -4
  244. package/src/node-types/agent-execute.ts +306 -0
  245. package/src/node-types/bot-report.ts +40 -9
  246. package/src/node-types/build-context.ts +112 -25
  247. package/src/node-types/detect-provider.ts +4 -3
  248. package/src/node-types/exec-validate-retry.ts +47 -8
  249. package/src/node-types/execute-plan.ts +32 -8
  250. package/src/node-types/execute-target.ts +2 -1
  251. package/src/node-types/fix-errors.ts +20 -5
  252. package/src/node-types/genesis-observe.ts +2 -1
  253. package/src/node-types/genesis-report.ts +1 -1
  254. package/src/node-types/git-ops.ts +93 -4
  255. package/src/node-types/index.ts +2 -0
  256. package/src/node-types/load-config.ts +3 -3
  257. package/src/node-types/plan-task.ts +15 -3
  258. package/src/node-types/read-workflow.ts +2 -2
  259. package/src/node-types/receive-task.ts +31 -26
  260. package/src/node-types/send-notify.ts +1 -1
  261. package/src/node-types/validate-gate.ts +112 -0
  262. package/src/workflows/genesis-task.ts +20 -12
  263. package/src/workflows/weaver-agent.ts +799 -0
  264. package/src/workflows/weaver-bot-batch.ts +1049 -27
  265. package/src/workflows/weaver-bot.ts +1123 -36
@@ -13,7 +13,7 @@ import type { ExecutionEvent, WeaverConfig, RunRecord, RunOutcome, RunCostSummar
13
13
  import { AuditStore } from './bot/audit-store.js';
14
14
 
15
15
  export interface ParsedArgs {
16
- command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'genesis' | 'audit';
16
+ command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'status' | 'genesis' | 'audit' | 'init' | 'assistant' | 'examples' | 'doctor';
17
17
  file?: string;
18
18
  verbose: boolean;
19
19
  dryRun: boolean;
@@ -57,6 +57,16 @@ export interface ParsedArgs {
57
57
  genesisWatch: boolean;
58
58
  // eject
59
59
  ejectWorkflow?: string;
60
+ // session scheduling
61
+ sessionContinuous: boolean;
62
+ sessionUntil?: string;
63
+ sessionMaxTasks?: number;
64
+ sessionParallel?: number;
65
+ // assistant
66
+ assistantNew?: boolean;
67
+ assistantResume?: string;
68
+ assistantList?: boolean;
69
+ assistantDelete?: string;
60
70
  }
61
71
 
62
72
  export function parseArgs(argv: string[]): ParsedArgs {
@@ -81,6 +91,7 @@ export function parseArgs(argv: string[]): ParsedArgs {
81
91
  autoApprove: false,
82
92
  genesisInit: false,
83
93
  genesisWatch: false,
94
+ sessionContinuous: false,
84
95
  };
85
96
 
86
97
  const args = argv.slice(2);
@@ -192,6 +203,14 @@ export function parseArgs(argv: string[]): ParsedArgs {
192
203
  }
193
204
  } else if (arg === 'session') {
194
205
  result.command = 'session';
206
+ } else if (arg === 'assistant') {
207
+ result.command = 'assistant';
208
+ } else if (arg === 'examples') {
209
+ result.command = 'examples';
210
+ } else if (arg === 'doctor') {
211
+ result.command = 'doctor';
212
+ } else if (arg === 'status') {
213
+ result.command = 'status';
195
214
  } else if (arg === 'steer') {
196
215
  result.command = 'steer';
197
216
  // Next arg is the subcommand
@@ -236,10 +255,33 @@ export function parseArgs(argv: string[]): ParsedArgs {
236
255
  }
237
256
  } else if (arg === 'genesis') {
238
257
  result.command = 'genesis';
258
+ } else if (arg === 'init') {
259
+ result.command = 'init';
239
260
  } else if (arg === '--init') {
240
261
  result.genesisInit = true;
241
262
  } else if (arg === '--watch') {
242
263
  result.genesisWatch = true;
264
+ } else if (arg === '--new') {
265
+ result.assistantNew = true;
266
+ } else if (arg === '--resume' && i + 1 < args.length) {
267
+ i++;
268
+ result.assistantResume = args[i];
269
+ } else if (arg === '--list') {
270
+ result.assistantList = true;
271
+ } else if (arg === '--delete' && i + 1 < args.length) {
272
+ i++;
273
+ result.assistantDelete = args[i];
274
+ } else if (arg === '--continuous') {
275
+ result.sessionContinuous = true;
276
+ } else if (arg === '--until' && i + 1 < args.length) {
277
+ i++;
278
+ result.sessionUntil = args[i];
279
+ } else if (arg === '--max-tasks' && i + 1 < args.length) {
280
+ i++;
281
+ result.sessionMaxTasks = parseInt(args[i]!, 10) || undefined;
282
+ } else if (arg === '--parallel' && i + 1 < args.length) {
283
+ i++;
284
+ result.sessionParallel = Math.min(Math.max(parseInt(args[i]!, 10) || 1, 1), 5);
243
285
  } else if (arg === '--project-dir' && i + 1 < args.length) {
244
286
  i++;
245
287
  result.file = args[i];
@@ -337,6 +379,14 @@ function printRunDetail(r: RunRecord): void {
337
379
  if (r.params) {
338
380
  console.log(` Params: ${JSON.stringify(r.params)}`);
339
381
  }
382
+ if (r.stepLog && r.stepLog.length > 0) {
383
+ console.log(`\n Steps (${r.stepLog.length}):`);
384
+ for (const entry of r.stepLog) {
385
+ const icon = entry.status === 'ok' ? '\x1b[32m+\x1b[0m' : entry.status === 'blocked' ? '\x1b[33m⚠\x1b[0m' : '\x1b[31m✗\x1b[0m';
386
+ const detail = entry.detail ? ` — ${entry.detail}` : '';
387
+ console.log(` ${icon} ${entry.step}${detail}`);
388
+ }
389
+ }
340
390
  console.log('');
341
391
  }
342
392
 
@@ -448,11 +498,19 @@ export async function handleCosts(opts: ParsedArgs): Promise<void> {
448
498
  const summary = store.summarize({ since: sinceTs, model: opts.costsModel });
449
499
 
450
500
  if (summary.totalRuns === 0) {
451
- console.log('No cost data found.');
501
+ if (opts.historyJson) {
502
+ console.log(JSON.stringify(summary, null, 2));
503
+ } else {
504
+ console.log('No cost data found.');
505
+ }
452
506
  return;
453
507
  }
454
508
 
455
- console.log(formatCostTable(summary));
509
+ if (opts.historyJson) {
510
+ console.log(JSON.stringify(summary, null, 2));
511
+ } else {
512
+ console.log(formatCostTable(summary));
513
+ }
456
514
  }
457
515
 
458
516
  export async function handleWatch(opts: ParsedArgs): Promise<void> {
@@ -704,6 +762,7 @@ export async function handleProviders(): Promise<void> {
704
762
 
705
763
  const MANAGED_WORKFLOWS: Record<string, string> = {
706
764
  bot: 'weaver-bot',
765
+ agent: 'weaver-agent',
707
766
  batch: 'weaver-bot-batch',
708
767
  genesis: 'genesis-task',
709
768
  };
@@ -1211,36 +1270,324 @@ export async function handleBot(opts: ParsedArgs): Promise<void> {
1211
1270
  }
1212
1271
 
1213
1272
  export async function handleSession(opts: ParsedArgs): Promise<void> {
1214
- const workflowPath = resolveWorkflowPath('bot', opts.file ?? process.cwd());
1215
-
1273
+ const projectDir = opts.file ?? process.cwd();
1274
+ // Set project dir for per-project queue isolation
1275
+ process.env.WEAVER_PROJECT_DIR = projectDir;
1216
1276
  const config = await loadConfig(opts.configPath);
1277
+ const workflowPath = resolveWorkflowPath('agent', projectDir);
1278
+
1279
+ // Create terminal renderer for all session output
1280
+ const { TerminalRenderer } = await import('./bot/terminal-renderer.js');
1281
+ const renderer = new TerminalRenderer({ verbose: opts.verbose, quiet: opts.quiet });
1282
+
1283
+ // Parse --until HH:MM into a deadline timestamp
1284
+ let deadline: number | undefined;
1285
+ let deadlineStr: string | undefined;
1286
+ if (opts.sessionUntil) {
1287
+ const match = opts.sessionUntil.match(/^(\d{1,2}):(\d{2})$/);
1288
+ if (match) {
1289
+ const now = new Date();
1290
+ const target = new Date(now);
1291
+ target.setHours(parseInt(match[1]!, 10), parseInt(match[2]!, 10), 0, 0);
1292
+ if (target.getTime() <= now.getTime()) target.setDate(target.getDate() + 1);
1293
+ deadline = target.getTime();
1294
+ deadlineStr = target.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
1295
+ }
1296
+ }
1217
1297
 
1218
- if (!opts.quiet) {
1219
- console.log('[weaver] Starting bot session (Ctrl+C to stop)');
1220
- console.log('[weaver] Add tasks with: flow-weaver weaver queue add "task"');
1298
+ const maxTasks = opts.sessionMaxTasks ?? Infinity;
1299
+ const continuous = opts.sessionContinuous || !!opts.sessionUntil || maxTasks < Infinity;
1300
+ const parallelism = opts.sessionParallel ?? 1;
1301
+
1302
+ // Crash recovery
1303
+ if (continuous) {
1304
+ const { TaskQueue } = await import('./bot/task-queue.js');
1305
+ const recoveryQueue = new TaskQueue();
1306
+ const recovered = await recoveryQueue.recoverOrphans();
1307
+ if (recovered > 0) renderer.info(`Recovered ${recovered} orphaned task(s)`);
1221
1308
  }
1222
1309
 
1310
+ // Clean stale cache files
1223
1311
  try {
1224
- const result = await runWorkflow(workflowPath, {
1225
- params: { projectDir: opts.file ?? process.cwd() },
1226
- verbose: opts.verbose,
1227
- dryRun: opts.dryRun,
1228
- config,
1229
- });
1312
+ const { execSync: execSyncClean } = await import('node:child_process');
1313
+ const staleOutput = execSyncClean(`find "${projectDir}" -name "fw-exec-*" -type f`, { encoding: 'utf-8', timeout: 5000 }).trim();
1314
+ if (staleOutput) {
1315
+ const staleFiles = staleOutput.split('\n').filter(Boolean);
1316
+ for (const f of staleFiles) { try { fs.unlinkSync(f); } catch {} }
1317
+ }
1318
+ } catch { /* non-fatal */ }
1230
1319
 
1231
- if (!opts.quiet) {
1232
- const color = result.success ? '\x1b[32m' : '\x1b[31m';
1233
- console.log(`${color}Session: ${result.outcome}\x1b[0m`);
1320
+ // Detect provider label for session start
1321
+ const providerType = config?.provider ?? 'auto';
1322
+ const providerLabel = typeof providerType === 'object' ? providerType.name : String(providerType);
1323
+ const sessionStartTime = Date.now();
1324
+ renderer.sessionStart({ provider: providerLabel, parallel: parallelism, deadline: deadlineStr });
1325
+
1326
+ // Single-run mode (backwards compatible)
1327
+ if (!continuous) {
1328
+ try {
1329
+ const result = await runWorkflow(workflowPath, {
1330
+ params: { projectDir },
1331
+ verbose: opts.verbose,
1332
+ dryRun: opts.dryRun,
1333
+ config,
1334
+ });
1335
+ if (!opts.quiet) {
1336
+ const color = result.success ? '\x1b[32m' : '\x1b[31m';
1337
+ console.log(`${color}Session: ${result.outcome}\x1b[0m`);
1338
+ }
1339
+ process.exit(result.success ? 0 : 1);
1340
+ } catch (err: unknown) {
1341
+ const msg = err instanceof Error ? err.message : String(err);
1342
+ console.error(`\x1b[31m[weaver] Fatal: ${msg}\x1b[0m`);
1343
+ process.exit(1);
1234
1344
  }
1345
+ return;
1346
+ }
1235
1347
 
1236
- process.exit(result.success ? 0 : 1);
1237
- } catch (err: unknown) {
1238
- const msg = err instanceof Error ? err.message : String(err);
1239
- console.error(`\x1b[31m[weaver] Fatal: ${msg}\x1b[0m`);
1240
- process.exit(1);
1348
+ // Continuous mode: loop until deadline/maxTasks/interrupt
1349
+ const { TaskQueue } = await import('./bot/task-queue.js');
1350
+ const { isTransientError } = await import('./bot/retry-utils.js');
1351
+ const { getErrorGuidance } = await import('./bot/error-guide.js');
1352
+ const queue = new TaskQueue();
1353
+ let taskCount = 0;
1354
+ let interrupted = false;
1355
+ let consecutiveErrors = 0;
1356
+ let consecutiveNoOps = 0;
1357
+ const MAX_CONSECUTIVE_ERRORS = 3;
1358
+ const MAX_CONSECUTIVE_NO_OPS = 5;
1359
+
1360
+ // Session stats
1361
+ let sessionCompleted = 0, sessionFailed = 0, sessionNoOp = 0;
1362
+ let sessionInputTokens = 0, sessionOutputTokens = 0, sessionCost = 0;
1363
+
1364
+ process.on('SIGINT', () => { interrupted = true; });
1365
+ process.on('SIGTERM', () => { interrupted = true; });
1366
+
1367
+ // Parallel task tracking
1368
+ const running = new Map<string, Promise<void>>();
1369
+ const filesInUse = new Set<string>();
1370
+
1371
+ const processTask = async (task: { id: string; instruction?: string; targets?: string[] }) => {
1372
+ try {
1373
+ const result = await runWorkflow(workflowPath, {
1374
+ params: { projectDir, taskJson: JSON.stringify(task) },
1375
+ verbose: opts.verbose,
1376
+ dryRun: opts.dryRun,
1377
+ config,
1378
+ });
1379
+
1380
+ // Classify outcome: the summary contains "no changes" or "0 files" for no-ops
1381
+ const isNoOp = result.success && (
1382
+ result.summary.includes('no changes') ||
1383
+ result.summary.includes('0 file') ||
1384
+ result.summary.includes("doesn't exist") ||
1385
+ result.summary.includes('does not exist') ||
1386
+ result.summary.includes('nothing to') ||
1387
+ result.outcome === 'no-op'
1388
+ );
1389
+
1390
+ if (isNoOp) {
1391
+ await queue.markNoOp(task.id);
1392
+ sessionNoOp++;
1393
+ consecutiveNoOps++;
1394
+ consecutiveErrors = 0;
1395
+ } else if (result.success) {
1396
+ await queue.markComplete(task.id);
1397
+ sessionCompleted++;
1398
+ consecutiveErrors = 0;
1399
+ consecutiveNoOps = 0;
1400
+ } else {
1401
+ await queue.markFailed(task.id, result.summary || result.outcome || 'unknown error');
1402
+ sessionFailed++;
1403
+ consecutiveErrors++;
1404
+ consecutiveNoOps = 0;
1405
+ }
1406
+
1407
+ // Track cost from workflow result
1408
+ if (result.cost) {
1409
+ sessionInputTokens += result.cost.totalInputTokens ?? 0;
1410
+ sessionOutputTokens += result.cost.totalOutputTokens ?? 0;
1411
+ sessionCost += result.cost.totalCost ?? 0;
1412
+ }
1413
+
1414
+ } catch (err: unknown) {
1415
+ const msg = err instanceof Error ? err.message : String(err);
1416
+ const guidance = getErrorGuidance(msg);
1417
+ renderer.error(`Task ${task.id.slice(0, 8)} error`, guidance ? `${msg}\n Hint: ${guidance}` : msg);
1418
+ await queue.markFailed(task.id, msg);
1419
+ sessionFailed++;
1420
+ if (!isTransientError(err)) {
1421
+ consecutiveErrors++;
1422
+ }
1423
+ } finally {
1424
+ for (const f of task.targets ?? []) filesInUse.delete(f);
1425
+ running.delete(task.id);
1426
+ }
1427
+ };
1428
+
1429
+ while (taskCount < maxTasks && !interrupted) {
1430
+ if (deadline && Date.now() >= deadline) {
1431
+ renderer.info('Deadline reached, stopping session.');
1432
+ break;
1433
+ }
1434
+
1435
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1436
+ renderer.error('Session stopped', `${MAX_CONSECUTIVE_ERRORS} consecutive errors — check your API key or provider config.`);
1437
+ break;
1438
+ }
1439
+
1440
+ // Pause on consecutive no-ops (bot is spinning without doing anything)
1441
+ if (consecutiveNoOps >= MAX_CONSECUTIVE_NO_OPS) {
1442
+ renderer.warn(`${MAX_CONSECUTIVE_NO_OPS} consecutive no-op tasks — pausing 60s`);
1443
+ await new Promise(r => setTimeout(r, 60_000));
1444
+ consecutiveNoOps = 0;
1445
+ }
1446
+
1447
+ // Wait if at capacity
1448
+ if (running.size >= parallelism) {
1449
+ await Promise.race(running.values());
1450
+ continue;
1451
+ }
1452
+
1453
+ const task = await queue.next();
1454
+ if (!task) {
1455
+ if (running.size > 0) {
1456
+ // Tasks still running — wait for one to finish
1457
+ await Promise.race(running.values());
1458
+ continue;
1459
+ }
1460
+ // No pending or running tasks — wait and retry
1461
+ await new Promise(r => setTimeout(r, 5_000));
1462
+ continue;
1463
+ }
1464
+
1465
+ // Auto-decompose broad tasks into per-file tasks
1466
+ const { decomposeTask } = await import('./bot/task-decomposer.js');
1467
+ const { decomposed, tasks: subtasks } = decomposeTask(task, projectDir);
1468
+ if (decomposed && subtasks.length > 1) {
1469
+ // Replace the broad task with per-file tasks
1470
+ await queue.markComplete(task.id);
1471
+ let decomposedCount = 0;
1472
+ for (const st of subtasks) {
1473
+ const { duplicate } = await queue.add({ instruction: st.instruction, mode: st.mode as 'modify', targets: st.targets, priority: st.priority ?? 0 });
1474
+ if (!duplicate) decomposedCount++;
1475
+ }
1476
+ renderer.info(`Decomposed into ${subtasks.length} per-file tasks`);
1477
+ continue;
1478
+ }
1479
+
1480
+ // File conflict check: if task targets overlap with files in use, wait
1481
+ const taskTargets = task.targets ?? [];
1482
+ const hasConflict = taskTargets.some(f => filesInUse.has(f));
1483
+ if (hasConflict && running.size > 0) {
1484
+ await Promise.race(running.values());
1485
+ continue;
1486
+ }
1487
+
1488
+ taskCount++;
1489
+ if (opts.verbose) process.env.WEAVER_VERBOSE = '1';
1490
+ renderer.taskStart(taskCount, task.instruction ?? task.id);
1491
+
1492
+ await queue.markRunning(task.id);
1493
+ // Reserve files
1494
+ for (const f of taskTargets) filesInUse.add(f);
1495
+
1496
+ // Launch task (parallel or sequential based on parallelism setting)
1497
+ const promise = processTask(task);
1498
+ running.set(task.id, promise);
1499
+
1500
+ // In sequential mode (parallelism=1), await immediately
1501
+ if (parallelism <= 1) {
1502
+ await promise;
1503
+ }
1504
+ }
1505
+
1506
+ // Wait for all remaining parallel tasks
1507
+ if (running.size > 0) {
1508
+ await Promise.allSettled(running.values());
1509
+ }
1510
+
1511
+ const elapsed = Date.now() - sessionStartTime;
1512
+ renderer.sessionEnd({
1513
+ tasks: taskCount,
1514
+ completed: sessionCompleted,
1515
+ failed: sessionFailed,
1516
+ totalInputTokens: sessionInputTokens,
1517
+ totalOutputTokens: sessionOutputTokens,
1518
+ totalCost: sessionCost,
1519
+ elapsed,
1520
+ });
1521
+
1522
+ // Desktop notification on session end (cross-platform)
1523
+ if (taskCount > 0) {
1524
+ try {
1525
+ const { sendDesktopNotification } = await import('./bot/bot-manager.js');
1526
+ sendDesktopNotification('Weaver Session Complete', `${sessionCompleted} done, ${sessionFailed} failed, ${sessionNoOp} no-op`);
1527
+ } catch { /* non-fatal */ }
1241
1528
  }
1242
1529
  }
1243
1530
 
1531
+ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
1532
+ const projectDir = opts.file ?? process.cwd();
1533
+
1534
+ // Handle --list and --delete before creating provider
1535
+ if (opts.assistantList) {
1536
+ const { ConversationStore } = await import('./bot/conversation-store.js');
1537
+ const { formatTokens, formatElapsed } = await import('./bot/terminal-renderer.js');
1538
+ const store = new ConversationStore();
1539
+ const convos = store.list();
1540
+ if (convos.length === 0) {
1541
+ console.log(' No conversations yet.');
1542
+ return;
1543
+ }
1544
+ console.log(` Conversations (${convos.length}):\n`);
1545
+ for (const c of convos) {
1546
+ const ago = formatElapsed(Date.now() - c.lastMessageAt) + ' ago';
1547
+ const title = c.title ? `"${c.title}"` : '(untitled)';
1548
+ console.log(` ${c.id} ${title.padEnd(45)} ${String(c.messageCount).padStart(3)} msgs ${formatTokens(c.totalTokens).padStart(5)} tokens ${ago}`);
1549
+ }
1550
+ return;
1551
+ }
1552
+
1553
+ if (opts.assistantDelete) {
1554
+ const { ConversationStore } = await import('./bot/conversation-store.js');
1555
+ const store = new ConversationStore();
1556
+ store.delete(opts.assistantDelete);
1557
+ console.log(` Deleted conversation ${opts.assistantDelete}`);
1558
+ return;
1559
+ }
1560
+
1561
+ const config = await loadConfig(opts.configPath);
1562
+
1563
+ // Create provider
1564
+ const { createAnthropicProvider, createClaudeCliProvider } = await import('@synergenius/flow-weaver/agent');
1565
+ const providerSetting = config?.provider ?? 'auto';
1566
+ const providerType = typeof providerSetting === 'object' ? providerSetting.name : String(providerSetting);
1567
+
1568
+ let provider;
1569
+ if (providerType === 'anthropic' || (providerType === 'auto' && process.env.ANTHROPIC_API_KEY)) {
1570
+ const apiKey = process.env.ANTHROPIC_API_KEY ?? (typeof providerSetting === 'object' ? (providerSetting as { apiKey?: string }).apiKey : undefined);
1571
+ if (!apiKey) { console.error('ANTHROPIC_API_KEY required for anthropic provider'); process.exit(1); }
1572
+ provider = createAnthropicProvider({ apiKey, model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
1573
+ } else {
1574
+ provider = createClaudeCliProvider({ model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
1575
+ }
1576
+
1577
+ const { ASSISTANT_TOOLS, createAssistantExecutor } = await import('./bot/assistant-tools.js');
1578
+ const { runAssistant } = await import('./bot/assistant-core.js');
1579
+ const executor = createAssistantExecutor(projectDir);
1580
+
1581
+ await runAssistant({
1582
+ provider,
1583
+ tools: ASSISTANT_TOOLS,
1584
+ executor,
1585
+ projectDir,
1586
+ resumeId: opts.assistantResume,
1587
+ newConversation: opts.assistantNew,
1588
+ });
1589
+ }
1590
+
1244
1591
  export async function handleSteer(opts: ParsedArgs): Promise<void> {
1245
1592
  const { SteeringController } = await import('./bot/steering.js');
1246
1593
  const controller = new SteeringController();
@@ -1266,8 +1613,8 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1266
1613
  const queue = new TaskQueue();
1267
1614
 
1268
1615
  const action = opts.botTask;
1269
- if (!action || !['add', 'list', 'clear', 'remove'].includes(action)) {
1270
- console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove> [task|id]');
1616
+ if (!action || !['add', 'list', 'clear', 'remove', 'retry'].includes(action)) {
1617
+ console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove|retry> [task|id]');
1271
1618
  process.exit(1);
1272
1619
  }
1273
1620
 
@@ -1278,18 +1625,24 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1278
1625
  console.error('[weaver] Usage: flow-weaver weaver queue add "task instruction"');
1279
1626
  process.exit(1);
1280
1627
  }
1281
- const id = await queue.add({ instruction, priority: 0 });
1282
- console.log(`[weaver] Task added: ${id}`);
1628
+ const { id, duplicate } = await queue.add({ instruction, priority: 0 });
1629
+ if (duplicate) {
1630
+ console.log(`[weaver] Task already queued (${id}).`);
1631
+ } else {
1632
+ console.log(`[weaver] Task added: ${id}`);
1633
+ }
1283
1634
  break;
1284
1635
  }
1285
1636
  case 'list': {
1286
1637
  const tasks = await queue.list();
1287
- if (tasks.length === 0) {
1638
+ if (opts.historyJson) {
1639
+ console.log(JSON.stringify(tasks, null, 2));
1640
+ } else if (tasks.length === 0) {
1288
1641
  console.log('No tasks in queue.');
1289
1642
  } else {
1290
- console.log('ID'.padEnd(10) + 'STATUS'.padEnd(12) + 'INSTRUCTION');
1643
+ console.log('ID'.padEnd(10) + 'STATUS'.padEnd(12) + 'PRIORITY'.padEnd(10) + 'INSTRUCTION');
1291
1644
  for (const t of tasks) {
1292
- console.log(t.id.padEnd(10) + t.status.padEnd(12) + t.instruction.slice(0, 60));
1645
+ console.log(t.id.padEnd(10) + t.status.padEnd(12) + String(t.priority).padEnd(10) + t.instruction.slice(0, 60));
1293
1646
  }
1294
1647
  }
1295
1648
  break;
@@ -1309,7 +1662,63 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1309
1662
  console.log(removed ? `Removed task ${id}.` : `No task found with id "${id}".`);
1310
1663
  break;
1311
1664
  }
1665
+ case 'retry': {
1666
+ const id = opts.botFile;
1667
+ if (id) {
1668
+ // Retry a specific task
1669
+ const retried = await queue.retry(id);
1670
+ console.log(retried ? `Task ${id} reset to pending.` : `No failed/running task found with id "${id}".`);
1671
+ } else {
1672
+ // Retry all failed tasks
1673
+ const count = await queue.retryAll();
1674
+ console.log(`Reset ${count} failed task(s) to pending.`);
1675
+ }
1676
+ break;
1677
+ }
1678
+ }
1679
+ }
1680
+
1681
+ export async function handleStatus(opts: ParsedArgs): Promise<void> {
1682
+ const store = new RunStore();
1683
+ const { TaskQueue } = await import('./bot/task-queue.js');
1684
+ const queue = new TaskQueue();
1685
+
1686
+ const orphans = store.checkOrphans();
1687
+ const recentRuns = store.list({ limit: 5 });
1688
+ const tasks = await queue.list();
1689
+ const pending = tasks.filter(t => t.status === 'pending').length;
1690
+ const running = tasks.filter(t => t.status === 'running').length;
1691
+ const completed = tasks.filter(t => t.status === 'completed').length;
1692
+ const failed = tasks.filter(t => t.status === 'failed').length;
1693
+
1694
+ if (opts.historyJson) {
1695
+ console.log(JSON.stringify({
1696
+ queue: { pending, running, completed, failed, total: tasks.length },
1697
+ orphanedRuns: orphans.length,
1698
+ recentRuns: recentRuns.map(r => ({
1699
+ id: r.id, outcome: r.outcome, summary: r.summary,
1700
+ startedAt: r.startedAt, durationMs: r.durationMs,
1701
+ })),
1702
+ }, null, 2));
1703
+ return;
1704
+ }
1705
+
1706
+ console.log('\n\x1b[1mWeaver Status\x1b[0m\n');
1707
+ console.log(` Queue: ${pending} pending, ${running} running, ${completed} completed, ${failed} failed`);
1708
+ if (orphans.length > 0) {
1709
+ console.log(` \x1b[33mOrphaned runs: ${orphans.length} (recovered)\x1b[0m`);
1710
+ }
1711
+
1712
+ if (recentRuns.length > 0) {
1713
+ console.log(`\n Recent runs:`);
1714
+ for (const r of recentRuns) {
1715
+ const icon = r.success ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
1716
+ console.log(` ${icon} ${r.id.slice(0, 8)} ${r.outcome.padEnd(9)} ${formatDuration(r.durationMs).padEnd(8)} ${r.summary.slice(0, 60)}`);
1717
+ }
1718
+ } else {
1719
+ console.log('\n No recent runs.');
1312
1720
  }
1721
+ console.log('');
1313
1722
  }
1314
1723
 
1315
1724
  export async function handleGenesis(opts: ParsedArgs): Promise<void> {
@@ -1443,3 +1852,325 @@ function printAuditEvents(events: AuditEvent[], json: boolean): void {
1443
1852
  console.log(`${time} [${e.type}]${dataStr}`);
1444
1853
  }
1445
1854
  }
1855
+
1856
+ // --- Help ---
1857
+
1858
+ const COMMAND_HELP: Record<string, string> = {
1859
+ run: 'run <workflow.ts> Execute a workflow with AI agent channel',
1860
+ bot: 'bot "task description" Create or modify a workflow from a task',
1861
+ init: 'init Create a .weaver.json config file',
1862
+ history: 'history [id] [--limit N] Show execution history',
1863
+ costs: 'costs [--since 7d] Show AI token usage and costs',
1864
+ providers: 'providers List available AI providers',
1865
+ watch: 'watch <workflow.ts> Re-run on file changes',
1866
+ cron: 'cron "*/5 * * * *" <file> Schedule workflow execution',
1867
+ pipeline: 'pipeline <config.json> Run multi-stage pipeline',
1868
+ dashboard: 'dashboard <file> [--port N] Start live execution dashboard',
1869
+ session: 'session Start continuous task queue processing',
1870
+ queue: 'queue <add|list|clear> [task] Manage task queue',
1871
+ steer: 'steer <pause|resume|cancel> Control a running session',
1872
+ genesis: 'genesis [--init] [--watch] Run self-evolution cycle',
1873
+ eject: 'eject [--workflow bot|genesis] Export managed workflows',
1874
+ audit: 'audit [runId] [--limit N] View audit log',
1875
+ assistant: 'assistant AI-powered assistant for managing bots and workflows',
1876
+ };
1877
+
1878
+ export function printHelp(command?: string): void {
1879
+ if (command && command !== 'help' && COMMAND_HELP[command]) {
1880
+ console.log(`\nUsage: flow-weaver weaver ${COMMAND_HELP[command]}\n`);
1881
+ printCommandHelp(command);
1882
+ return;
1883
+ }
1884
+
1885
+ console.log(`
1886
+ Weaver — AI-powered workflow automation for Flow Weaver
1887
+
1888
+ Usage: flow-weaver weaver <command> [options]
1889
+
1890
+ Commands:
1891
+ ${Object.values(COMMAND_HELP).join('\n ')}
1892
+
1893
+ Global options:
1894
+ -h, --help Show help
1895
+ -v, --verbose Show detailed output
1896
+ -n, --dry-run Preview without executing
1897
+ --quiet Suppress output
1898
+ -p, --params <json> Input parameters as JSON
1899
+ -c, --config <path> Path to .weaver.json config
1900
+ --approval <mode> Override approval mode (auto|prompt|web|timeout-auto)
1901
+
1902
+ Quick start:
1903
+ flow-weaver weaver init # create .weaver.json
1904
+ flow-weaver weaver providers # check available AI providers
1905
+ flow-weaver weaver bot "Create a hello world workflow" # create your first workflow
1906
+ `);
1907
+ }
1908
+
1909
+ function printCommandHelp(command: string): void {
1910
+ const help: Record<string, string> = {
1911
+ run: `Options:
1912
+ --dashboard Enable live dashboard
1913
+ --port <n> Dashboard port (default: 4242)`,
1914
+ bot: `Options:
1915
+ --file <path> Target file for modification (switches to modify mode)
1916
+ --template <name> Use a template for scaffolding
1917
+ --batch <n> Process multiple tasks
1918
+ --auto-approve Skip approval gate
1919
+ --dashboard Enable live dashboard`,
1920
+ history: `Options:
1921
+ --limit <n> Number of records (default: 20)
1922
+ --outcome <type> Filter: completed, failed, error, skipped
1923
+ --workflow <path> Filter by workflow file
1924
+ --since <range> Time range: 7d, 2h, or ISO date
1925
+ --json Output as JSON
1926
+ --prune Remove old records
1927
+ --clear Delete all history`,
1928
+ costs: `Options:
1929
+ --since <range> Time range: 7d, 30d, or ISO date
1930
+ --model <name> Filter by model`,
1931
+ genesis: `Options:
1932
+ --init Initialize .genesis/config.json
1933
+ --watch Run multiple evolution cycles
1934
+ --project-dir <p> Project directory`,
1935
+ watch: `Options:
1936
+ --debounce <ms> Debounce interval (default: 500)
1937
+ --log <path> Log file for daemon output`,
1938
+ pipeline: `Options:
1939
+ --stage <id> Run a specific stage only`,
1940
+ queue: `Actions:
1941
+ add "task" Add task to queue
1942
+ list Show queue contents
1943
+ clear Remove all tasks
1944
+ remove <id> Remove a specific task`,
1945
+ steer: `Commands:
1946
+ pause Pause current execution
1947
+ resume Resume paused execution
1948
+ cancel Cancel current execution
1949
+ redirect "task" Switch to a different task
1950
+ queue "task" Add task to queue from steer`,
1951
+ };
1952
+
1953
+ if (help[command]) {
1954
+ console.log(help[command]);
1955
+ console.log('');
1956
+ }
1957
+ }
1958
+
1959
+ // --- Init ---
1960
+
1961
+ export async function handleInit(opts: ParsedArgs): Promise<void> {
1962
+ const dir = opts.file ? path.resolve(opts.file) : process.cwd();
1963
+ const configPath = path.join(dir, '.weaver.json');
1964
+
1965
+ if (fs.existsSync(configPath)) {
1966
+ console.log(`[weaver] Config already exists: ${configPath}`);
1967
+ console.log(' Edit it directly or delete it to regenerate.');
1968
+ return;
1969
+ }
1970
+
1971
+ // Detect best available provider (priority: Anthropic API > OpenAI API > Claude CLI > Copilot CLI)
1972
+ let provider: string = 'auto';
1973
+ const detected: string[] = [];
1974
+ try {
1975
+ if (process.env.ANTHROPIC_API_KEY) {
1976
+ provider = 'anthropic';
1977
+ detected.push('Anthropic API (ANTHROPIC_API_KEY)');
1978
+ }
1979
+ if (process.env.OPENAI_API_KEY) {
1980
+ if (provider === 'auto') provider = 'openai';
1981
+ detected.push('OpenAI API (OPENAI_API_KEY)');
1982
+ }
1983
+ const { execFileSync } = await import('node:child_process');
1984
+ try {
1985
+ execFileSync('claude', ['--version'], { stdio: 'pipe' });
1986
+ if (provider === 'auto') provider = 'claude-cli';
1987
+ detected.push('Claude CLI');
1988
+ } catch { /* not found */ }
1989
+ try {
1990
+ execFileSync('copilot', ['--version'], { stdio: 'pipe' });
1991
+ if (provider === 'auto') provider = 'copilot-cli';
1992
+ detected.push('Copilot CLI');
1993
+ } catch { /* not found */ }
1994
+ } catch {
1995
+ // Stay with auto
1996
+ }
1997
+
1998
+ const config = {
1999
+ provider: provider === 'auto' ? 'auto' : { name: provider },
2000
+ approval: 'auto',
2001
+ };
2002
+
2003
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
2004
+
2005
+ console.log('');
2006
+ console.log(' \x1b[1mWelcome to Weaver\x1b[0m — your AI workflow companion.');
2007
+ console.log('');
2008
+ console.log(' \x1b[2mDetecting providers...\x1b[0m');
2009
+ if (detected.length > 0) {
2010
+ for (const d of detected) console.log(` \x1b[32m✓\x1b[0m ${d}`);
2011
+ } else {
2012
+ console.log(' \x1b[33m⚠\x1b[0m No provider detected');
2013
+ console.log(' Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or install Claude CLI');
2014
+ }
2015
+ console.log('');
2016
+
2017
+ // Test connection if possible
2018
+ if (provider === 'claude-cli') {
2019
+ try {
2020
+ const { execFileSync: testExec } = await import('node:child_process');
2021
+ testExec('claude', ['--version'], { stdio: 'pipe', timeout: 5000 });
2022
+ console.log(' \x1b[2mTesting connection...\x1b[0m');
2023
+ console.log(' \x1b[32m✓\x1b[0m Claude CLI: connected');
2024
+ } catch {
2025
+ console.log(' \x1b[33m⚠\x1b[0m Could not verify Claude CLI connection');
2026
+ }
2027
+ }
2028
+
2029
+ console.log('');
2030
+ console.log(` \x1b[32m✓\x1b[0m Config written to .weaver.json`);
2031
+ console.log(` provider: ${provider}`);
2032
+ console.log(' approval: auto');
2033
+ console.log('');
2034
+ console.log(' Try it now:');
2035
+ console.log(' \x1b[36mweaver bot "Create a hello world workflow"\x1b[0m');
2036
+ console.log(' \x1b[36mweaver assistant\x1b[0m');
2037
+ console.log('');
2038
+ console.log(' Learn more:');
2039
+ console.log(' weaver examples \x1b[2m# see what weaver can do\x1b[0m');
2040
+ console.log(' weaver doctor \x1b[2m# validate your setup\x1b[0m');
2041
+ console.log('');
2042
+ }
2043
+
2044
+ export async function handleExamples(_opts: ParsedArgs): Promise<void> {
2045
+ console.log('');
2046
+ console.log(' \x1b[1mWeaver Examples\x1b[0m');
2047
+ console.log('');
2048
+ console.log(' \x1b[36mCreate workflows:\x1b[0m');
2049
+ console.log(' weaver bot "Create a workflow that validates user input and sends email"');
2050
+ console.log(' weaver bot "Create a data pipeline that reads CSV, transforms, and writes JSON"');
2051
+ console.log(' weaver bot "Create an AI agent with retry and fallback providers"');
2052
+ console.log(' weaver bot "Create a webhook handler that processes Stripe events"');
2053
+ console.log(' weaver bot "Create a RAG pipeline with document chunking and embedding"');
2054
+ console.log('');
2055
+ console.log(' \x1b[36mModify existing workflows:\x1b[0m');
2056
+ console.log(' weaver bot "Add error handling to my-workflow.ts" --file src/my-workflow.ts');
2057
+ console.log(' weaver bot "Add a validation step before the API call" --file src/pipeline.ts');
2058
+ console.log(' weaver bot "Make the retry node use exponential backoff" --file src/agent.ts');
2059
+ console.log('');
2060
+ console.log(' \x1b[36mInteractive assistant:\x1b[0m');
2061
+ console.log(' weaver assistant \x1b[2m# AI assistant with tools\x1b[0m');
2062
+ console.log(' weaver assistant --new \x1b[2m# fresh conversation\x1b[0m');
2063
+ console.log(' weaver assistant --list \x1b[2m# saved conversations\x1b[0m');
2064
+ console.log('');
2065
+ console.log(' \x1b[36mAutonomous mode:\x1b[0m');
2066
+ console.log(' weaver session --continuous \x1b[2m# process task queue\x1b[0m');
2067
+ console.log(' weaver queue add "Fix all validation errors"');
2068
+ console.log(' weaver session --continuous --until 10:00 --parallel 3');
2069
+ console.log('');
2070
+ console.log(' \x1b[36mInspect and debug:\x1b[0m');
2071
+ console.log(' flow-weaver validate src/*.ts \x1b[2m# check all workflows\x1b[0m');
2072
+ console.log(' flow-weaver diagram src/my-workflow.ts \x1b[2m# visual diagram\x1b[0m');
2073
+ console.log(' flow-weaver describe src/my-workflow.ts \x1b[2m# natural language description\x1b[0m');
2074
+ console.log('');
2075
+ console.log(' \x1b[36mCustomize the bot:\x1b[0m');
2076
+ console.log(' weaver eject \x1b[2m# get editable bot workflow\x1b[0m');
2077
+ console.log(' weaver eject --workflow bot \x1b[2m# eject specific workflow\x1b[0m');
2078
+ console.log('');
2079
+ }
2080
+
2081
+ export async function handleDoctor(opts: ParsedArgs): Promise<void> {
2082
+ const dir = opts.file ?? process.cwd();
2083
+ const checks: Array<{ label: string; status: 'ok' | 'warn' | 'fail'; detail: string }> = [];
2084
+
2085
+ // Config check
2086
+ const configPath = path.join(dir, '.weaver.json');
2087
+ if (fs.existsSync(configPath)) {
2088
+ checks.push({ label: 'Config', status: 'ok', detail: '.weaver.json found' });
2089
+ } else {
2090
+ checks.push({ label: 'Config', status: 'warn', detail: 'No .weaver.json — run "weaver init"' });
2091
+ }
2092
+
2093
+ // Provider check
2094
+ const config = await loadConfig(opts.configPath);
2095
+ const providerSetting = config?.provider ?? 'auto';
2096
+ const providerName = typeof providerSetting === 'object' ? providerSetting.name : String(providerSetting);
2097
+ let providerDetail = providerName;
2098
+
2099
+ if (providerName === 'anthropic' || process.env.ANTHROPIC_API_KEY) {
2100
+ providerDetail = 'anthropic (API key set)';
2101
+ checks.push({ label: 'Provider', status: 'ok', detail: providerDetail });
2102
+ } else {
2103
+ try {
2104
+ const { execFileSync: provCheck } = await import('node:child_process');
2105
+ provCheck('claude', ['--version'], { stdio: 'pipe', timeout: 5000 });
2106
+ providerDetail = 'claude-cli';
2107
+ checks.push({ label: 'Provider', status: 'ok', detail: providerDetail });
2108
+ } catch {
2109
+ checks.push({ label: 'Provider', status: 'fail', detail: 'No provider found — set ANTHROPIC_API_KEY or install Claude CLI' });
2110
+ }
2111
+ }
2112
+
2113
+ // Connection test
2114
+ if (providerName === 'claude-cli' || (!process.env.ANTHROPIC_API_KEY && providerName === 'auto')) {
2115
+ try {
2116
+ const { execFileSync: connCheck } = await import('node:child_process');
2117
+ const start = Date.now();
2118
+ connCheck('claude', ['-p', '--max-turns', '1', 'say ok'], { stdio: 'pipe', timeout: 15000 });
2119
+ checks.push({ label: 'Connection', status: 'ok', detail: `OK (${((Date.now() - start) / 1000).toFixed(1)}s)` });
2120
+ } catch {
2121
+ checks.push({ label: 'Connection', status: 'warn', detail: 'Could not verify (Claude CLI may need auth)' });
2122
+ }
2123
+ } else if (process.env.ANTHROPIC_API_KEY) {
2124
+ checks.push({ label: 'Connection', status: 'ok', detail: 'API key configured' });
2125
+ } else {
2126
+ checks.push({ label: 'Connection', status: 'warn', detail: 'Not tested' });
2127
+ }
2128
+
2129
+ // Flow Weaver version
2130
+ try {
2131
+ const { execFileSync: fwCheck } = await import('node:child_process');
2132
+ const version = fwCheck('npx', ['flow-weaver', '--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000, cwd: dir }).trim();
2133
+ checks.push({ label: 'Flow Weaver', status: 'ok', detail: version });
2134
+ } catch {
2135
+ checks.push({ label: 'Flow Weaver', status: 'fail', detail: 'Not installed — run "npm install @synergenius/flow-weaver"' });
2136
+ }
2137
+
2138
+ // Queue status
2139
+ try {
2140
+ const { TaskQueue } = await import('./bot/task-queue.js');
2141
+ process.env.WEAVER_PROJECT_DIR = dir;
2142
+ const queue = new TaskQueue();
2143
+ const tasks = await queue.list();
2144
+ const pending = tasks.filter(t => t.status === 'pending').length;
2145
+ const running = tasks.filter(t => t.status === 'running').length;
2146
+ checks.push({ label: 'Queue', status: 'ok', detail: `${pending} pending, ${running} running` });
2147
+ } catch {
2148
+ checks.push({ label: 'Queue', status: 'ok', detail: 'Empty (no queue file)' });
2149
+ }
2150
+
2151
+ // Plan file
2152
+ const planPath = path.join(dir, '.weaver-plan.md');
2153
+ if (fs.existsSync(planPath)) {
2154
+ checks.push({ label: 'Plan', status: 'ok', detail: '.weaver-plan.md found' });
2155
+ } else {
2156
+ checks.push({ label: 'Plan', status: 'warn', detail: 'No .weaver-plan.md — optional, guides bot behavior' });
2157
+ }
2158
+
2159
+ // Output
2160
+ console.log('');
2161
+ console.log(' \x1b[1mWeaver Doctor\x1b[0m');
2162
+ console.log('');
2163
+ const icons = { ok: '\x1b[32m✓\x1b[0m', warn: '\x1b[33m⚠\x1b[0m', fail: '\x1b[31m✗\x1b[0m' };
2164
+ for (const check of checks) {
2165
+ console.log(` ${icons[check.status]} ${check.label.padEnd(14)} ${check.detail}`);
2166
+ }
2167
+ console.log('');
2168
+
2169
+ const failures = checks.filter(c => c.status === 'fail');
2170
+ if (failures.length > 0) {
2171
+ console.log(` \x1b[31m${failures.length} issue(s) found.\x1b[0m`);
2172
+ } else {
2173
+ console.log(' \x1b[32mAll checks passed.\x1b[0m');
2174
+ }
2175
+ console.log('');
2176
+ }