@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
@@ -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 },
@@ -1,8 +1,8 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import * as os from 'node:os';
4
- import type { WeaverContext } from '../bot/types.js';
5
- import { callAI, parseJsonResponse } from '../bot/ai-client.js';
4
+ import type { WeaverContext, StepLogEntry } from '../bot/types.js';
5
+ import { callAI, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
6
6
  import { executeStep } from '../bot/step-executor.js';
7
7
  import { validateFiles } from '../bot/file-validator.js';
8
8
  import { auditEmit } from '../bot/audit-logger.js';
@@ -38,19 +38,29 @@ export async function weaverExecValidateRetry(
38
38
 
39
39
  const { providerInfo: pInfo, projectDir } = env;
40
40
  const maxAttempts = 3;
41
+ const taskDeadline = Date.now() + 180_000; // 3 minute max per task
41
42
  let currentPlan = JSON.parse(context.planJson!);
42
43
  let allFilesModified: string[] = [];
44
+ let allStepLog: StepLogEntry[] = [];
43
45
  let lastExecResult: Record<string, unknown> = {};
44
46
  let lastValidation: Array<{ file: string; valid: boolean; errors: string[] }> = [];
45
47
  let allValid = false;
48
+ let prevErrorCount = Infinity;
46
49
 
47
50
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
51
+ // Task timeout — stop if we've exceeded the deadline
52
+ if (Date.now() > taskDeadline) {
53
+ console.error('\x1b[33m→ Task timeout (3 min), moving on\x1b[0m');
54
+ break;
55
+ }
56
+
48
57
  console.log(`\x1b[36m→ Attempt ${attempt}/${maxAttempts}\x1b[0m`);
49
58
  auditEmit('step-start', { attempt, stepCount: currentPlan.steps?.length });
50
59
 
51
60
  const execResult = await executePlanSteps(currentPlan, projectDir);
52
61
  lastExecResult = execResult;
53
62
  allFilesModified = [...new Set([...allFilesModified, ...execResult.filesModified])];
63
+ allStepLog.push(...execResult.stepLog);
54
64
  auditEmit('step-complete', { attempt, filesModified: execResult.filesModified, errors: execResult.errors });
55
65
 
56
66
  const validation = await validateFiles(execResult.filesModified, projectDir);
@@ -59,6 +69,13 @@ export async function weaverExecValidateRetry(
59
69
  const errorCount = validation.filter(v => !v.valid).length;
60
70
  auditEmit('validation-run', { attempt, allValid, errorCount });
61
71
 
72
+ // Stop retrying if errors didn't decrease — AI is stuck
73
+ if (attempt > 1 && errorCount >= prevErrorCount) {
74
+ console.error(`\x1b[33m→ Errors not decreasing (${errorCount} >= ${prevErrorCount}), stopping retry\x1b[0m`);
75
+ break;
76
+ }
77
+ prevErrorCount = errorCount;
78
+
62
79
  // Collect design warnings from valid files
63
80
  const designWarnings = validation
64
81
  .filter(v => v.valid && v.warnings.length > 0)
@@ -97,16 +114,23 @@ export async function weaverExecValidateRetry(
97
114
  try {
98
115
  const mod = await import('../bot/system-prompt.js');
99
116
  systemPrompt = await mod.buildSystemPrompt();
100
- } catch {
117
+ } catch (err) {
118
+ if (process.env.WEAVER_VERBOSE) console.error('[exec-validate-retry] system prompt build failed:', err);
101
119
  systemPrompt = 'You are Weaver. Return ONLY valid JSON.';
102
120
  }
103
121
 
104
- const fixPrompt = `The following validation errors occurred:\n${errors}\n\nProvide a fix plan as JSON with steps and summary.`;
122
+ // Include step outputs so the AI has context from discovery steps
123
+ const outputContext = Object.entries(execResult.stepOutputs)
124
+ .map(([id, out]) => `--- Output of ${id} ---\n${out}`)
125
+ .join('\n\n');
126
+
127
+ const fixPrompt = `The following validation errors occurred:\n${errors}\n\n${outputContext ? `Discovery step outputs:\n${outputContext}\n\n` : ''}Provide a CONCRETE fix plan. Every patch-file step MUST include "file" (absolute path from the outputs above) and "patches" array with exact "find"/"replace" strings. Do NOT use placeholders.`;
105
128
 
106
129
  const text = await callAI(pInfo, systemPrompt, fixPrompt, 8192);
107
130
 
108
- currentPlan = parseJsonResponse(text);
109
- console.log(`\x1b[36m→ Fix plan: ${(currentPlan as { summary?: string }).summary ?? 'generated'}\x1b[0m`);
131
+ const parsed = parseJsonResponse(text);
132
+ currentPlan = normalizePlan(parsed);
133
+ console.log(`\x1b[36m→ Fix plan: ${currentPlan.summary} (${currentPlan.steps.length} steps)\x1b[0m`);
110
134
  } catch (err: unknown) {
111
135
  const msg = err instanceof Error ? err.message : String(err);
112
136
  console.error(`\x1b[31m→ Fix planning failed: ${msg}\x1b[0m`);
@@ -118,6 +142,7 @@ export async function weaverExecValidateRetry(
118
142
  context.resultJson = JSON.stringify(lastExecResult);
119
143
  context.validationResultJson = JSON.stringify(lastValidation);
120
144
  context.filesModified = JSON.stringify(allFilesModified);
145
+ context.stepLogJson = JSON.stringify(allStepLog);
121
146
  context.allValid = allValid;
122
147
 
123
148
  return { onSuccess: allValid, onFailure: !allValid, ctx: JSON.stringify(context) };
@@ -126,9 +151,11 @@ export async function weaverExecValidateRetry(
126
151
  async function executePlanSteps(
127
152
  plan: { steps: Array<{ id: string; operation: string; description: string; args: Record<string, unknown> }> },
128
153
  projectDir: string,
129
- ): Promise<{ success: boolean; filesModified: string[]; errors: string[]; stepsCompleted: number; stepsTotal: number }> {
154
+ ): Promise<{ success: boolean; filesModified: string[]; errors: string[]; stepsCompleted: number; stepsTotal: number; stepLog: StepLogEntry[]; stepOutputs: Record<string, string> }> {
130
155
  const filesModified: string[] = [];
131
156
  const errors: string[] = [];
157
+ const stepLog: StepLogEntry[] = [];
158
+ const stepOutputs: Record<string, string> = {};
132
159
  let completed = 0;
133
160
  const steps = plan.steps ?? [];
134
161
 
@@ -136,23 +163,35 @@ async function executePlanSteps(
136
163
  const steering = checkSteeringSignal();
137
164
  if (steering === 'cancel') {
138
165
  errors.push(`Cancelled at step ${step.id}`);
166
+ stepLog.push({ step: step.id, status: 'error', detail: 'Cancelled by steering signal' });
139
167
  break;
140
168
  }
141
169
 
142
170
  try {
143
171
  const result = await executeStep(step, projectDir);
172
+ if (result.blocked) {
173
+ console.error(`\x1b[33m ⚠ ${step.id}: ${result.blockReason}\x1b[0m`);
174
+ stepLog.push({ step: step.id, status: 'blocked', detail: result.blockReason });
175
+ continue;
176
+ }
144
177
  if (result.file) filesModified.push(result.file);
145
178
  if (result.files) filesModified.push(...result.files);
179
+ // Capture step output for feeding into fix prompts
180
+ if (result.output) {
181
+ stepOutputs[step.id] = result.output.slice(0, 4000); // cap at 4k to fit in prompt
182
+ }
146
183
  completed++;
147
184
  console.log(`\x1b[32m + ${step.id}: ${step.description}\x1b[0m`);
185
+ stepLog.push({ step: step.id, status: 'ok', detail: step.description });
148
186
  } catch (err: unknown) {
149
187
  const msg = err instanceof Error ? err.message : String(err);
150
188
  errors.push(`${step.id}: ${msg}`);
151
189
  console.error(`\x1b[31m x ${step.id}: ${msg}\x1b[0m`);
190
+ stepLog.push({ step: step.id, status: 'error', detail: msg });
152
191
  }
153
192
  }
154
193
 
155
- return { success: errors.length === 0, filesModified: [...new Set(filesModified)], errors, stepsCompleted: completed, stepsTotal: steps.length };
194
+ return { success: errors.length === 0, filesModified: [...new Set(filesModified)], errors, stepsCompleted: completed, stepsTotal: steps.length, stepLog, stepOutputs };
156
195
  }
157
196
 
158
197
  function checkSteeringSignal(): 'cancel' | null {
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import * as os from 'node:os';
4
4
  import type { WeaverEnv } from '../bot/types.js';
5
- import { executeStep } from '../bot/step-executor.js';
5
+ import { executeStep, resetPlanFileCounter } from '../bot/step-executor.js';
6
6
 
7
7
  /**
8
8
  * Executes plan steps via the flow-weaver CLI. Checks steering
@@ -39,6 +39,7 @@ export async function weaverExecutePlan(
39
39
  }
40
40
 
41
41
  const { projectDir } = env;
42
+ resetPlanFileCounter(); // Reset per-plan write counter for safety guards
42
43
  const plan = JSON.parse(planJson) as { steps: Array<{ id: string; operation: string; description: string; args: Record<string, unknown> }> };
43
44
  const filesModified: string[] = [];
44
45
  const filesCreated: string[] = [];
@@ -55,10 +56,27 @@ export async function weaverExecutePlan(
55
56
  };
56
57
  }
57
58
 
58
- for (const step of plan.steps) {
59
+ for (let i = 0; i < plan.steps.length; i++) {
60
+ const step = plan.steps[i];
61
+ // Defensive: ensure step has required fields
62
+ if (!step || typeof step !== 'object') {
63
+ errors.push(`step-${i + 1}: Malformed step (not an object)`);
64
+ continue;
65
+ }
66
+ const stepId = step.id ?? `step-${i + 1}`;
67
+ const stepDesc = step.description ?? step.operation ?? 'unknown';
68
+ if (!step.operation) {
69
+ errors.push(`${stepId}: Missing "operation" field`);
70
+ console.error(`\x1b[31m x ${stepId}: Missing operation\x1b[0m`);
71
+ continue;
72
+ }
73
+ if (!step.args) {
74
+ step.args = {};
75
+ }
76
+
59
77
  const steering = checkSteering();
60
78
  if (steering === 'cancel') {
61
- output.push(`Cancelled at step ${step.id}`);
79
+ output.push(`Cancelled at ${stepId}`);
62
80
  break;
63
81
  }
64
82
  if (steering === 'pause') {
@@ -68,18 +86,24 @@ export async function weaverExecutePlan(
68
86
 
69
87
  try {
70
88
  const result = await executeStep(step, projectDir);
89
+ if (result.blocked) {
90
+ errors.push(`${stepId}: BLOCKED - ${result.blockReason}`);
91
+ output.push(`${stepId}: BLOCKED - ${result.blockReason}`);
92
+ console.error(`\x1b[33m ⚠ ${stepId}: ${result.blockReason}\x1b[0m`);
93
+ continue;
94
+ }
71
95
  if (result.file) {
72
96
  if (result.created) filesCreated.push(result.file);
73
97
  else filesModified.push(result.file);
74
98
  }
75
99
  completed++;
76
- output.push(`${step.id}: ${step.description} - done`);
77
- console.log(`\x1b[32m + ${step.id}: ${step.description}\x1b[0m`);
100
+ output.push(`${stepId}: ${stepDesc} - done`);
101
+ console.log(`\x1b[32m + ${stepId}: ${stepDesc}\x1b[0m`);
78
102
  } catch (err: unknown) {
79
103
  const msg = err instanceof Error ? err.message : String(err);
80
- errors.push(`${step.id}: ${msg}`);
81
- output.push(`${step.id}: FAILED - ${msg}`);
82
- console.error(`\x1b[31m x ${step.id}: ${msg}\x1b[0m`);
104
+ errors.push(`${stepId}: ${msg}`);
105
+ output.push(`${stepId}: FAILED - ${msg}`);
106
+ console.error(`\x1b[31m x ${stepId}: ${msg}\x1b[0m`);
83
107
  }
84
108
  }
85
109
 
@@ -137,7 +137,8 @@ When stabilize mode is active, only fix-up operations are allowed: removeNode, r
137
137
  ## Response Format
138
138
 
139
139
  Return ONLY valid JSON. No markdown, no code fences, no explanation outside the JSON structure.`;
140
- } catch {
140
+ } catch (err) {
141
+ if (process.env.WEAVER_VERBOSE) console.error('[execute-target] prompt build failed:', err);
141
142
  return FALLBACK;
142
143
  }
143
144
  }
@@ -1,5 +1,5 @@
1
1
  import type { WeaverEnv } from '../bot/types.js';
2
- import { callAI, parseJsonResponse } from '../bot/ai-client.js';
2
+ import { callAI, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
3
3
 
4
4
  /**
5
5
  * When validation fails, sends errors + context to the AI and
@@ -42,18 +42,33 @@ export async function weaverFixErrors(
42
42
  try {
43
43
  const mod = await import('../bot/system-prompt.js');
44
44
  systemPrompt = await mod.buildSystemPrompt();
45
- } catch {
45
+ } catch (err) {
46
+ if (process.env.WEAVER_VERBOSE) console.error('[fix-errors] system prompt build failed:', err);
46
47
  systemPrompt = 'You are Weaver. Return ONLY valid JSON.';
47
48
  }
48
49
 
49
50
  const errorSummary = errors.map(e => `${e.file}: ${e.errors.join(', ')}`).join('\n');
50
- const userPrompt = `The following validation errors occurred:\n${errorSummary}\n\nProvide a fix plan as JSON with "steps" and "summary". Each step needs "id", "operation", "description", and "args".`;
51
+ const userPrompt = `The following validation errors occurred:
52
+ ${errorSummary}
53
+
54
+ Provide a fix plan as JSON: {"steps": [...], "summary": "..."}
55
+
56
+ Each step MUST have: "id" (string), "operation" (string), "description" (string), "args" (object).
57
+
58
+ Available operations for fixes:
59
+ - patch-file: Surgical find-and-replace. args: { "file": "path", "patches": [{ "find": "old text", "replace": "new text" }] }
60
+ PREFERRED for fixing @input annotations. Example: { "find": "@input portName", "replace": "@input [portName]" }
61
+ - run-shell: Execute a command. args: { "command": "..." }
62
+ - read-file: Read file content. args: { "file": "path" }
63
+
64
+ Return ONLY valid JSON. No explanation outside the JSON.`;
51
65
 
52
66
  try {
53
67
  const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
54
68
 
55
- const plan = parseJsonResponse(text);
56
- console.log(`\x1b[36m→ Fix plan: ${(plan as { summary?: string }).summary ?? 'generated'}\x1b[0m`);
69
+ const parsed = parseJsonResponse(text);
70
+ const plan = normalizePlan(parsed);
71
+ console.log(`\x1b[36m→ Fix plan: ${plan.summary} (${plan.steps.length} steps)\x1b[0m`);
57
72
  return { onSuccess: true, onFailure: false, env, taskJson, fixPlanJson: JSON.stringify(plan) };
58
73
  } catch (err: unknown) {
59
74
  const msg = err instanceof Error ? err.message : String(err);
@@ -77,8 +77,9 @@ export async function genesisObserve(
77
77
  gitCommit = execFileSync('git', ['rev-parse', 'HEAD'], {
78
78
  cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
79
79
  }).trim();
80
- } catch {
80
+ } catch (err) {
81
81
  // Not a git repo or git unavailable
82
+ if (process.env.WEAVER_VERBOSE) console.error('[genesis-observe] git unavailable:', err);
82
83
  }
83
84
 
84
85
  const existingWorkflows: string[] = [];
@@ -34,7 +34,7 @@ export function genesisReport(successCtx?: string, failCtx?: string, proposeFail
34
34
  try {
35
35
  const result = JSON.parse(context.applyResultJson) as { applied: number; failed: number };
36
36
  summary += ` (applied: ${result.applied}, failed: ${result.failed})`;
37
- } catch { /* ignore */ }
37
+ } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[genesis-report] applyResultJson parse failed:', err); }
38
38
  }
39
39
  summary += elapsed ? ` [${elapsed}]` : '';
40
40
  console.log(`\n\x1b[31m${summary}\x1b[0m\n`);
@@ -1,7 +1,62 @@
1
1
  import { execFileSync } from 'node:child_process';
2
+ import * as path from 'node:path';
2
3
  import type { WeaverContext } from '../bot/types.js';
3
4
  import { auditEmit } from '../bot/audit-logger.js';
4
5
 
6
+ /**
7
+ * Review staged diff for suspicious changes before committing.
8
+ * Returns an array of issues found (empty = safe to commit).
9
+ */
10
+ function reviewStagedDiff(cwd: string): string[] {
11
+ const issues: string[] = [];
12
+
13
+ try {
14
+ const diff = execFileSync('git', ['diff', '--cached', '--stat'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
15
+ if (!diff) return issues;
16
+
17
+ // Check for large deletions (file became empty or nearly empty)
18
+ const numstat = execFileSync('git', ['diff', '--cached', '--numstat'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
19
+ for (const line of numstat.split('\n').filter(Boolean)) {
20
+ const [added, deleted, file] = line.split('\t');
21
+ if (!file || !added || !deleted) continue;
22
+ const addedN = parseInt(added, 10) || 0;
23
+ const deletedN = parseInt(deleted, 10) || 0;
24
+
25
+ // Flag: file lost >80% of its content with minimal additions
26
+ if (deletedN > 50 && addedN < 5 && deletedN > addedN * 10) {
27
+ issues.push(`${file}: deleted ${deletedN} lines, added only ${addedN} (possible truncation)`);
28
+ }
29
+
30
+ // Flag: file became empty (0 additions, all deletions)
31
+ if (addedN === 0 && deletedN > 10) {
32
+ issues.push(`${file}: file emptied (${deletedN} lines deleted, 0 added)`);
33
+ }
34
+ }
35
+
36
+ // Check for sensitive patterns in added lines
37
+ const patchDiff = execFileSync('git', ['diff', '--cached', '-U0'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
38
+ const sensitivePatterns = [
39
+ /api[_-]?key\s*[:=]\s*["'][^"']{20,}/i,
40
+ /secret\s*[:=]\s*["'][^"']{10,}/i,
41
+ /password\s*[:=]\s*["'][^"']{5,}/i,
42
+ ];
43
+ for (const line of patchDiff.split('\n')) {
44
+ if (!line.startsWith('+') || line.startsWith('+++')) continue;
45
+ for (const pattern of sensitivePatterns) {
46
+ if (pattern.test(line)) {
47
+ issues.push('Possible credential/secret in staged changes');
48
+ return issues; // One is enough to block
49
+ }
50
+ }
51
+ }
52
+ } catch (err) {
53
+ // If diff commands fail, don't block — let commit proceed
54
+ if (process.env.WEAVER_VERBOSE) console.error('[git-ops] diff review failed:', err);
55
+ }
56
+
57
+ return issues;
58
+ }
59
+
5
60
  /**
6
61
  * Git operations on created/modified files: stage, commit, branch.
7
62
  * Runs in parallel with notifications after execution.
@@ -48,25 +103,59 @@ export function weaverGitOps(ctx: string): { ctx: string } {
48
103
  }
49
104
  }
50
105
 
51
- // Stage files
106
+ // Get actually-changed files from git (avoids phantom commits)
107
+ let changedFiles: Set<string>;
108
+ try {
109
+ const diff = execFileSync('git', ['diff', '--name-only'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
110
+ const untracked = execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
111
+ changedFiles = new Set([...diff.split('\n'), ...untracked.split('\n')].filter(Boolean));
112
+ } catch (err) {
113
+ if (process.env.WEAVER_VERBOSE) console.error('[git-ops] git diff failed, using filesModified fallback:', err);
114
+ changedFiles = new Set(files); // fallback: trust filesModified
115
+ }
116
+
117
+ // Stage only files that are both in filesModified AND actually changed
118
+ let staged = 0;
52
119
  for (const file of files) {
120
+ // Resolve to relative path for comparison
121
+ const relative = path.relative(projectDir, path.resolve(projectDir, file));
122
+ if (!changedFiles.has(relative) && !changedFiles.has(file)) continue;
53
123
  try {
54
124
  execFileSync('git', ['add', file], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
125
+ staged++;
55
126
  } catch { /* ignore unstaged files */ }
56
127
  }
57
128
 
129
+ if (staged === 0) {
130
+ results.push('No actual changes to commit');
131
+ auditEmit('git-operation', { branch: gitConfig.branch, filesCount: 0, results });
132
+ context.gitResultJson = JSON.stringify({ skipped: true, reason: 'no actual changes', results });
133
+ return { ctx: JSON.stringify(context) };
134
+ }
135
+
136
+ // Diff review: check for suspicious changes before committing
137
+ const diffIssues = reviewStagedDiff(projectDir);
138
+ if (diffIssues.length > 0) {
139
+ // Unstage and skip commit — something looks wrong
140
+ try { execFileSync('git', ['reset', 'HEAD'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }); } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[git-ops] reset failed:', err); }
141
+ results.push(`Commit blocked: ${diffIssues.join('; ')}`);
142
+ auditEmit('git-operation', { branch: gitConfig.branch, filesCount: 0, results, blocked: true });
143
+ context.gitResultJson = JSON.stringify({ skipped: true, reason: 'diff review failed', issues: diffIssues, results });
144
+ return { ctx: JSON.stringify(context) };
145
+ }
146
+
58
147
  // Commit
59
148
  const prefix = gitConfig.commitPrefix ?? 'weaver:';
60
- const commitMsg = `${prefix} bot task (${files.length} file${files.length === 1 ? '' : 's'})`;
149
+ const commitMsg = `${prefix} bot task (${staged} file${staged === 1 ? '' : 's'})`;
61
150
  try {
62
151
  execFileSync('git', ['commit', '-m', commitMsg], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
63
152
  results.push(`Committed: ${commitMsg}`);
64
- console.log(`\x1b[36m→ Git: ${commitMsg}\x1b[0m`);
153
+ if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Git: ${commitMsg}\x1b[0m\n`);
65
154
  } catch {
66
155
  results.push('Nothing to commit');
67
156
  }
68
157
 
69
- auditEmit('git-operation', { branch: gitConfig.branch, filesCount: files.length, results });
158
+ auditEmit('git-operation', { branch: gitConfig.branch, filesCount: staged, results });
70
159
  context.gitResultJson = JSON.stringify({ skipped: false, results });
71
160
  return { ctx: JSON.stringify(context) };
72
161
  }
@@ -17,6 +17,8 @@ export { weaverValidateResult } from './validate-result.js';
17
17
  export { weaverFixErrors } from './fix-errors.js';
18
18
  export { weaverGitOps } from './git-ops.js';
19
19
  export { weaverBotReport } from './bot-report.js';
20
+ export { weaverAgentExecute } from './agent-execute.js';
21
+ export { weaverValidateGate } from './validate-gate.js';
20
22
  export { genesisLoadConfig } from './genesis-load-config.js';
21
23
  export { genesisObserve } from './genesis-observe.js';
22
24
  export { genesisDiffFingerprint } from './genesis-diff-fingerprint.js';
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- import type { WeaverConfig } from '../bot/types.js';
3
+ import type { BotConfig } from '../bot/types.js';
4
4
 
5
5
  /**
6
6
  * Read .weaver.json, merge with defaults, and output the config object.
@@ -13,10 +13,10 @@ import type { WeaverConfig } from '../bot/types.js';
13
13
  * @output config [order:1] - Weaver configuration
14
14
  * @output onFailure [hidden]
15
15
  */
16
- export function weaverLoadConfig(projectDir?: string): { projectDir: string; config: WeaverConfig } {
16
+ export function weaverLoadConfig(projectDir?: string): { projectDir: string; config: BotConfig } {
17
17
  const dir = projectDir || process.cwd();
18
18
  const configPath = path.join(dir, '.weaver.json');
19
- let config: WeaverConfig = { provider: 'auto' };
19
+ let config: BotConfig = { provider: 'auto' };
20
20
  if (fs.existsSync(configPath)) {
21
21
  config = { ...config, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) };
22
22
  console.log(`\x1b[36m→ Loaded config from ${configPath}\x1b[0m`);