@synergenius/flow-weaver-pack-weaver 0.9.0 → 0.9.4

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 (235) 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/ansi.d.ts +13 -0
  6. package/dist/bot/ansi.d.ts.map +1 -0
  7. package/dist/bot/ansi.js +13 -0
  8. package/dist/bot/ansi.js.map +1 -0
  9. package/dist/bot/assistant-core.d.ts +25 -0
  10. package/dist/bot/assistant-core.d.ts.map +1 -0
  11. package/dist/bot/assistant-core.js +272 -0
  12. package/dist/bot/assistant-core.js.map +1 -0
  13. package/dist/bot/assistant-tools.d.ts +10 -0
  14. package/dist/bot/assistant-tools.d.ts.map +1 -0
  15. package/dist/bot/assistant-tools.js +324 -0
  16. package/dist/bot/assistant-tools.js.map +1 -0
  17. package/dist/bot/audit-logger.d.ts.map +1 -1
  18. package/dist/bot/audit-logger.js +9 -5
  19. package/dist/bot/audit-logger.js.map +1 -1
  20. package/dist/bot/bot-manager.d.ts +49 -0
  21. package/dist/bot/bot-manager.d.ts.map +1 -0
  22. package/dist/bot/bot-manager.js +279 -0
  23. package/dist/bot/bot-manager.js.map +1 -0
  24. package/dist/bot/child-process-tracker.d.ts +6 -0
  25. package/dist/bot/child-process-tracker.d.ts.map +1 -0
  26. package/dist/bot/child-process-tracker.js +35 -0
  27. package/dist/bot/child-process-tracker.js.map +1 -0
  28. package/dist/bot/cli-provider.d.ts.map +1 -1
  29. package/dist/bot/cli-provider.js +13 -8
  30. package/dist/bot/cli-provider.js.map +1 -1
  31. package/dist/bot/conversation-store.d.ts +40 -0
  32. package/dist/bot/conversation-store.d.ts.map +1 -0
  33. package/dist/bot/conversation-store.js +182 -0
  34. package/dist/bot/conversation-store.js.map +1 -0
  35. package/dist/bot/error-classifier.d.ts +27 -0
  36. package/dist/bot/error-classifier.d.ts.map +1 -0
  37. package/dist/bot/error-classifier.js +71 -0
  38. package/dist/bot/error-classifier.js.map +1 -0
  39. package/dist/bot/error-guide.d.ts +5 -0
  40. package/dist/bot/error-guide.d.ts.map +1 -0
  41. package/dist/bot/error-guide.js +5 -0
  42. package/dist/bot/error-guide.js.map +1 -0
  43. package/dist/bot/knowledge-store.d.ts +17 -0
  44. package/dist/bot/knowledge-store.d.ts.map +1 -0
  45. package/dist/bot/knowledge-store.js +53 -0
  46. package/dist/bot/knowledge-store.js.map +1 -0
  47. package/dist/bot/paths.d.ts +11 -0
  48. package/dist/bot/paths.d.ts.map +1 -0
  49. package/dist/bot/paths.js +26 -0
  50. package/dist/bot/paths.js.map +1 -0
  51. package/dist/bot/retry-utils.d.ts +5 -0
  52. package/dist/bot/retry-utils.d.ts.map +1 -0
  53. package/dist/bot/retry-utils.js +5 -0
  54. package/dist/bot/retry-utils.js.map +1 -0
  55. package/dist/bot/runner.d.ts.map +1 -1
  56. package/dist/bot/runner.js +12 -1
  57. package/dist/bot/runner.js.map +1 -1
  58. package/dist/bot/safety.d.ts +10 -0
  59. package/dist/bot/safety.d.ts.map +1 -0
  60. package/dist/bot/safety.js +14 -0
  61. package/dist/bot/safety.js.map +1 -0
  62. package/dist/bot/session-state.d.ts.map +1 -1
  63. package/dist/bot/session-state.js +3 -1
  64. package/dist/bot/session-state.js.map +1 -1
  65. package/dist/bot/steering.js +2 -2
  66. package/dist/bot/steering.js.map +1 -1
  67. package/dist/bot/step-executor.d.ts +10 -5
  68. package/dist/bot/step-executor.d.ts.map +1 -1
  69. package/dist/bot/step-executor.js +252 -3
  70. package/dist/bot/step-executor.js.map +1 -1
  71. package/dist/bot/system-prompt.d.ts +1 -1
  72. package/dist/bot/system-prompt.d.ts.map +1 -1
  73. package/dist/bot/system-prompt.js +69 -43
  74. package/dist/bot/system-prompt.js.map +1 -1
  75. package/dist/bot/task-decomposer.d.ts +24 -0
  76. package/dist/bot/task-decomposer.d.ts.map +1 -0
  77. package/dist/bot/task-decomposer.js +75 -0
  78. package/dist/bot/task-decomposer.js.map +1 -0
  79. package/dist/bot/task-queue.d.ts +17 -4
  80. package/dist/bot/task-queue.d.ts.map +1 -1
  81. package/dist/bot/task-queue.js +83 -5
  82. package/dist/bot/task-queue.js.map +1 -1
  83. package/dist/bot/terminal-renderer.d.ts +60 -0
  84. package/dist/bot/terminal-renderer.d.ts.map +1 -0
  85. package/dist/bot/terminal-renderer.js +204 -0
  86. package/dist/bot/terminal-renderer.js.map +1 -0
  87. package/dist/bot/tool-registry.d.ts +24 -0
  88. package/dist/bot/tool-registry.d.ts.map +1 -0
  89. package/dist/bot/tool-registry.js +458 -0
  90. package/dist/bot/tool-registry.js.map +1 -0
  91. package/dist/bot/types.d.ts +7 -0
  92. package/dist/bot/types.d.ts.map +1 -1
  93. package/dist/bot/weaver-tools.d.ts +18 -0
  94. package/dist/bot/weaver-tools.d.ts.map +1 -0
  95. package/dist/bot/weaver-tools.js +124 -0
  96. package/dist/bot/weaver-tools.js.map +1 -0
  97. package/dist/cli-bridge.d.ts.map +1 -1
  98. package/dist/cli-bridge.js +5 -1
  99. package/dist/cli-bridge.js.map +1 -1
  100. package/dist/cli-handlers.d.ts +13 -1
  101. package/dist/cli-handlers.d.ts.map +1 -1
  102. package/dist/cli-handlers.js +615 -48
  103. package/dist/cli-handlers.js.map +1 -1
  104. package/dist/mcp-tools.js +2 -2
  105. package/dist/mcp-tools.js.map +1 -1
  106. package/dist/node-types/abort-task.d.ts.map +1 -1
  107. package/dist/node-types/abort-task.js +4 -3
  108. package/dist/node-types/abort-task.js.map +1 -1
  109. package/dist/node-types/agent-execute.d.ts +38 -0
  110. package/dist/node-types/agent-execute.d.ts.map +1 -0
  111. package/dist/node-types/agent-execute.js +252 -0
  112. package/dist/node-types/agent-execute.js.map +1 -0
  113. package/dist/node-types/bot-report.d.ts +5 -3
  114. package/dist/node-types/bot-report.d.ts.map +1 -1
  115. package/dist/node-types/bot-report.js +39 -7
  116. package/dist/node-types/bot-report.js.map +1 -1
  117. package/dist/node-types/build-context.d.ts +3 -3
  118. package/dist/node-types/build-context.d.ts.map +1 -1
  119. package/dist/node-types/build-context.js +108 -24
  120. package/dist/node-types/build-context.js.map +1 -1
  121. package/dist/node-types/detect-provider.d.ts +2 -2
  122. package/dist/node-types/detect-provider.d.ts.map +1 -1
  123. package/dist/node-types/detect-provider.js +3 -1
  124. package/dist/node-types/detect-provider.js.map +1 -1
  125. package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
  126. package/dist/node-types/exec-validate-retry.js +43 -6
  127. package/dist/node-types/exec-validate-retry.js.map +1 -1
  128. package/dist/node-types/execute-plan.d.ts.map +1 -1
  129. package/dist/node-types/execute-plan.js +31 -8
  130. package/dist/node-types/execute-plan.js.map +1 -1
  131. package/dist/node-types/execute-target.d.ts.map +1 -1
  132. package/dist/node-types/execute-target.js +3 -1
  133. package/dist/node-types/execute-target.js.map +1 -1
  134. package/dist/node-types/fix-errors.d.ts.map +1 -1
  135. package/dist/node-types/fix-errors.js +21 -5
  136. package/dist/node-types/fix-errors.js.map +1 -1
  137. package/dist/node-types/genesis-observe.d.ts.map +1 -1
  138. package/dist/node-types/genesis-observe.js +3 -1
  139. package/dist/node-types/genesis-observe.js.map +1 -1
  140. package/dist/node-types/genesis-report.js +4 -1
  141. package/dist/node-types/genesis-report.js.map +1 -1
  142. package/dist/node-types/git-ops.d.ts.map +1 -1
  143. package/dist/node-types/git-ops.js +98 -4
  144. package/dist/node-types/git-ops.js.map +1 -1
  145. package/dist/node-types/index.d.ts +2 -0
  146. package/dist/node-types/index.d.ts.map +1 -1
  147. package/dist/node-types/index.js +2 -0
  148. package/dist/node-types/index.js.map +1 -1
  149. package/dist/node-types/load-config.d.ts +2 -2
  150. package/dist/node-types/load-config.d.ts.map +1 -1
  151. package/dist/node-types/load-config.js.map +1 -1
  152. package/dist/node-types/plan-task.d.ts.map +1 -1
  153. package/dist/node-types/plan-task.js +14 -2
  154. package/dist/node-types/plan-task.js.map +1 -1
  155. package/dist/node-types/read-workflow.js +8 -2
  156. package/dist/node-types/read-workflow.js.map +1 -1
  157. package/dist/node-types/receive-task.d.ts.map +1 -1
  158. package/dist/node-types/receive-task.js +35 -26
  159. package/dist/node-types/receive-task.js.map +1 -1
  160. package/dist/node-types/send-notify.js +2 -1
  161. package/dist/node-types/send-notify.js.map +1 -1
  162. package/dist/node-types/validate-gate.d.ts +18 -0
  163. package/dist/node-types/validate-gate.d.ts.map +1 -0
  164. package/dist/node-types/validate-gate.js +96 -0
  165. package/dist/node-types/validate-gate.js.map +1 -0
  166. package/dist/workflows/genesis-task.d.ts +20 -12
  167. package/dist/workflows/genesis-task.d.ts.map +1 -1
  168. package/dist/workflows/genesis-task.js +20 -12
  169. package/dist/workflows/genesis-task.js.map +1 -1
  170. package/dist/workflows/weaver-agent.d.ts +35 -0
  171. package/dist/workflows/weaver-agent.d.ts.map +1 -0
  172. package/dist/workflows/weaver-agent.js +777 -0
  173. package/dist/workflows/weaver-agent.js.map +1 -0
  174. package/dist/workflows/weaver-bot-batch.d.ts +19 -26
  175. package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
  176. package/dist/workflows/weaver-bot-batch.js +1043 -27
  177. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  178. package/dist/workflows/weaver-bot.d.ts +21 -35
  179. package/dist/workflows/weaver-bot.d.ts.map +1 -1
  180. package/dist/workflows/weaver-bot.js +1119 -36
  181. package/dist/workflows/weaver-bot.js.map +1 -1
  182. package/flowweaver.manifest.json +21 -1
  183. package/package.json +5 -2
  184. package/src/bot/ai-client.ts +180 -19
  185. package/src/bot/ansi.ts +12 -0
  186. package/src/bot/assistant-core.ts +312 -0
  187. package/src/bot/assistant-tools.ts +318 -0
  188. package/src/bot/audit-logger.ts +6 -5
  189. package/src/bot/bot-manager.ts +293 -0
  190. package/src/bot/child-process-tracker.ts +40 -0
  191. package/src/bot/cli-provider.ts +13 -8
  192. package/src/bot/conversation-store.ts +222 -0
  193. package/src/bot/error-classifier.ts +90 -0
  194. package/src/bot/error-guide.ts +4 -0
  195. package/src/bot/knowledge-store.ts +59 -0
  196. package/src/bot/paths.ts +27 -0
  197. package/src/bot/retry-utils.ts +4 -0
  198. package/src/bot/runner.ts +12 -1
  199. package/src/bot/safety.ts +16 -0
  200. package/src/bot/session-state.ts +2 -1
  201. package/src/bot/steering.ts +2 -2
  202. package/src/bot/step-executor.ts +313 -5
  203. package/src/bot/system-prompt.ts +70 -47
  204. package/src/bot/task-decomposer.ts +100 -0
  205. package/src/bot/task-queue.ts +100 -8
  206. package/src/bot/terminal-renderer.ts +238 -0
  207. package/src/bot/tool-registry.ts +477 -0
  208. package/src/bot/types.ts +8 -0
  209. package/src/bot/weaver-tools.ts +134 -0
  210. package/src/cli-bridge.ts +7 -1
  211. package/src/cli-handlers.ts +624 -48
  212. package/src/mcp-tools.ts +2 -2
  213. package/src/node-types/abort-task.ts +5 -4
  214. package/src/node-types/agent-execute.ts +303 -0
  215. package/src/node-types/bot-report.ts +40 -9
  216. package/src/node-types/build-context.ts +112 -25
  217. package/src/node-types/detect-provider.ts +4 -3
  218. package/src/node-types/exec-validate-retry.ts +47 -8
  219. package/src/node-types/execute-plan.ts +32 -8
  220. package/src/node-types/execute-target.ts +2 -1
  221. package/src/node-types/fix-errors.ts +20 -5
  222. package/src/node-types/genesis-observe.ts +2 -1
  223. package/src/node-types/genesis-report.ts +1 -1
  224. package/src/node-types/git-ops.ts +93 -4
  225. package/src/node-types/index.ts +2 -0
  226. package/src/node-types/load-config.ts +3 -3
  227. package/src/node-types/plan-task.ts +15 -3
  228. package/src/node-types/read-workflow.ts +2 -2
  229. package/src/node-types/receive-task.ts +31 -26
  230. package/src/node-types/send-notify.ts +1 -1
  231. package/src/node-types/validate-gate.ts +112 -0
  232. package/src/workflows/genesis-task.ts +20 -12
  233. package/src/workflows/weaver-agent.ts +799 -0
  234. package/src/workflows/weaver-bot-batch.ts +1049 -27
  235. 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' | 'init';
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
@@ -242,6 +261,27 @@ export function parseArgs(argv: string[]): ParsedArgs {
242
261
  result.genesisInit = true;
243
262
  } else if (arg === '--watch') {
244
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);
245
285
  } else if (arg === '--project-dir' && i + 1 < args.length) {
246
286
  i++;
247
287
  result.file = args[i];
@@ -339,6 +379,14 @@ function printRunDetail(r: RunRecord): void {
339
379
  if (r.params) {
340
380
  console.log(` Params: ${JSON.stringify(r.params)}`);
341
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
+ }
342
390
  console.log('');
343
391
  }
344
392
 
@@ -450,11 +498,19 @@ export async function handleCosts(opts: ParsedArgs): Promise<void> {
450
498
  const summary = store.summarize({ since: sinceTs, model: opts.costsModel });
451
499
 
452
500
  if (summary.totalRuns === 0) {
453
- 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
+ }
454
506
  return;
455
507
  }
456
508
 
457
- console.log(formatCostTable(summary));
509
+ if (opts.historyJson) {
510
+ console.log(JSON.stringify(summary, null, 2));
511
+ } else {
512
+ console.log(formatCostTable(summary));
513
+ }
458
514
  }
