@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
package/src/mcp-tools.ts CHANGED
@@ -206,8 +206,8 @@ export async function registerMcpTools(mcp: McpServer): Promise<void> {
206
206
  switch (args.action) {
207
207
  case 'add': {
208
208
  if (!args.task) return { content: [{ type: 'text', text: 'Error: task instruction required' }] };
209
- const id = await queue.add({ instruction: args.task as string, priority: 0 });
210
- return { content: [{ type: 'text', text: `Task added: ${id}` }] };
209
+ const { id, duplicate } = await queue.add({ instruction: args.task as string, priority: 0 });
210
+ return { content: [{ type: 'text', text: duplicate ? `Task already queued (${id})` : `Task added: ${id}` }] };
211
211
  }
212
212
  case 'list':
213
213
  return { content: [{ type: 'text', text: JSON.stringify(await queue.list(), null, 2) }] };
@@ -13,17 +13,18 @@ import type { WeaverContext } from '../bot/types.js';
13
13
  */
14
14
  export function weaverAbortTask(ctx: string): { ctx: string } {
15
15
  const context = JSON.parse(ctx) as WeaverContext;
16
- const task = JSON.parse(context.taskJson!) as { instruction?: string };
16
+ const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string } : {};
17
+ const reason = context.rejectionReason ?? 'no reason given';
17
18
  const result = {
18
19
  success: false,
19
20
  outcome: 'aborted',
20
- summary: `Task aborted: ${context.rejectionReason}`,
21
- instruction: task.instruction,
21
+ summary: `Task aborted: ${reason}`,
22
+ instruction: (task as { instruction?: string }).instruction,
22
23
  filesModified: [],
23
24
  filesCreated: [],
24
25
  };
25
26
 
26
- console.log(`\x1b[33m→ Task aborted: ${context.rejectionReason}\x1b[0m`);
27
+ console.log(`\x1b[33m→ Task aborted: ${reason}\x1b[0m`);
27
28
 
28
29
  context.resultJson = JSON.stringify(result);
29
30
  context.filesModified = '[]';
@@ -0,0 +1,303 @@
1
+ import type { WeaverContext } from '../bot/types.js';
2
+ import {
3
+ runAgentLoop,
4
+ createAnthropicProvider,
5
+ getOrCreateCliSession,
6
+ killAllCliSessions,
7
+ type AgentProvider,
8
+ type AgentMessage,
9
+ type ToolDefinition,
10
+ type StreamEvent,
11
+ type StreamOptions,
12
+ type ToolEvent,
13
+ } from '@synergenius/flow-weaver/agent';
14
+ import { WEAVER_TOOLS, createWeaverExecutor } from '../bot/weaver-tools.js';
15
+ import { auditEmit } from '../bot/audit-logger.js';
16
+ import { withRetry, getErrorGuidance } from '../bot/error-classifier.js';
17
+ import { CostTracker } from '../bot/cost-tracker.js';
18
+
19
+ // Clean up persistent sessions on process exit
20
+ let cleanupRegistered = false;
21
+ function registerCleanup(): void {
22
+ if (cleanupRegistered) return;
23
+ cleanupRegistered = true;
24
+ const cleanup = () => { try { killAllCliSessions(); } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] session cleanup failed:', err); } };
25
+ process.on('exit', cleanup);
26
+ process.on('SIGTERM', cleanup);
27
+ process.on('SIGINT', cleanup);
28
+ }
29
+
30
+ /**
31
+ * Adapter: wraps a persistent CliSession as an AgentProvider.
32
+ * The agent loop sends full message history each iteration;
33
+ * we only forward the latest user/tool messages since the session
34
+ * maintains its own conversation state.
35
+ */
36
+ class CliSessionProvider implements AgentProvider {
37
+ private sentCount = 0;
38
+
39
+ constructor(
40
+ private session: { ready: boolean; spawn: () => Promise<void>; send: (msg: string, systemPrompt?: string) => AsyncGenerator<StreamEvent> },
41
+ ) {}
42
+
43
+ async *stream(
44
+ messages: AgentMessage[],
45
+ _tools: ToolDefinition[],
46
+ options?: StreamOptions,
47
+ ): AsyncGenerator<StreamEvent> {
48
+ if (!this.session.ready) await this.session.spawn();
49
+
50
+ // Only send new messages (session has history of previous ones)
51
+ const newMessages = messages.slice(this.sentCount);
52
+ this.sentCount = messages.length;
53
+
54
+ // Format new messages into a prompt string
55
+ const prompt = newMessages
56
+ .map((m) => {
57
+ if (m.role === 'user') return typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
58
+ if (m.role === 'tool') return `Tool result (${m.toolCallId}): ${typeof m.content === 'string' ? m.content : JSON.stringify(m.content)}`;
59
+ return '';
60
+ })
61
+ .filter(Boolean)
62
+ .join('\n');
63
+
64
+ if (!prompt) return;
65
+
66
+ // Only pass system prompt on the first call
67
+ const systemPrompt = this.sentCount <= messages.length ? options?.systemPrompt : undefined;
68
+ yield* this.session.send(prompt, systemPrompt);
69
+ }
70
+
71
+ /** Reset for a new task (new conversation context) */
72
+ resetForNewTask(): void {
73
+ this.sentCount = 0;
74
+ }
75
+ }
76
+
77
+ // Test-only export (tree-shaken in production bundles)
78
+ export { CliSessionProvider };
79
+
80
+ // Re-use StepLogEntry shape from the bot types
81
+ type LocalStepLogEntry = { step: string; status: string; detail: string };
82
+
83
+ /**
84
+ * Tool-use agent execution. Claude drives the entire task via tool calls
85
+ * (validate, read-file, patch-file, run-shell, etc.) in a single continuous
86
+ * conversation. No separate plan or retry loop needed.
87
+ *
88
+ * @flowWeaver nodeType
89
+ * @label Agent Execute
90
+ * @input ctx [order:0] - Weaver context (JSON)
91
+ * @output ctx [order:0] - Weaver context with results (JSON)
92
+ * @output onSuccess [order:-2] - On Success
93
+ * @output onFailure [order:-1] [hidden] - On Failure
94
+ */
95
+ export async function weaverAgentExecute(
96
+ execute: boolean,
97
+ ctx: string,
98
+ ): Promise<{
99
+ onSuccess: boolean; onFailure: boolean;
100
+ ctx: string;
101
+ }> {
102
+ const context = JSON.parse(ctx) as WeaverContext;
103
+ const { env } = context;
104
+
105
+ if (!execute) {
106
+ context.resultJson = JSON.stringify({ success: true, toolCallCount: 0 });
107
+ context.validationResultJson = '[]';
108
+ context.filesModified = '[]';
109
+ context.stepLogJson = '[]';
110
+ context.allValid = true;
111
+ return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
112
+ }
113
+
114
+ const { providerInfo: pInfo, projectDir } = env;
115
+ const task = JSON.parse(context.taskJson!);
116
+
117
+ // Build system prompt
118
+ let systemPrompt: string;
119
+ try {
120
+ const mod = await import('../bot/system-prompt.js');
121
+ const basePrompt = await mod.buildSystemPrompt();
122
+ let cliCommands: { name: string; description: string; botCompatible?: boolean; options?: { flags: string; arg?: string; description: string }[] }[] = [];
123
+ try {
124
+ const docMeta = await import('@synergenius/flow-weaver/doc-metadata');
125
+ cliCommands = docMeta.CLI_COMMANDS ?? [];
126
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] doc-metadata unavailable (older fw):', err); }
127
+ const botPrompt = mod.buildBotSystemPrompt(context.contextBundle, cliCommands, projectDir);
128
+ systemPrompt = basePrompt + '\n\n' + botPrompt;
129
+ } catch (err) {
130
+ if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] system prompt build failed, using fallback:', err);
131
+ systemPrompt = 'You are Weaver, an AI workflow bot. Use the provided tools to complete tasks.';
132
+ }
133
+
134
+ const taskPrompt = `Task: ${task.instruction}\nProject directory: ${projectDir}\n${task.targets ? 'Target files: ' + task.targets.join(', ') : ''}`;
135
+
136
+ // Create renderer — single source of all terminal output
137
+ const { TerminalRenderer } = await import('../bot/terminal-renderer.js');
138
+ const renderer = new TerminalRenderer({ verbose: !!process.env.WEAVER_VERBOSE });
139
+
140
+ auditEmit('run-start', { task: task.instruction });
141
+
142
+ try {
143
+ const provider = createProvider(pInfo, projectDir);
144
+ const executor = createWeaverExecutor(projectDir);
145
+
146
+ const filesModified: string[] = [];
147
+ const stepLog: LocalStepLogEntry[] = [];
148
+ const taskStart = Date.now();
149
+
150
+ // Route tool events through renderer + track state
151
+ const onToolEvent = (event: ToolEvent) => {
152
+ renderer.onToolEvent(event);
153
+
154
+ if (event.type === 'tool_call_result') {
155
+ stepLog.push({
156
+ step: event.name,
157
+ status: event.isError ? 'error' : 'ok',
158
+ detail: event.isError ? (event.result ?? '').slice(0, 200) : event.name,
159
+ });
160
+ if ((event.name === 'patch_file' || event.name === 'write_file') && !event.isError && event.args?.file) {
161
+ filesModified.push(event.args.file as string);
162
+ }
163
+ }
164
+ };
165
+
166
+ const onStreamEvent = (event: StreamEvent) => renderer.onStreamEvent(event);
167
+
168
+ const result = await withRetry(
169
+ () => runAgentLoop(
170
+ provider,
171
+ WEAVER_TOOLS,
172
+ executor,
173
+ [{ role: 'user', content: taskPrompt }],
174
+ { systemPrompt, maxIterations: 15, onToolEvent, onStreamEvent },
175
+ ),
176
+ {
177
+ maxRetries: 3,
178
+ baseDelayMs: 5_000,
179
+ onRetry: (attempt, delay, err) => {
180
+ renderer.warn(`Transient error, retrying in ${delay / 1000}s (attempt ${attempt}/3): ${err.message.slice(0, 80)}`);
181
+ },
182
+ },
183
+ );
184
+
185
+ const usage = result.usage;
186
+ const model = pInfo.model ?? 'claude-sonnet-4-6';
187
+ const estimatedCost = CostTracker.estimateCost(model, {
188
+ inputTokens: usage.promptTokens,
189
+ outputTokens: usage.completionTokens,
190
+ });
191
+
192
+ // Post-agent validation gate: don't trust the AI's self-assessment
193
+ const uniqueFiles = [...new Set(filesModified)];
194
+ let validationPassed = result.success;
195
+ if (uniqueFiles.length > 0) {
196
+ try {
197
+ const { weaverValidateGate } = await import('./validate-gate.js');
198
+ const gateCtx: WeaverContext = { ...context, filesModified: JSON.stringify(uniqueFiles) };
199
+ const gateResult = weaverValidateGate(JSON.stringify(gateCtx));
200
+ const gateData = JSON.parse(gateResult.ctx) as WeaverContext;
201
+ if (!gateData.allValid) {
202
+ validationPassed = false;
203
+ renderer.warn('Post-agent validation found errors — task marked as failed');
204
+ }
205
+ context.validationResultJson = gateData.validationResultJson;
206
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] validate-gate unavailable, skipping:', err); }
207
+ }
208
+
209
+ context.resultJson = JSON.stringify({
210
+ success: validationPassed,
211
+ summary: result.summary,
212
+ toolCallCount: result.toolCallCount,
213
+ usage: { inputTokens: usage.promptTokens, outputTokens: usage.completionTokens, estimatedCost },
214
+ });
215
+ context.filesModified = JSON.stringify(uniqueFiles);
216
+ context.stepLogJson = JSON.stringify(stepLog);
217
+ context.allValid = validationPassed;
218
+
219
+ auditEmit('run-complete', {
220
+ success: validationPassed,
221
+ toolCalls: result.toolCallCount,
222
+ filesModified: uniqueFiles.length,
223
+ tokens: { in: usage.promptTokens, out: usage.completionTokens },
224
+ estimatedCost,
225
+ });
226
+
227
+ renderer.taskEnd(validationPassed, {
228
+ toolCalls: result.toolCallCount,
229
+ inputTokens: usage.promptTokens,
230
+ outputTokens: usage.completionTokens,
231
+ estimatedCost,
232
+ filesModified: uniqueFiles.length,
233
+ elapsed: Date.now() - taskStart,
234
+ });
235
+
236
+ return { onSuccess: validationPassed, onFailure: !validationPassed, ctx: JSON.stringify(context) };
237
+ } catch (err: unknown) {
238
+ const msg = err instanceof Error ? err.message : String(err);
239
+ let errorDetail = msg;
240
+ const guidance = getErrorGuidance(msg);
241
+ if (guidance) errorDetail = `${msg}\n Hint: ${guidance}`;
242
+ renderer.error('Agent error', errorDetail);
243
+
244
+ context.resultJson = JSON.stringify({ success: false, error: msg });
245
+ context.filesModified = '[]';
246
+ context.stepLogJson = '[]';
247
+ context.allValid = false;
248
+
249
+ return { onSuccess: false, onFailure: true, ctx: JSON.stringify(context) };
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Create an AgentProvider from pack-weaver's ProviderInfo.
255
+ */
256
+ function createProvider(
257
+ pInfo: { type: string; apiKey?: string; model?: string; maxTokens?: number },
258
+ projectDir?: string,
259
+ ): AgentProvider {
260
+ const type = pInfo.type ?? 'auto';
261
+
262
+ // Explicit Anthropic API
263
+ if (type === 'anthropic' && pInfo.apiKey) {
264
+ return createAnthropicProvider({
265
+ apiKey: pInfo.apiKey,
266
+ model: pInfo.model,
267
+ maxTokens: pInfo.maxTokens,
268
+ });
269
+ }
270
+
271
+ // Claude CLI — use persistent session for warm start
272
+ if (type === 'claude-cli') {
273
+ return createSessionProvider(pInfo.model, projectDir);
274
+ }
275
+
276
+ // Auto mode: try Anthropic API key from env, then fall back to Claude CLI
277
+ if (type === 'auto') {
278
+ if (process.env.ANTHROPIC_API_KEY) {
279
+ return createAnthropicProvider({
280
+ apiKey: process.env.ANTHROPIC_API_KEY,
281
+ model: pInfo.model,
282
+ maxTokens: pInfo.maxTokens,
283
+ });
284
+ }
285
+ // No API key — use Claude CLI with persistent session
286
+ return createSessionProvider(pInfo.model, projectDir);
287
+ }
288
+
289
+ throw new Error(
290
+ `Unsupported provider type: ${type}. Use 'anthropic', 'claude-cli', or 'auto'.`,
291
+ );
292
+ }
293
+
294
+ function createSessionProvider(model?: string, projectDir?: string): CliSessionProvider {
295
+ registerCleanup();
296
+ const key = projectDir ?? process.cwd();
297
+ const session = getOrCreateCliSession(key, {
298
+ binPath: 'claude',
299
+ cwd: key,
300
+ model: model ?? 'claude-sonnet-4-6',
301
+ });
302
+ return new CliSessionProvider(session);
303
+ }
@@ -1,11 +1,15 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
1
4
  import type { WeaverContext } from '../bot/types.js';
5
+ import { withFileLock } from '../bot/file-lock.js';
2
6
 
3
7
  /**
4
8
  * Generates the final bot session report. Receives context from any
5
9
  * of the three paths: read-only, main execution, or abort.
10
+ * Also marks the queue task as completed or failed.
6
11
  *
7
12
  * @flowWeaver nodeType
8
- * @expression
9
13
  * @label Bot Report
10
14
  * @executeWhen DISJUNCTION
11
15
  * @input [mainCtx] [order:0] - Context from main path (JSON, optional)
@@ -15,20 +19,21 @@ import type { WeaverContext } from '../bot/types.js';
15
19
  * @output reportJson [order:1] [hidden] - Full report (JSON)
16
20
  * @output onFailure [hidden]
17
21
  */
18
- export function weaverBotReport(
22
+ export async function weaverBotReport(
23
+ execute: boolean,
19
24
  mainCtx?: string,
20
25
  readCtx?: string,
21
26
  abortCtx?: string,
22
- ): { summary: string; reportJson: string } {
27
+ ): Promise<{ onSuccess: boolean; onFailure: boolean; summary: string; reportJson: string }> {
23
28
  const ctxStr = mainCtx ?? readCtx ?? abortCtx;
24
29
 
25
- if (!ctxStr) {
30
+ if (!execute || !ctxStr) {
26
31
  const report = { task: {}, path: 'unknown', result: null, filesModified: [], gitResult: null, timestamp: Date.now() };
27
- return { summary: '', reportJson: JSON.stringify(report) };
32
+ return { onSuccess: true, onFailure: false, summary: '', reportJson: JSON.stringify(report) };
28
33
  }
29
34
 
30
35
  const context = JSON.parse(ctxStr) as WeaverContext;
31
- const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string; mode?: string } : {};
36
+ const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string; mode?: string; queueId?: string } : {};
32
37
  const files: string[] = context.filesModified ? JSON.parse(context.filesModified) : [];
33
38
  const gitResult = context.gitResultJson ? JSON.parse(context.gitResultJson) : null;
34
39
 
@@ -52,8 +57,10 @@ export function weaverBotReport(
52
57
  parts.push(`Task: ${task.instruction}`);
53
58
  }
54
59
 
60
+ const success = result?.success !== false && pathName !== 'abort';
61
+
55
62
  if (result) {
56
- parts.push(`Outcome: ${result.outcome ?? (result.success ? 'completed' : 'failed')}`);
63
+ parts.push(`Outcome: ${result.outcome ?? (success ? 'completed' : 'failed')}`);
57
64
  if (result.summary) parts.push(`Summary: ${result.summary}`);
58
65
  }
59
66
 
@@ -67,6 +74,13 @@ export function weaverBotReport(
67
74
 
68
75
  const summary = parts.join(' | ');
69
76
 
77
+ // Mark queue task as completed or failed
78
+ if (task.queueId) {
79
+ try {
80
+ await markQueueTask(task.queueId, success ? 'completed' : 'failed');
81
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[bot-report] queue update failed:', err); }
82
+ }
83
+
70
84
  const report = {
71
85
  task,
72
86
  path: pathName,
@@ -76,7 +90,24 @@ export function weaverBotReport(
76
90
  timestamp: Date.now(),
77
91
  };
78
92
 
79
- console.log(`\n\x1b[1m${result?.success !== false ? '\x1b[32m' : '\x1b[31m'}Bot Report: ${summary}\x1b[0m\n`);
93
+ console.log(`\n\x1b[1m${success ? '\x1b[32m' : '\x1b[31m'}Bot Report: ${summary}\x1b[0m\n`);
94
+
95
+ return { onSuccess: success, onFailure: !success, summary, reportJson: JSON.stringify(report) };
96
+ }
97
+
98
+ async function markQueueTask(id: string, status: 'completed' | 'failed'): Promise<void> {
99
+ const queuePath = path.join(os.homedir(), '.weaver', 'task-queue.ndjson');
100
+ if (!fs.existsSync(queuePath)) return;
80
101
 
81
- return { summary, reportJson: JSON.stringify(report) };
102
+ await withFileLock(queuePath, () => {
103
+ const content = fs.readFileSync(queuePath, 'utf-8').trim();
104
+ if (!content) return;
105
+ const tasks = content.split('\n').filter(Boolean).map(l => JSON.parse(l));
106
+ const task = tasks.find((t: { id: string }) => t.id === id);
107
+ if (task) {
108
+ task.status = status;
109
+ fs.writeFileSync(queuePath, tasks.map((t: unknown) => JSON.stringify(t)).join('\n') + '\n', 'utf-8');
110
+ console.log(`\x1b[36m→ Queue task ${id}: ${status}\x1b[0m`);
111
+ }
112
+ });
82
113
  }
@@ -4,9 +4,9 @@ import * as path from 'node:path';
4
4
  import type { WeaverContext } from '../bot/types.js';
5
5
 
6
6
  /**
7
- * Builds the knowledge bundle the AI needs for planning. Calls
8
- * flow-weaver context authoring for the base knowledge, and
9
- * appends target file sources for modify tasks.
7
+ * Builds the knowledge bundle the AI needs for planning.
8
+ * Adaptive: for modify tasks, includes only grammar + referenced node types.
9
+ * For create tasks, includes full authoring context + templates.
10
10
  *
11
11
  * @flowWeaver nodeType
12
12
  * @expression
@@ -21,31 +21,81 @@ export function weaverBuildContext(ctx: string): { ctx: string } {
21
21
  const task = JSON.parse(context.taskJson!) as { mode?: string; targets?: string[] };
22
22
  const sections: string[] = [];
23
23
 
24
+ if (task.mode === 'modify' && task.targets?.length) {
25
+ // Adaptive context: minimal grammar + target files + referenced node types
26
+ sections.push(...buildModifyContext(projectDir, task.targets));
27
+ } else {
28
+ // Full context for create tasks or unknown modes
29
+ sections.push(...buildFullContext(projectDir, task.mode));
30
+ }
31
+
32
+ const bundle = sections.join('\n\n---\n\n');
33
+ // Output handled by session renderer; keep a dim line for debugging
34
+ if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Context: ${bundle.length} chars\x1b[0m\n`);
35
+
36
+ context.contextBundle = bundle;
37
+ return { ctx: JSON.stringify(context) };
38
+ }
39
+
40
+ /** Minimal context for modify tasks: grammar + annotations + target sources + referenced node types. */
41
+ function buildModifyContext(projectDir: string, targets: string[]): string[] {
42
+ const sections: string[] = [];
43
+
44
+ // Minimal grammar (jsdoc-grammar + advanced-annotations only — skip concepts/scaffold/patterns)
24
45
  try {
25
- const ctxOutput = execFileSync('flow-weaver', ['context', 'authoring', '--profile', 'assistant'], {
26
- encoding: 'utf-8',
27
- stdio: ['pipe', 'pipe', 'pipe'],
28
- timeout: 30_000,
29
- cwd: projectDir,
30
- }).trim();
46
+ const ctxOutput = execFileSync(
47
+ 'flow-weaver',
48
+ ['context', '--topics', 'jsdoc-grammar,advanced-annotations', '--profile', 'assistant'],
49
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30_000, cwd: projectDir },
50
+ ).trim();
31
51
  if (ctxOutput) sections.push(ctxOutput);
32
- } catch {
52
+ } catch (err) {
53
+ if (process.env.WEAVER_VERBOSE) console.error('[build-context] modify context unavailable:', err);
33
54
  sections.push('(flow-weaver context not available)');
34
55
  }
35
56
 
36
- if (task.mode === 'modify' && task.targets) {
37
- for (const target of task.targets) {
38
- const filePath = path.isAbsolute(target) ? target : path.resolve(projectDir, target);
39
- try {
40
- if (fs.existsSync(filePath)) {
41
- const source = fs.readFileSync(filePath, 'utf-8');
42
- sections.push(`## Current Source: ${target}\n\n\`\`\`typescript\n${source}\n\`\`\``);
57
+ // Target file sources + referenced node type sources
58
+ const includedFiles = new Set<string>();
59
+ for (const target of targets) {
60
+ const filePath = path.isAbsolute(target) ? target : path.resolve(projectDir, target);
61
+ try {
62
+ if (!fs.existsSync(filePath)) continue;
63
+ const source = fs.readFileSync(filePath, 'utf-8');
64
+ sections.push(`## Target: ${target}\n\n\`\`\`typescript\n${source}\n\`\`\``);
65
+ includedFiles.add(filePath);
66
+
67
+ // Extract import paths to find referenced node type files
68
+ const nodeTypeSources = extractReferencedNodeTypes(filePath, source, projectDir);
69
+ for (const [relPath, ntSource] of nodeTypeSources) {
70
+ const absPath = path.resolve(projectDir, relPath);
71
+ if (!includedFiles.has(absPath)) {
72
+ includedFiles.add(absPath);
73
+ sections.push(`## Node Type: ${relPath}\n\n\`\`\`typescript\n${ntSource}\n\`\`\``);
43
74
  }
44
- } catch { /* skip unreadable files */ }
45
- }
75
+ }
76
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] unreadable file:', err); }
77
+ }
78
+
79
+ return sections;
80
+ }
81
+
82
+ /** Full context for create tasks: full authoring preset + templates. */
83
+ function buildFullContext(projectDir: string, mode?: string): string[] {
84
+ const sections: string[] = [];
85
+
86
+ try {
87
+ const ctxOutput = execFileSync(
88
+ 'flow-weaver',
89
+ ['context', 'authoring', '--profile', 'assistant'],
90
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30_000, cwd: projectDir },
91
+ ).trim();
92
+ if (ctxOutput) sections.push(ctxOutput);
93
+ } catch (err) {
94
+ if (process.env.WEAVER_VERBOSE) console.error('[build-context] full context unavailable:', err);
95
+ sections.push('(flow-weaver context not available)');
46
96
  }
47
97
 
48
- if (task.mode === 'create') {
98
+ if (mode === 'create') {
49
99
  try {
50
100
  const templates = execFileSync('flow-weaver', ['list', 'templates'], {
51
101
  encoding: 'utf-8',
@@ -54,12 +104,49 @@ export function weaverBuildContext(ctx: string): { ctx: string } {
54
104
  cwd: projectDir,
55
105
  }).trim();
56
106
  if (templates) sections.push(`## Available Templates\n\n${templates}`);
57
- } catch { /* templates not available */ }
107
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] templates not available:', err); }
58
108
  }
59
109
 
60
- const bundle = sections.join('\n\n---\n\n');
61
- console.log(`\x1b[36m→ Context bundle: ${bundle.length} chars\x1b[0m`);
110
+ return sections;
111
+ }
62
112
 
63
- context.contextBundle = bundle;
64
- return { ctx: JSON.stringify(context) };
113
+ /**
114
+ * Extract node type file sources referenced by a workflow file's imports.
115
+ * Parses import statements to find relative imports from node-types directories.
116
+ */
117
+ function extractReferencedNodeTypes(
118
+ filePath: string,
119
+ source: string,
120
+ projectDir: string,
121
+ ): Array<[relPath: string, source: string]> {
122
+ const results: Array<[string, string]> = [];
123
+ const dir = path.dirname(filePath);
124
+
125
+ // Match: import { ... } from '../node-types/foo.js' or './node-types/bar'
126
+ const importRegex = /import\s+(?:type\s+)?{[^}]+}\s+from\s+['"]([^'"]+)['"]/g;
127
+ let match: RegExpExecArray | null;
128
+
129
+ while ((match = importRegex.exec(source)) !== null) {
130
+ const importPath = match[1];
131
+ // Only include relative imports that look like node type files
132
+ if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;
133
+ if (!importPath.includes('node-type') && !importPath.includes('node_type')) continue;
134
+
135
+ // Resolve the import to an absolute path
136
+ let resolved = path.resolve(dir, importPath);
137
+ // Try with .ts extension if no extension
138
+ if (!fs.existsSync(resolved)) {
139
+ if (fs.existsSync(resolved + '.ts')) resolved = resolved + '.ts';
140
+ else if (fs.existsSync(resolved.replace(/\.js$/, '.ts'))) resolved = resolved.replace(/\.js$/, '.ts');
141
+ else continue;
142
+ }
143
+
144
+ try {
145
+ const ntSource = fs.readFileSync(resolved, 'utf-8');
146
+ const relPath = path.relative(projectDir, resolved);
147
+ results.push([relPath, ntSource]);
148
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] unreadable node type:', err); }
149
+ }
150
+
151
+ return results;
65
152
  }
@@ -1,5 +1,5 @@
1
1
  import { execFileSync } from 'node:child_process';
2
- import type { WeaverConfig, WeaverEnv, ProviderInfo } from '../bot/types.js';
2
+ import type { BotConfig, WeaverEnv, ProviderInfo } from '../bot/types.js';
3
3
 
4
4
  const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
5
5
 
@@ -23,7 +23,7 @@ function whichSafe(cmd: string, cwd: string): string {
23
23
  * @output env [order:0] - Weaver environment bundle
24
24
  * @output onFailure [hidden]
25
25
  */
26
- export function weaverDetectProvider(projectDir: string, config: WeaverConfig): {
26
+ export function weaverDetectProvider(projectDir: string, config: BotConfig): {
27
27
  env: WeaverEnv;
28
28
  } {
29
29
  const providerSetting = config.provider ?? 'auto';
@@ -69,7 +69,8 @@ export function weaverDetectProvider(projectDir: string, config: WeaverConfig):
69
69
  }
70
70
 
71
71
  const label = providerInfo.model ? `${type} (${providerInfo.model})` : type;
72
- console.log(`\x1b[36m→ Provider: ${label}\x1b[0m`);
72
+ // Provider info now shown by session renderer; keep for verbose/debug
73
+ if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Provider: ${label}\x1b[0m\n`);
73
74
 
74
75
  return {
75
76
  env: { projectDir, config, providerType: type, providerInfo },