459
515
 
460
516
  export async function handleWatch(opts: ParsedArgs): Promise<void> {
@@ -706,6 +762,7 @@ export async function handleProviders(): Promise<void> {
706
762
 
707
763
  const MANAGED_WORKFLOWS: Record<string, string> = {
708
764
  bot: 'weaver-bot',
765
+ agent: 'weaver-agent',
709
766
  batch: 'weaver-bot-batch',
710
767
  genesis: 'genesis-task',
711
768
  };
@@ -1213,34 +1270,321 @@ export async function handleBot(opts: ParsedArgs): Promise<void> {
1213
1270
  }
1214
1271
 
1215
1272
  export async function handleSession(opts: ParsedArgs): Promise<void> {
1216
- const workflowPath = resolveWorkflowPath('bot', opts.file ?? process.cwd());
1217
-
1273
+ const projectDir = opts.file ?? process.cwd();
1274
+ // Set project dir for per-project queue isolation
1275
+ process.env.WEAVER_PROJECT_DIR = projectDir;
1218
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
+ }
1219
1297
 
1220
- if (!opts.quiet) {
1221
- console.log('[weaver] Starting bot session (Ctrl+C to stop)');
1222
- 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)`);
1223
1308
  }
1224
1309
 
1310
+ // Clean stale cache files
1225
1311
  try {
1226
- const result = await runWorkflow(workflowPath, {
1227
- params: { projectDir: opts.file ?? process.cwd() },
1228
- verbose: opts.verbose,
1229
- dryRun: opts.dryRun,
1230
- config,
1231
- });
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 */ }
1232
1319
 
1233
- if (!opts.quiet) {
1234
- const color = result.success ? '\x1b[32m' : '\x1b[31m';
1235
- 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);
1236
1344
  }
1345
+ return;
1346
+ }
1237
1347
 
1238
- process.exit(result.success ? 0 : 1);
1239
- } catch (err: unknown) {
1240
- const msg = err instanceof Error ? err.message : String(err);
1241
- console.error(`\x1b[31m[weaver] Fatal: ${msg}\x1b[0m`);
1242
- process.exit(1);
1348
+ // Continuous mode: loop until deadline/maxTasks/interrupt
1349
+ const { TaskQueue } = await import('./bot/task-queue.js');
1350
+ const { isTransientError, getErrorGuidance } = await import('./bot/error-classifier.js');
1351
+ const queue = new TaskQueue();
1352
+ let taskCount = 0;
1353
+ let interrupted = false;
1354
+ let consecutiveErrors = 0;
1355
+ let consecutiveNoOps = 0;
1356
+ const MAX_CONSECUTIVE_ERRORS = 3;
1357
+ const MAX_CONSECUTIVE_NO_OPS = 5;
1358
+
1359
+ // Session stats
1360
+ let sessionCompleted = 0, sessionFailed = 0, sessionNoOp = 0;
1361
+ let sessionInputTokens = 0, sessionOutputTokens = 0, sessionCost = 0;
1362
+
1363
+ process.on('SIGINT', () => { interrupted = true; });
1364
+ process.on('SIGTERM', () => { interrupted = true; });
1365
+
1366
+ // Parallel task tracking
1367
+ const running = new Map<string, Promise<void>>();
1368
+ const filesInUse = new Set<string>();
1369
+
1370
+ const processTask = async (task: { id: string; instruction?: string; targets?: string[] }) => {
1371
+ try {
1372
+ const result = await runWorkflow(workflowPath, {
1373
+ params: { projectDir, taskJson: JSON.stringify(task) },
1374
+ verbose: opts.verbose,
1375
+ dryRun: opts.dryRun,
1376
+ config,
1377
+ });
1378
+
1379
+ // Classify outcome: the summary contains "no changes" or "0 files" for no-ops
1380
+ const isNoOp = result.success && (
1381
+ result.summary.includes('no changes') ||
1382
+ result.summary.includes('0 file') ||
1383
+ result.summary.includes("doesn't exist") ||
1384
+ result.summary.includes('does not exist') ||
1385
+ result.summary.includes('nothing to') ||
1386
+ result.outcome === 'no-op'
1387
+ );
1388
+
1389
+ if (isNoOp) {
1390
+ await queue.markNoOp(task.id);
1391
+ sessionNoOp++;
1392
+ consecutiveNoOps++;
1393
+ consecutiveErrors = 0;
1394
+ } else if (result.success) {
1395
+ await queue.markComplete(task.id);
1396
+ sessionCompleted++;
1397
+ consecutiveErrors = 0;
1398
+ consecutiveNoOps = 0;
1399
+ } else {
1400
+ await queue.markFailed(task.id, result.summary || result.outcome || 'unknown error');
1401
+ sessionFailed++;
1402
+ consecutiveErrors++;
1403
+ consecutiveNoOps = 0;
1404
+ }
1405
+
1406
+ // Track cost from workflow result
1407
+ if (result.cost) {
1408
+ sessionInputTokens += result.cost.totalInputTokens ?? 0;
1409
+ sessionOutputTokens += result.cost.totalOutputTokens ?? 0;
1410
+ sessionCost += result.cost.totalCost ?? 0;
1411
+ }
1412
+
1413
+ } catch (err: unknown) {
1414
+ const msg = err instanceof Error ? err.message : String(err);
1415
+ const guidance = getErrorGuidance(msg);
1416
+ renderer.error(`Task ${task.id.slice(0, 8)} error`, guidance ? `${msg}\n Hint: ${guidance}` : msg);
1417
+ await queue.markFailed(task.id, msg);
1418
+ sessionFailed++;
1419
+ if (!isTransientError(err)) {
1420
+ consecutiveErrors++;
1421
+ }
1422
+ } finally {
1423
+ for (const f of task.targets ?? []) filesInUse.delete(f);
1424
+ running.delete(task.id);
1425
+ }
1426
+ };
1427
+
1428
+ while (taskCount < maxTasks && !interrupted) {
1429
+ if (deadline && Date.now() >= deadline) {
1430
+ renderer.info('Deadline reached, stopping session.');
1431
+ break;
1432
+ }
1433
+
1434
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1435
+ renderer.error('Session stopped', `${MAX_CONSECUTIVE_ERRORS} consecutive errors — check your API key or provider config.`);
1436
+ break;
1437
+ }
1438
+
1439
+ // Pause on consecutive no-ops (bot is spinning without doing anything)
1440
+ if (consecutiveNoOps >= MAX_CONSECUTIVE_NO_OPS) {
1441
+ renderer.warn(`${MAX_CONSECUTIVE_NO_OPS} consecutive no-op tasks — pausing 60s`);
1442
+ await new Promise(r => setTimeout(r, 60_000));
1443
+ consecutiveNoOps = 0;
1444
+ }
1445
+
1446
+ // Wait if at capacity
1447
+ if (running.size >= parallelism) {
1448
+ await Promise.race(running.values());
1449
+ continue;
1450
+ }
1451
+
1452
+ const task = await queue.next();
1453
+ if (!task) {
1454
+ if (running.size > 0) {
1455
+ // Tasks still running — wait for one to finish
1456
+ await Promise.race(running.values());
1457
+ continue;
1458
+ }
1459
+ // No pending or running tasks — wait and retry
1460
+ await new Promise(r => setTimeout(r, 5_000));
1461
+ continue;
1462
+ }
1463
+
1464
+ // Auto-decompose broad tasks into per-file tasks
1465
+ const { decomposeTask } = await import('./bot/task-decomposer.js');
1466
+ const { decomposed, tasks: subtasks } = decomposeTask(task, projectDir);
1467
+ if (decomposed && subtasks.length > 1) {
1468
+ // Replace the broad task with per-file tasks
1469
+ await queue.markComplete(task.id);
1470
+ let decomposedCount = 0;
1471
+ for (const st of subtasks) {
1472
+ const { duplicate } = await queue.add({ instruction: st.instruction, mode: st.mode as 'modify', targets: st.targets, priority: st.priority ?? 0 });
1473
+ if (!duplicate) decomposedCount++;
1474
+ }
1475
+ renderer.info(`Decomposed into ${subtasks.length} per-file tasks`);
1476
+ continue;
1477
+ }
1478
+
1479
+ // File conflict check: if task targets overlap with files in use, wait
1480
+ const taskTargets = task.targets ?? [];
1481
+ const hasConflict = taskTargets.some(f => filesInUse.has(f));
1482
+ if (hasConflict && running.size > 0) {
1483
+ await Promise.race(running.values());
1484
+ continue;
1485
+ }
1486
+
1487
+ taskCount++;
1488
+ if (opts.verbose) process.env.WEAVER_VERBOSE = '1';
1489
+ renderer.taskStart(taskCount, task.instruction ?? task.id);
1490
+
1491
+ await queue.markRunning(task.id);
1492
+ // Reserve files
1493
+ for (const f of taskTargets) filesInUse.add(f);
1494
+
1495
+ // Launch task (parallel or sequential based on parallelism setting)
1496
+ const promise = processTask(task);
1497
+ running.set(task.id, promise);
1498
+
1499
+ // In sequential mode (parallelism=1), await immediately
1500
+ if (parallelism <= 1) {
1501
+ await promise;
1502
+ }
1503
+ }
1504
+
1505
+ // Wait for all remaining parallel tasks
1506
+ if (running.size > 0) {
1507
+ await Promise.allSettled(running.values());
1508
+ }
1509
+
1510
+ const elapsed = Date.now() - sessionStartTime;
1511
+ renderer.sessionEnd({
1512
+ tasks: taskCount,
1513
+ completed: sessionCompleted,
1514
+ failed: sessionFailed,
1515
+ totalInputTokens: sessionInputTokens,
1516
+ totalOutputTokens: sessionOutputTokens,
1517
+ totalCost: sessionCost,
1518
+ elapsed,
1519
+ });
1520
+
1521
+ // Desktop notification on session end (cross-platform)
1522
+ if (taskCount > 0) {
1523
+ try {
1524
+ const { sendDesktopNotification } = await import('./bot/bot-manager.js');
1525
+ sendDesktopNotification('Weaver Session Complete', `${sessionCompleted} done, ${sessionFailed} failed, ${sessionNoOp} no-op`);
1526
+ } catch { /* non-fatal */ }
1527
+ }
1528
+ }
1529
+
1530
+ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
1531
+ const projectDir = opts.file ?? process.cwd();
1532
+
1533
+ // Handle --list and --delete before creating provider
1534
+ if (opts.assistantList) {
1535
+ const { ConversationStore } = await import('./bot/conversation-store.js');
1536
+ const { formatTokens, formatElapsed } = await import('./bot/terminal-renderer.js');
1537
+ const store = new ConversationStore();
1538
+ const convos = store.list();
1539
+ if (convos.length === 0) {
1540
+ console.log(' No conversations yet.');
1541
+ return;
1542
+ }
1543
+ console.log(` Conversations (${convos.length}):\n`);
1544
+ for (const c of convos) {
1545
+ const ago = formatElapsed(Date.now() - c.lastMessageAt) + ' ago';
1546
+ const title = c.title ? `"${c.title}"` : '(untitled)';
1547
+ console.log(` ${c.id} ${title.padEnd(45)} ${String(c.messageCount).padStart(3)} msgs ${formatTokens(c.totalTokens).padStart(5)} tokens ${ago}`);
1548
+ }
1549
+ return;
1550
+ }
1551
+
1552
+ if (opts.assistantDelete) {
1553
+ const { ConversationStore } = await import('./bot/conversation-store.js');
1554
+ const store = new ConversationStore();
1555
+ store.delete(opts.assistantDelete);
1556
+ console.log(` Deleted conversation ${opts.assistantDelete}`);
1557
+ return;
1558
+ }
1559
+
1560
+ const config = await loadConfig(opts.configPath);
1561
+
1562
+ // Create provider
1563
+ const { createAnthropicProvider, createClaudeCliProvider } = await import('@synergenius/flow-weaver/agent');
1564
+ const providerSetting = config?.provider ?? 'auto';
1565
+ const providerType = typeof providerSetting === 'object' ? providerSetting.name : String(providerSetting);
1566
+
1567
+ let provider;
1568
+ if (providerType === 'anthropic' || (providerType === 'auto' && process.env.ANTHROPIC_API_KEY)) {
1569
+ const apiKey = process.env.ANTHROPIC_API_KEY ?? (typeof providerSetting === 'object' ? (providerSetting as { apiKey?: string }).apiKey : undefined);
1570
+ if (!apiKey) { console.error('ANTHROPIC_API_KEY required for anthropic provider'); process.exit(1); }
1571
+ provider = createAnthropicProvider({ apiKey, model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
1572
+ } else {
1573
+ provider = createClaudeCliProvider({ model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
1243
1574
  }
1575
+
1576
+ const { ASSISTANT_TOOLS, createAssistantExecutor } = await import('./bot/assistant-tools.js');
1577
+ const { runAssistant } = await import('./bot/assistant-core.js');
1578
+ const executor = createAssistantExecutor(projectDir);
1579
+
1580
+ await runAssistant({
1581
+ provider,
1582
+ tools: ASSISTANT_TOOLS,
1583
+ executor,
1584
+ projectDir,
1585
+ resumeId: opts.assistantResume,
1586
+ newConversation: opts.assistantNew,
1587
+ });
1244
1588
  }
1245
1589
 
1246
1590
  export async function handleSteer(opts: ParsedArgs): Promise<void> {
@@ -1268,8 +1612,8 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1268
1612
  const queue = new TaskQueue();
1269
1613
 
1270
1614
  const action = opts.botTask;
1271
- if (!action || !['add', 'list', 'clear', 'remove'].includes(action)) {
1272
- console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove> [task|id]');
1615
+ if (!action || !['add', 'list', 'clear', 'remove', 'retry'].includes(action)) {
1616
+ console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove|retry> [task|id]');
1273
1617
  process.exit(1);
1274
1618
  }
1275
1619
 
@@ -1280,18 +1624,24 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1280
1624
  console.error('[weaver] Usage: flow-weaver weaver queue add "task instruction"');
1281
1625
  process.exit(1);
1282
1626
  }
1283
- const id = await queue.add({ instruction, priority: 0 });
1284
- console.log(`[weaver] Task added: ${id}`);
1627
+ const { id, duplicate } = await queue.add({ instruction, priority: 0 });
1628
+ if (duplicate) {
1629
+ console.log(`[weaver] Task already queued (${id}).`);
1630
+ } else {
1631
+ console.log(`[weaver] Task added: ${id}`);
1632
+ }
1285
1633
  break;
1286
1634
  }
1287
1635
  case 'list': {
1288
1636
  const tasks = await queue.list();
1289
- if (tasks.length === 0) {
1637
+ if (opts.historyJson) {
1638
+ console.log(JSON.stringify(tasks, null, 2));
1639
+ } else if (tasks.length === 0) {
1290
1640
  console.log('No tasks in queue.');
1291
1641
  } else {
1292
- console.log('ID'.padEnd(10) + 'STATUS'.padEnd(12) + 'INSTRUCTION');
1642
+ console.log('ID'.padEnd(10) + 'STATUS'.padEnd(12) + 'PRIORITY'.padEnd(10) + 'INSTRUCTION');
1293
1643
  for (const t of tasks) {
1294
- console.log(t.id.padEnd(10) + t.status.padEnd(12) + t.instruction.slice(0, 60));
1644
+ console.log(t.id.padEnd(10) + t.status.padEnd(12) + String(t.priority).padEnd(10) + t.instruction.slice(0, 60));
1295
1645
  }
1296
1646
  }
1297
1647
  break;
@@ -1311,9 +1661,65 @@ export async function handleQueue(opts: ParsedArgs): Promise<void> {
1311
1661
  console.log(removed ? `Removed task ${id}.` : `No task found with id "${id}".`);
1312
1662
  break;
1313
1663
  }
1664
+ case 'retry': {
1665
+ const id = opts.botFile;
1666
+ if (id) {
1667
+ // Retry a specific task
1668
+ const retried = await queue.retry(id);
1669
+ console.log(retried ? `Task ${id} reset to pending.` : `No failed/running task found with id "${id}".`);
1670
+ } else {
1671
+ // Retry all failed tasks
1672
+ const count = await queue.retryAll();
1673
+ console.log(`Reset ${count} failed task(s) to pending.`);
1674
+ }
1675
+ break;
1676
+ }
1314
1677
  }
1315
1678
  }
1316
1679
 
1680
+ export async function handleStatus(opts: ParsedArgs): Promise<void> {
1681
+ const store = new RunStore();
1682
+ const { TaskQueue } = await import('./bot/task-queue.js');
1683
+ const queue = new TaskQueue();
1684
+
1685
+ const orphans = store.checkOrphans();
1686
+ const recentRuns = store.list({ limit: 5 });
1687
+ const tasks = await queue.list();
1688
+ const pending = tasks.filter(t => t.status === 'pending').length;
1689
+ const running = tasks.filter(t => t.status === 'running').length;
1690
+ const completed = tasks.filter(t => t.status === 'completed').length;
1691
+ const failed = tasks.filter(t => t.status === 'failed').length;
1692
+
1693
+ if (opts.historyJson) {
1694
+ console.log(JSON.stringify({
1695
+ queue: { pending, running, completed, failed, total: tasks.length },
1696
+ orphanedRuns: orphans.length,
1697
+ recentRuns: recentRuns.map(r => ({
1698
+ id: r.id, outcome: r.outcome, summary: r.summary,
1699
+ startedAt: r.startedAt, durationMs: r.durationMs,
1700
+ })),
1701
+ }, null, 2));
1702
+ return;
1703
+ }
1704
+
1705
+ console.log('\n\x1b[1mWeaver Status\x1b[0m\n');
1706
+ console.log(` Queue: ${pending} pending, ${running} running, ${completed} completed, ${failed} failed`);
1707
+ if (orphans.length > 0) {
1708
+ console.log(` \x1b[33mOrphaned runs: ${orphans.length} (recovered)\x1b[0m`);
1709
+ }
1710
+
1711
+ if (recentRuns.length > 0) {
1712
+ console.log(`\n Recent runs:`);
1713
+ for (const r of recentRuns) {
1714
+ const icon = r.success ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
1715
+ console.log(` ${icon} ${r.id.slice(0, 8)} ${r.outcome.padEnd(9)} ${formatDuration(r.durationMs).padEnd(8)} ${r.summary.slice(0, 60)}`);
1716
+ }
1717
+ } else {
1718
+ console.log('\n No recent runs.');
1719
+ }
1720
+ console.log('');
1721
+ }
1722
+
1317
1723
  export async function handleGenesis(opts: ParsedArgs): Promise<void> {
1318
1724
  const projectDir = opts.file ?? process.cwd();
1319
1725
 
@@ -1465,6 +1871,7 @@ const COMMAND_HELP: Record<string, string> = {
1465
1871
  genesis: 'genesis [--init] [--watch] Run self-evolution cycle',
1466
1872
  eject: 'eject [--workflow bot|genesis] Export managed workflows',
1467
1873
  audit: 'audit [runId] [--limit N] View audit log',
1874
+ assistant: 'assistant AI-powered assistant for managing bots and workflows',
1468
1875
  };
1469
1876
 
1470
1877
  export function printHelp(command?: string): void {
@@ -1560,25 +1967,29 @@ export async function handleInit(opts: ParsedArgs): Promise<void> {
1560
1967
  return;
1561
1968
  }
1562
1969
 
1563
- // Detect best available provider
1970
+ // Detect best available provider (priority: Anthropic API > OpenAI API > Claude CLI > Copilot CLI)
1564
1971
  let provider: string = 'auto';
1972
+ const detected: string[] = [];
1565
1973
  try {
1566
1974
  if (process.env.ANTHROPIC_API_KEY) {
1567
1975
  provider = 'anthropic';
1568
- } else {
1569
- const { execFileSync } = await import('node:child_process');
1570
- try {
1571
- execFileSync('claude', ['--version'], { stdio: 'pipe' });
1572
- provider = 'claude-cli';
1573
- } catch {
1574
- try {
1575
- execFileSync('copilot', ['--version'], { stdio: 'pipe' });
1576
- provider = 'copilot-cli';
1577
- } catch {
1578
- // Stay with auto
1579
- }
1580
- }
1976
+ detected.push('Anthropic API (ANTHROPIC_API_KEY)');
1977
+ }
1978
+ if (process.env.OPENAI_API_KEY) {
1979
+ if (provider === 'auto') provider = 'openai';
1980
+ detected.push('OpenAI API (OPENAI_API_KEY)');
1581
1981
  }
1982
+ const { execFileSync } = await import('node:child_process');
1983
+ try {
1984
+ execFileSync('claude', ['--version'], { stdio: 'pipe' });
1985
+ if (provider === 'auto') provider = 'claude-cli';
1986
+ detected.push('Claude CLI');
1987
+ } catch { /* not found */ }
1988
+ try {
1989
+ execFileSync('copilot', ['--version'], { stdio: 'pipe' });
1990
+ if (provider === 'auto') provider = 'copilot-cli';
1991
+ detected.push('Copilot CLI');
1992
+ } catch { /* not found */ }
1582
1993
  } catch {
1583
1994
  // Stay with auto
1584
1995
  }
@@ -1590,10 +2001,175 @@ export async function handleInit(opts: ParsedArgs): Promise<void> {
1590
2001
 
1591
2002
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1592
2003
 
1593
- console.log(`\x1b[32m✓\x1b[0m Created ${configPath}`);
1594
- console.log(` Provider: ${provider}`);
1595
2004
  console.log('');
1596
- console.log('Next steps:');
1597
- console.log(' flow-weaver weaver providers # verify provider detection');
1598
- console.log(' flow-weaver weaver bot "Create a ..." # create your first workflow');
2005
+ console.log(' \x1b[1mWelcome to Weaver\x1b[0m — your AI workflow companion.');
2006
+ console.log('');
2007
+ console.log(' \x1b[2mDetecting providers...\x1b[0m');
2008
+ if (detected.length > 0) {
2009
+ for (const d of detected) console.log(` \x1b[32m✓\x1b[0m ${d}`);
2010
+ } else {
2011
+ console.log(' \x1b[33m⚠\x1b[0m No provider detected');
2012
+ console.log(' Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or install Claude CLI');
2013
+ }
2014
+ console.log('');
2015
+
2016
+ // Test connection if possible
2017
+ if (provider === 'claude-cli') {
2018
+ try {
2019
+ const { execFileSync: testExec } = await import('node:child_process');
2020
+ testExec('claude', ['--version'], { stdio: 'pipe', timeout: 5000 });
2021
+ console.log(' \x1b[2mTesting connection...\x1b[0m');
2022
+ console.log(' \x1b[32m✓\x1b[0m Claude CLI: connected');
2023
+ } catch {
2024
+ console.log(' \x1b[33m⚠\x1b[0m Could not verify Claude CLI connection');
2025
+ }
2026
+ }
2027
+
2028
+ console.log('');
2029
+ console.log(` \x1b[32m✓\x1b[0m Config written to .weaver.json`);
2030
+ console.log(` provider: ${provider}`);
2031
+ console.log(' approval: auto');
2032
+ console.log('');
2033
+ console.log(' Try it now:');
2034
+ console.log(' \x1b[36mweaver bot "Create a hello world workflow"\x1b[0m');
2035
+ console.log(' \x1b[36mweaver assistant\x1b[0m');
2036
+ console.log('');
2037
+ console.log(' Learn more:');
2038
+ console.log(' weaver examples \x1b[2m# see what weaver can do\x1b[0m');
2039
+ console.log(' weaver doctor \x1b[2m# validate your setup\x1b[0m');
2040
+ console.log('');
2041
+ }
2042
+
2043
+ export async function handleExamples(_opts: ParsedArgs): Promise<void> {
2044
+ console.log('');
2045
+ console.log(' \x1b[1mWeaver Examples\x1b[0m');
2046
+ console.log('');
2047
+ console.log(' \x1b[36mCreate workflows:\x1b[0m');
2048
+ console.log(' weaver bot "Create a workflow that validates user input and sends email"');
2049
+ console.log(' weaver bot "Create a data pipeline that reads CSV, transforms, and writes JSON"');
2050
+ console.log(' weaver bot "Create an AI agent with retry and fallback providers"');
2051
+ console.log(' weaver bot "Create a webhook handler that processes Stripe events"');
2052
+ console.log(' weaver bot "Create a RAG pipeline with document chunking and embedding"');
2053
+ console.log('');
2054
+ console.log(' \x1b[36mModify existing workflows:\x1b[0m');
2055
+ console.log(' weaver bot "Add error handling to my-workflow.ts" --file src/my-workflow.ts');
2056
+ console.log(' weaver bot "Add a validation step before the API call" --file src/pipeline.ts');
2057
+ console.log(' weaver bot "Make the retry node use exponential backoff" --file src/agent.ts');
2058
+ console.log('');
2059
+ console.log(' \x1b[36mInteractive assistant:\x1b[0m');
2060
+ console.log(' weaver assistant \x1b[2m# AI assistant with tools\x1b[0m');
2061
+ console.log(' weaver assistant --new \x1b[2m# fresh conversation\x1b[0m');
2062
+ console.log(' weaver assistant --list \x1b[2m# saved conversations\x1b[0m');
2063
+ console.log('');
2064
+ console.log(' \x1b[36mAutonomous mode:\x1b[0m');
2065
+ console.log(' weaver session --continuous \x1b[2m# process task queue\x1b[0m');
2066
+ console.log(' weaver queue add "Fix all validation errors"');
2067
+ console.log(' weaver session --continuous --until 10:00 --parallel 3');
2068
+ console.log('');
2069
+ console.log(' \x1b[36mInspect and debug:\x1b[0m');
2070
+ console.log(' flow-weaver validate src/*.ts \x1b[2m# check all workflows\x1b[0m');
2071
+ console.log(' flow-weaver diagram src/my-workflow.ts \x1b[2m# visual diagram\x1b[0m');
2072
+ console.log(' flow-weaver describe src/my-workflow.ts \x1b[2m# natural language description\x1b[0m');
2073
+ console.log('');
2074
+ console.log(' \x1b[36mCustomize the bot:\x1b[0m');
2075
+ console.log(' weaver eject \x1b[2m# get editable bot workflow\x1b[0m');
2076
+ console.log(' weaver eject --workflow bot \x1b[2m# eject specific workflow\x1b[0m');
2077
+ console.log('');
2078
+ }
2079
+
2080
+ export async function handleDoctor(opts: ParsedArgs): Promise<void> {
2081
+ const dir = opts.file ?? process.cwd();
2082
+ const checks: Array<{ label: string; status: 'ok' | 'warn' | 'fail'; detail: string }> = [];
2083
+
2084
+ // Config check
2085
+ const configPath = path.join(dir, '.weaver.json');
2086
+ if (fs.existsSync(configPath)) {
2087
+ checks.push({ label: 'Config', status: 'ok', detail: '.weaver.json found' });
2088
+ } else {
2089
+ checks.push({ label: 'Config', status: 'warn', detail: 'No .weaver.json — run "weaver init"' });
2090
+ }
2091
+
2092
+ // Provider check
2093
+ const config = await loadConfig(opts.configPath);
2094
+ const providerSetting = config?.provider ?? 'auto';
2095
+ const providerName = typeof providerSetting === 'object' ? providerSetting.name : String(providerSetting);
2096
+ let providerDetail = providerName;
2097
+
2098
+ if (providerName === 'anthropic' || process.env.ANTHROPIC_API_KEY) {
2099
+ providerDetail = 'anthropic (API key set)';
2100
+ checks.push({ label: 'Provider', status: 'ok', detail: providerDetail });
2101
+ } else {
2102
+ try {
2103
+ const { execFileSync: provCheck } = await import('node:child_process');
2104
+ provCheck('claude', ['--version'], { stdio: 'pipe', timeout: 5000 });
2105
+ providerDetail = 'claude-cli';
2106
+ checks.push({ label: 'Provider', status: 'ok', detail: providerDetail });
2107
+ } catch {
2108
+ checks.push({ label: 'Provider', status: 'fail', detail: 'No provider found — set ANTHROPIC_API_KEY or install Claude CLI' });
2109
+ }
2110
+ }
2111
+
2112
+ // Connection test
2113
+ if (providerName === 'claude-cli' || (!process.env.ANTHROPIC_API_KEY && providerName === 'auto')) {
2114
+ try {
2115
+ const { execFileSync: connCheck } = await import('node:child_process');
2116
+ const start = Date.now();
2117
+ connCheck('claude', ['-p', '--max-turns', '1', 'say ok'], { stdio: 'pipe', timeout: 15000 });
2118
+ checks.push({ label: 'Connection', status: 'ok', detail: `OK (${((Date.now() - start) / 1000).toFixed(1)}s)` });
2119
+ } catch {
2120
+ checks.push({ label: 'Connection', status: 'warn', detail: 'Could not verify (Claude CLI may need auth)' });
2121
+ }
2122
+ } else if (process.env.ANTHROPIC_API_KEY) {
2123
+ checks.push({ label: 'Connection', status: 'ok', detail: 'API key configured' });
2124
+ } else {
2125
+ checks.push({ label: 'Connection', status: 'warn', detail: 'Not tested' });
2126
+ }
2127
+
2128
+ // Flow Weaver version
2129
+ try {
2130
+ const { execFileSync: fwCheck } = await import('node:child_process');
2131
+ const version = fwCheck('npx', ['flow-weaver', '--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000, cwd: dir }).trim();
2132
+ checks.push({ label: 'Flow Weaver', status: 'ok', detail: version });
2133
+ } catch {
2134
+ checks.push({ label: 'Flow Weaver', status: 'fail', detail: 'Not installed — run "npm install @synergenius/flow-weaver"' });
2135
+ }
2136
+
2137
+ // Queue status
2138
+ try {
2139
+ const { TaskQueue } = await import('./bot/task-queue.js');
2140
+ process.env.WEAVER_PROJECT_DIR = dir;
2141
+ const queue = new TaskQueue();
2142
+ const tasks = await queue.list();
2143
+ const pending = tasks.filter(t => t.status === 'pending').length;
2144
+ const running = tasks.filter(t => t.status === 'running').length;
2145
+ checks.push({ label: 'Queue', status: 'ok', detail: `${pending} pending, ${running} running` });
2146
+ } catch {
2147
+ checks.push({ label: 'Queue', status: 'ok', detail: 'Empty (no queue file)' });
2148
+ }
2149
+
2150
+ // Plan file
2151
+ const planPath = path.join(dir, '.weaver-plan.md');
2152
+ if (fs.existsSync(planPath)) {
2153
+ checks.push({ label: 'Plan', status: 'ok', detail: '.weaver-plan.md found' });
2154
+ } else {
2155
+ checks.push({ label: 'Plan', status: 'warn', detail: 'No .weaver-plan.md — optional, guides bot behavior' });
2156
+ }
2157
+
2158
+ // Output
2159
+ console.log('');
2160
+ console.log(' \x1b[1mWeaver Doctor\x1b[0m');
2161
+ console.log('');
2162
+ const icons = { ok: '\x1b[32m✓\x1b[0m', warn: '\x1b[33m⚠\x1b[0m', fail: '\x1b[31m✗\x1b[0m' };
2163
+ for (const check of checks) {
2164
+ console.log(` ${icons[check.status]} ${check.label.padEnd(14)} ${check.detail}`);
2165
+ }
2166
+ console.log('');
2167
+
2168
+ const failures = checks.filter(c => c.status === 'fail');
2169
+ if (failures.length > 0) {
2170
+ console.log(` \x1b[31m${failures.length} issue(s) found.\x1b[0m`);
2171
+ } else {
2172
+ console.log(' \x1b[32mAll checks passed.\x1b[0m');
2173
+ }
2174
+ console.log('');
1599
2175
  }