@synergenius/flow-weaver-pack-weaver 0.9.7 → 0.9.9

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 (224) hide show
  1. package/dist/bot/agent-loop.d.ts +20 -0
  2. package/dist/bot/agent-loop.d.ts.map +1 -0
  3. package/dist/bot/agent-loop.js +331 -0
  4. package/dist/bot/agent-loop.js.map +1 -0
  5. package/dist/bot/agent-provider.d.ts.map +1 -1
  6. package/dist/bot/agent-provider.js +3 -2
  7. package/dist/bot/agent-provider.js.map +1 -1
  8. package/dist/bot/approvals.js +17 -8
  9. package/dist/bot/approvals.js.map +1 -1
  10. package/dist/bot/assistant-core.d.ts +17 -0
  11. package/dist/bot/assistant-core.d.ts.map +1 -1
  12. package/dist/bot/assistant-core.js +418 -60
  13. package/dist/bot/assistant-core.js.map +1 -1
  14. package/dist/bot/assistant-tools.d.ts +1 -1
  15. package/dist/bot/assistant-tools.d.ts.map +1 -1
  16. package/dist/bot/assistant-tools.js +283 -9
  17. package/dist/bot/assistant-tools.js.map +1 -1
  18. package/dist/bot/bot-agent-channel.d.ts.map +1 -1
  19. package/dist/bot/bot-agent-channel.js +2 -0
  20. package/dist/bot/bot-agent-channel.js.map +1 -1
  21. package/dist/bot/bot-manager.d.ts +4 -0
  22. package/dist/bot/bot-manager.d.ts.map +1 -1
  23. package/dist/bot/bot-manager.js +72 -27
  24. package/dist/bot/bot-manager.js.map +1 -1
  25. package/dist/bot/conversation-store.d.ts +6 -5
  26. package/dist/bot/conversation-store.d.ts.map +1 -1
  27. package/dist/bot/conversation-store.js +98 -42
  28. package/dist/bot/conversation-store.js.map +1 -1
  29. package/dist/bot/cost-store.d.ts +3 -0
  30. package/dist/bot/cost-store.d.ts.map +1 -1
  31. package/dist/bot/cost-store.js +21 -10
  32. package/dist/bot/cost-store.js.map +1 -1
  33. package/dist/bot/cost-tracker.d.ts.map +1 -1
  34. package/dist/bot/cost-tracker.js +14 -1
  35. package/dist/bot/cost-tracker.js.map +1 -1
  36. package/dist/bot/cron-parser.d.ts.map +1 -1
  37. package/dist/bot/cron-parser.js +2 -0
  38. package/dist/bot/cron-parser.js.map +1 -1
  39. package/dist/bot/cron-scheduler.d.ts.map +1 -1
  40. package/dist/bot/cron-scheduler.js +1 -0
  41. package/dist/bot/cron-scheduler.js.map +1 -1
  42. package/dist/bot/device-connection.d.ts +13 -0
  43. package/dist/bot/device-connection.d.ts.map +1 -0
  44. package/dist/bot/device-connection.js +102 -0
  45. package/dist/bot/device-connection.js.map +1 -0
  46. package/dist/bot/error-classifier.d.ts.map +1 -1
  47. package/dist/bot/error-classifier.js +5 -0
  48. package/dist/bot/error-classifier.js.map +1 -1
  49. package/dist/bot/file-lock.d.ts.map +1 -1
  50. package/dist/bot/file-lock.js +13 -3
  51. package/dist/bot/file-lock.js.map +1 -1
  52. package/dist/bot/file-watcher.d.ts.map +1 -1
  53. package/dist/bot/file-watcher.js +1 -0
  54. package/dist/bot/file-watcher.js.map +1 -1
  55. package/dist/bot/genesis-prompt-context.d.ts +5 -0
  56. package/dist/bot/genesis-prompt-context.d.ts.map +1 -1
  57. package/dist/bot/genesis-prompt-context.js +55 -0
  58. package/dist/bot/genesis-prompt-context.js.map +1 -1
  59. package/dist/bot/genesis-store.d.ts +4 -0
  60. package/dist/bot/genesis-store.d.ts.map +1 -1
  61. package/dist/bot/genesis-store.js +79 -12
  62. package/dist/bot/genesis-store.js.map +1 -1
  63. package/dist/bot/improve-loop.d.ts +46 -0
  64. package/dist/bot/improve-loop.d.ts.map +1 -0
  65. package/dist/bot/improve-loop.js +592 -0
  66. package/dist/bot/improve-loop.js.map +1 -0
  67. package/dist/bot/insight-engine.d.ts +12 -0
  68. package/dist/bot/insight-engine.d.ts.map +1 -0
  69. package/dist/bot/insight-engine.js +256 -0
  70. package/dist/bot/insight-engine.js.map +1 -0
  71. package/dist/bot/knowledge-store.d.ts.map +1 -1
  72. package/dist/bot/knowledge-store.js +4 -1
  73. package/dist/bot/knowledge-store.js.map +1 -1
  74. package/dist/bot/pipeline-runner.d.ts.map +1 -1
  75. package/dist/bot/pipeline-runner.js +12 -4
  76. package/dist/bot/pipeline-runner.js.map +1 -1
  77. package/dist/bot/project-model.d.ts +25 -0
  78. package/dist/bot/project-model.d.ts.map +1 -0
  79. package/dist/bot/project-model.js +372 -0
  80. package/dist/bot/project-model.js.map +1 -0
  81. package/dist/bot/response-formatter.js +2 -3
  82. package/dist/bot/response-formatter.js.map +1 -1
  83. package/dist/bot/run-store.d.ts.map +1 -1
  84. package/dist/bot/run-store.js +10 -2
  85. package/dist/bot/run-store.js.map +1 -1
  86. package/dist/bot/safe-path.d.ts +1 -1
  87. package/dist/bot/safe-path.d.ts.map +1 -1
  88. package/dist/bot/safe-path.js +20 -1
  89. package/dist/bot/safe-path.js.map +1 -1
  90. package/dist/bot/safety.d.ts +10 -2
  91. package/dist/bot/safety.d.ts.map +1 -1
  92. package/dist/bot/safety.js +45 -2
  93. package/dist/bot/safety.js.map +1 -1
  94. package/dist/bot/session-state.d.ts +4 -0
  95. package/dist/bot/session-state.d.ts.map +1 -1
  96. package/dist/bot/session-state.js +52 -9
  97. package/dist/bot/session-state.js.map +1 -1
  98. package/dist/bot/slash-commands.d.ts.map +1 -1
  99. package/dist/bot/slash-commands.js +109 -3
  100. package/dist/bot/slash-commands.js.map +1 -1
  101. package/dist/bot/steering-engine.d.ts +67 -0
  102. package/dist/bot/steering-engine.d.ts.map +1 -0
  103. package/dist/bot/steering-engine.js +198 -0
  104. package/dist/bot/steering-engine.js.map +1 -0
  105. package/dist/bot/step-executor.d.ts.map +1 -1
  106. package/dist/bot/step-executor.js +62 -25
  107. package/dist/bot/step-executor.js.map +1 -1
  108. package/dist/bot/system-prompt.d.ts.map +1 -1
  109. package/dist/bot/system-prompt.js +5 -2
  110. package/dist/bot/system-prompt.js.map +1 -1
  111. package/dist/bot/task-queue.d.ts +6 -1
  112. package/dist/bot/task-queue.d.ts.map +1 -1
  113. package/dist/bot/task-queue.js +43 -4
  114. package/dist/bot/task-queue.js.map +1 -1
  115. package/dist/bot/tool-registry.d.ts +1 -1
  116. package/dist/bot/tool-registry.d.ts.map +1 -1
  117. package/dist/bot/tool-registry.js +65 -4
  118. package/dist/bot/tool-registry.js.map +1 -1
  119. package/dist/bot/trust-calculator.d.ts +34 -0
  120. package/dist/bot/trust-calculator.d.ts.map +1 -0
  121. package/dist/bot/trust-calculator.js +67 -0
  122. package/dist/bot/trust-calculator.js.map +1 -0
  123. package/dist/bot/types.d.ts +97 -0
  124. package/dist/bot/types.d.ts.map +1 -1
  125. package/dist/bot/update-checker.d.ts +21 -0
  126. package/dist/bot/update-checker.d.ts.map +1 -0
  127. package/dist/bot/update-checker.js +129 -0
  128. package/dist/bot/update-checker.js.map +1 -0
  129. package/dist/bot/weaver-tools.d.ts.map +1 -1
  130. package/dist/bot/weaver-tools.js +11 -4
  131. package/dist/bot/weaver-tools.js.map +1 -1
  132. package/dist/cli-bridge.d.ts +2 -0
  133. package/dist/cli-bridge.d.ts.map +1 -1
  134. package/dist/cli-bridge.js +3 -1
  135. package/dist/cli-bridge.js.map +1 -1
  136. package/dist/cli-handlers.d.ts +10 -1
  137. package/dist/cli-handlers.d.ts.map +1 -1
  138. package/dist/cli-handlers.js +141 -24
  139. package/dist/cli-handlers.js.map +1 -1
  140. package/dist/cli.d.ts +3 -0
  141. package/dist/cli.d.ts.map +1 -0
  142. package/dist/cli.js +749 -0
  143. package/dist/cli.js.map +1 -0
  144. package/dist/docs/weaver-config.md +15 -9
  145. package/dist/handlers/on-execution-completed.d.ts +11 -0
  146. package/dist/handlers/on-execution-completed.d.ts.map +1 -0
  147. package/dist/handlers/on-execution-completed.js +25 -0
  148. package/dist/handlers/on-execution-completed.js.map +1 -0
  149. package/dist/mcp-tools.d.ts.map +1 -1
  150. package/dist/mcp-tools.js +33 -0
  151. package/dist/mcp-tools.js.map +1 -1
  152. package/dist/node-types/genesis-approve.d.ts.map +1 -1
  153. package/dist/node-types/genesis-approve.js +28 -3
  154. package/dist/node-types/genesis-approve.js.map +1 -1
  155. package/dist/node-types/genesis-observe.d.ts.map +1 -1
  156. package/dist/node-types/genesis-observe.js +23 -13
  157. package/dist/node-types/genesis-observe.js.map +1 -1
  158. package/dist/node-types/genesis-propose.d.ts.map +1 -1
  159. package/dist/node-types/genesis-propose.js +8 -0
  160. package/dist/node-types/genesis-propose.js.map +1 -1
  161. package/dist/node-types/genesis-update-history.d.ts.map +1 -1
  162. package/dist/node-types/genesis-update-history.js +13 -0
  163. package/dist/node-types/genesis-update-history.js.map +1 -1
  164. package/dist/templates/weaver-template.d.ts +11 -0
  165. package/dist/templates/weaver-template.d.ts.map +1 -0
  166. package/dist/templates/weaver-template.js +53 -0
  167. package/dist/templates/weaver-template.js.map +1 -0
  168. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  169. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  170. package/dist/workflows/weaver-bot-session.js +68 -0
  171. package/dist/workflows/weaver-bot-session.js.map +1 -0
  172. package/dist/workflows/weaver.d.ts +24 -0
  173. package/dist/workflows/weaver.d.ts.map +1 -0
  174. package/dist/workflows/weaver.js +28 -0
  175. package/dist/workflows/weaver.js.map +1 -0
  176. package/flowweaver.manifest.json +28 -1
  177. package/package.json +6 -3
  178. package/src/bot/agent-provider.ts +3 -2
  179. package/src/bot/approvals.ts +16 -8
  180. package/src/bot/assistant-core.ts +420 -63
  181. package/src/bot/assistant-tools.ts +291 -9
  182. package/src/bot/bot-agent-channel.ts +2 -0
  183. package/src/bot/bot-manager.ts +70 -29
  184. package/src/bot/conversation-store.ts +87 -42
  185. package/src/bot/cost-store.ts +20 -9
  186. package/src/bot/cost-tracker.ts +13 -1
  187. package/src/bot/cron-parser.ts +1 -0
  188. package/src/bot/cron-scheduler.ts +1 -0
  189. package/src/bot/device-connection.ts +102 -0
  190. package/src/bot/error-classifier.ts +5 -0
  191. package/src/bot/file-lock.ts +12 -2
  192. package/src/bot/file-watcher.ts +1 -0
  193. package/src/bot/genesis-prompt-context.ts +61 -0
  194. package/src/bot/genesis-store.ts +68 -16
  195. package/src/bot/improve-loop.ts +651 -0
  196. package/src/bot/insight-engine.ts +273 -0
  197. package/src/bot/knowledge-store.ts +4 -1
  198. package/src/bot/pipeline-runner.ts +11 -6
  199. package/src/bot/project-model.ts +404 -0
  200. package/src/bot/response-formatter.ts +2 -3
  201. package/src/bot/run-store.ts +5 -2
  202. package/src/bot/safe-path.ts +20 -1
  203. package/src/bot/safety.ts +57 -3
  204. package/src/bot/session-state.ts +47 -7
  205. package/src/bot/slash-commands.ts +111 -3
  206. package/src/bot/steering-engine.ts +233 -0
  207. package/src/bot/step-executor.ts +66 -26
  208. package/src/bot/system-prompt.ts +5 -2
  209. package/src/bot/task-queue.ts +40 -4
  210. package/src/bot/tool-registry.ts +67 -5
  211. package/src/bot/trust-calculator.ts +87 -0
  212. package/src/bot/types.ts +104 -0
  213. package/src/bot/update-checker.ts +138 -0
  214. package/src/bot/weaver-tools.ts +10 -4
  215. package/src/cli-bridge.ts +4 -1
  216. package/src/cli-handlers.ts +150 -29
  217. package/src/handlers/on-execution-completed.ts +30 -0
  218. package/src/mcp-tools.ts +38 -0
  219. package/src/node-types/genesis-approve.ts +28 -3
  220. package/src/node-types/genesis-observe.ts +23 -12
  221. package/src/node-types/genesis-propose.ts +8 -0
  222. package/src/node-types/genesis-update-history.ts +12 -0
  223. package/src/ui/evolution-panel.tsx +96 -0
  224. package/src/ui/insights-widget.tsx +77 -0
@@ -23,6 +23,17 @@ import { VERBOSE_TOOL_NAMES } from './tool-registry.js';
23
23
  import { generateToolPromptSection, generateVerboseToolList } from './tool-registry.js';
24
24
  import { CHARS_PER_TOKEN } from './safety.js';
25
25
 
26
+ export interface AssistantDebugTurn {
27
+ turn: number;
28
+ input: string;
29
+ toolCalls: Array<{ name: string; args: Record<string, unknown>; result: string; isError: boolean }>;
30
+ response: string;
31
+ tokensUsed: number;
32
+ systemPromptLength: number;
33
+ insightNudge?: string;
34
+ conversationId: string;
35
+ }
36
+
26
37
  export interface AssistantOptions {
27
38
  provider: AgentProvider;
28
39
  tools: ToolDefinition[];
@@ -37,42 +48,75 @@ export interface AssistantOptions {
37
48
  resumeId?: string;
38
49
  /** Always start a fresh conversation */
39
50
  newConversation?: boolean;
51
+ /** Debug mode: output structured NDJSON per turn, no ANSI, full conversation loop */
52
+ debug?: boolean;
40
53
  }
41
54
 
42
- const DEFAULT_SYSTEM_PROMPT = `You are Weaver Assistant — a director-level AI that manages bot workers and the flow-weaver ecosystem.
55
+ const DEFAULT_SYSTEM_PROMPT = `You are Weaver — a hands-on AI assistant for Flow Weaver projects.
56
+
57
+ You help people build, validate, debug, and manage workflows. You can also spawn autonomous bot workers that execute tasks in the background.
58
+
59
+ ## What you do
60
+
61
+ Tell me what you want to build or fix. I will:
62
+ 1. Break it into steps
63
+ 2. Use tools to read, write, validate, and test code
64
+ 3. Spawn bots for longer tasks that run in the background
65
+ 4. Track project health and surface insights proactively
66
+ 5. Propose bot workflow improvements based on execution patterns
67
+ 6. Report results — not plans
68
+
69
+ ## How to respond
43
70
 
44
- You help users with the following tools (grouped by category):
71
+ Be direct and helpful. Adapt to the user explain more for beginners, less for experts.
72
+ When someone asks "what can you do", give a SHORT overview (5-6 lines max), not an exhaustive list. Mention /help for the full reference.
73
+
74
+ USE TOOLS to fulfill requests. Don't describe what you'd do — do it.
75
+ When asked to start a bot, call bot_spawn. For status, call bot_list. For tasks, call queue_add.
76
+
77
+ ## Available tools
45
78
 
46
79
  ${generateToolPromptSection()}
47
- USE TOOLS to fulfill requests. Don't describe what you'd do — actually do it.
48
- When the user asks to "start a bot", call bot_spawn.
49
- When they ask for status, call bot_list or bot_status.
50
- When they ask to add tasks, call queue_add or queue_add_batch.
51
-
52
- Be concise. Show results, not explanations.
53
- The user is a senior engineer don't over-explain.
54
-
55
- CRITICAL: You are running in a terminal. Do NOT use markdown formatting.
56
- - No **bold**, no _italic_, no \`backticks\`, no tables with |pipes|
57
- - No emoji (✅, 🔴, etc.)
58
- - Use plain text with indentation for structure
59
- - Use UPPERCASE or quotes for emphasis instead of markdown
60
- - For lists, use simple dashes: - item
61
- - For key-value pairs, use: key: value (one per line)
62
- - Keep output scannable and clean
63
-
64
- IMPORTANT: Some tool results are displayed DIRECTLY to the user in the terminal.
65
- These tools show FULL output — the user already sees everything:
80
+
81
+ ## Terminal output rules
82
+
83
+ You are running in a terminal. Plain text only.
84
+ - No markdown: no **bold**, \`backticks\`, or |tables|
85
+ - No emoji
86
+ - Use plain dashes for lists, UPPERCASE or "quotes" for emphasis
87
+ - Keep responses concise and scannable
88
+
89
+ ## Tool output handling
90
+
91
+ CRITICAL: These tools display their FULL output directly to the user:
66
92
  ${generateVerboseToolList()}
67
- For these: do NOT repeat, summarize, or reformat the output. Just add a brief comment if needed.
68
- Never re-type ASCII art, diagrams, or large text blocks that were already printed.
93
+ The user ALREADY SEES the complete output from these tools. After calling them:
94
+ - Do NOT list, enumerate, or walk through the output
95
+ - Do NOT restate what the diagram/description shows
96
+ - ONLY add a brief insight the user cannot see (e.g. "Notice the fan-out at step 5 — that's where parallelism happens")
97
+ - If the output speaks for itself, say nothing or just "There it is."
98
+ For all other tools, you may explain the result briefly.
69
99
 
70
- Other tools show only a short preview.
71
- For those: you may summarize or explain the result as needed.`;
100
+ ## Personality
101
+
102
+ - Helpful, practical, no fluff
103
+ - Lead with the RESULT, not narration of what you did. Never start with "Let me...", "Found it.", "Now let me...", "Good, I have..."
104
+ - If something fails, say what went wrong and what you'll try next
105
+ - Never apologize for tool usage — tools are how you work
106
+ - When you don't know something, say so
107
+
108
+ ## Project intelligence
109
+
110
+ You have access to project health, bot performance, failure patterns, cost trends, and evolution history.
111
+ Be proactive: when you see something relevant to what the user is doing, mention it.
112
+ At session start, briefly acknowledge the project state if there's something worth noting.
113
+ You can propose workflow improvements with genesis_propose when patterns suggest a structural fix.
114
+ If a bot workflow needs modification and isn't ejected yet, auto-eject it first.`;
72
115
 
73
116
 
74
117
  export async function runAssistant(opts: AssistantOptions): Promise<void> {
75
118
  const { provider, tools, executor, projectDir } = opts;
119
+ const isDebug = !!opts.debug;
76
120
  const out = (s: string) => process.stderr.write(s);
77
121
 
78
122
  // Pipe mode: if stdin is not a TTY, read all input as one message
@@ -82,7 +126,10 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
82
126
  chunks.push(typeof chunk === 'string' ? chunk : chunk.toString());
83
127
  }
84
128
  const pipeInput = chunks.join('').trim();
85
- if (!pipeInput) return;
129
+ if (!pipeInput) {
130
+ process.stderr.write(' No input provided. Pipe a message: echo "describe my workflows" | flow-weaver weaver assistant\n');
131
+ return;
132
+ }
86
133
 
87
134
  // Build system prompt for pipe mode
88
135
  let systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
@@ -96,13 +143,23 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
96
143
  }
97
144
  } catch { /* plan not available */ }
98
145
 
146
+ // Inject project intelligence (ambient awareness)
147
+ try {
148
+ const { ProjectModelStore } = await import('./project-model.js');
149
+ const pms = new ProjectModelStore(projectDir);
150
+ const model = await pms.getOrBuild();
151
+ if (model && (model.health.workflows.length > 0 || model.bots.length > 0)) {
152
+ systemPrompt += '\n\n## Project Intelligence\n\n' + pms.formatSummary(model);
153
+ }
154
+ } catch { /* project model not available yet */ }
155
+
99
156
  // Run single message, print result, exit
100
157
  await runAgentLoop(provider, tools, executor, [{ role: 'user', content: pipeInput }], {
101
158
  systemPrompt, maxIterations: 20,
102
159
  onStreamEvent: (e) => { if (e.type === 'text_delta') out(e.text); },
103
160
  onToolEvent: (e) => {
104
161
  if (e.type === 'tool_call_start') out(`\n ${c.cyan('◆')} ${e.name}\n`);
105
- if (e.type === 'tool_call_result') out(` ${c.dim('→')} ${(e.result ?? '').slice(0, 200)}\n`);
162
+ if (e.type === 'tool_call_result') out(` ${c.dim('→')} ${(e.result ?? '').slice(0, 500)}\n`);
106
163
  },
107
164
  });
108
165
  out('\n');
@@ -121,12 +178,87 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
121
178
  }
122
179
  } catch { /* plan not available */ }
123
180
 
181
+ // Load steering configuration
182
+ let steeringEngine: import('./steering-engine.js').SteeringEngine | undefined;
183
+ try {
184
+ const { SteeringEngine, loadSteers } = await import('./steering-engine.js');
185
+ const steers = loadSteers(projectDir);
186
+ steeringEngine = new SteeringEngine(steers);
187
+ } catch { /* steering not available */ }
188
+
189
+ // Inject project intelligence (ambient awareness)
190
+ try {
191
+ const { ProjectModelStore } = await import('./project-model.js');
192
+ const pms = new ProjectModelStore(projectDir);
193
+ const model = await pms.getOrBuild();
194
+ if (model && (model.health.workflows.length > 0 || model.bots.length > 0)) {
195
+ systemPrompt += '\n\n## Project Intelligence\n\n' + pms.formatSummary(model);
196
+ }
197
+ } catch { /* project model not available yet */ }
198
+
199
+ // Auto-context scan: on first-ever use, scan project and persist context in knowledge store
200
+ try {
201
+ const { KnowledgeStore } = await import('./knowledge-store.js');
202
+ const knowledge = new KnowledgeStore(projectDir);
203
+ const existing = knowledge.recall('project:context');
204
+ if (existing.length === 0) {
205
+ // First-ever scan — build project context
206
+ const fsMod = await import('node:fs');
207
+ const parts: string[] = [];
208
+
209
+ // Package info
210
+ const pkgPath = path.join(projectDir, 'package.json');
211
+ if (fsMod.existsSync(pkgPath)) {
212
+ try {
213
+ const pkg = JSON.parse(fsMod.readFileSync(pkgPath, 'utf-8'));
214
+ parts.push(`Project: ${pkg.name ?? 'unknown'} v${pkg.version ?? '0.0.0'}`);
215
+ if (pkg.description) parts.push(`Description: ${pkg.description}`);
216
+ const deps = Object.keys(pkg.dependencies ?? {}).slice(0, 10);
217
+ if (deps.length > 0) parts.push(`Key deps: ${deps.join(', ')}`);
218
+ } catch { /* parse failed */ }
219
+ }
220
+
221
+ // Workflow scan
222
+ try {
223
+ const { execFileSync: scanExec } = await import('node:child_process');
224
+ const found = scanExec('npx', ['flow-weaver', 'find-workflows', '--json'], {
225
+ encoding: 'utf-8', cwd: projectDir, timeout: 10_000, stdio: ['pipe', 'pipe', 'pipe'],
226
+ });
227
+ const workflows = JSON.parse(found);
228
+ if (Array.isArray(workflows) && workflows.length > 0) {
229
+ parts.push(`Workflows (${workflows.length}): ${workflows.map((w: { name?: string; file?: string }) => w.name ?? w.file ?? 'unknown').join(', ')}`);
230
+ }
231
+ } catch { /* find-workflows failed */ }
232
+
233
+ // TypeScript config
234
+ const tsConfigPath = path.join(projectDir, 'tsconfig.json');
235
+ if (fsMod.existsSync(tsConfigPath)) parts.push('TypeScript: yes');
236
+
237
+ // Weaver config
238
+ const weaverConfigPath = path.join(projectDir, '.weaver.json');
239
+ if (fsMod.existsSync(weaverConfigPath)) {
240
+ try {
241
+ const wc = JSON.parse(fsMod.readFileSync(weaverConfigPath, 'utf-8'));
242
+ parts.push(`Weaver provider: ${typeof wc.provider === 'string' ? wc.provider : wc.provider?.name ?? 'auto'}`);
243
+ } catch { /* parse failed */ }
244
+ }
245
+
246
+ if (parts.length > 0) {
247
+ knowledge.learn('project:context', parts.join('\n'), 'auto-scan');
248
+ systemPrompt += '\n\n## Project Context (auto-scanned)\n\n' + parts.join('\n');
249
+ }
250
+ } else {
251
+ // Context already scanned — inject from knowledge store
252
+ systemPrompt += '\n\n## Project Context\n\n' + existing[0]!.value;
253
+ }
254
+ } catch { /* knowledge store not available */ }
255
+
124
256
  // Persistent conversation store
125
257
  const { ConversationStore } = await import('./conversation-store.js');
126
258
  const store = new ConversationStore();
127
259
 
128
260
  // Resolve conversation: resume, new, or auto
129
- let conversation: { id: string; title: string; messageCount: number };
261
+ let conversation: { id: string; title: string; messageCount: number; lastMessageAt: number };
130
262
  const history: AgentMessage[] = [];
131
263
 
132
264
  if (opts.resumeId) {
@@ -139,7 +271,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
139
271
  history.push(...store.loadMessages(existing.id));
140
272
  compressHistory(history);
141
273
  } else if (opts.newConversation) {
142
- conversation = store.create(projectDir);
274
+ conversation = await store.create(projectDir);
143
275
  } else {
144
276
  // Auto-resume most recent if within 1 hour, else create new
145
277
  const recent = store.getMostRecent();
@@ -148,7 +280,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
148
280
  history.push(...store.loadMessages(recent.id));
149
281
  compressHistory(history);
150
282
  } else {
151
- conversation = store.create(projectDir);
283
+ conversation = await store.create(projectDir);
152
284
  }
153
285
  }
154
286
 
@@ -162,22 +294,107 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
162
294
  try {
163
295
  const fsMod = await import('node:fs');
164
296
  const url = await import('node:url');
165
- const packPkg = JSON.parse(fsMod.readFileSync(new url.URL('../package.json', import.meta.url), 'utf-8'));
297
+ const packPkg = JSON.parse(fsMod.readFileSync(new url.URL('../../package.json', import.meta.url), 'utf-8'));
166
298
  weaverVersion = packPkg.version;
167
299
  } catch { /* not available */ }
168
300
 
169
- // Welcome
170
- out(`\n ${c.bold('weaver assistant')} ${c.dim(`v${weaverVersion}`)} ${c.dim(`· flow-weaver v${fwVersion}`)}\n`);
171
- if (process.env.FW_PLATFORM_TOKEN) {
172
- out(` ${c.dim('AI: Platform credits (no API key needed)')}\n`);
301
+ // Welcome — detect cloud status from credentials
302
+ let cloudStatus = '';
303
+ let cloudPlan = '';
304
+ try {
305
+ const credPath = path.join(os.homedir(), '.fw', 'credentials.json');
306
+ const fsMod = await import('node:fs');
307
+ if (fsMod.existsSync(credPath)) {
308
+ const creds = JSON.parse(fsMod.readFileSync(credPath, 'utf-8'));
309
+ if (creds.token && creds.expiresAt > Date.now()) {
310
+ cloudPlan = creds.plan ?? 'connected';
311
+ cloudStatus = `Cloud: ${cloudPlan}`;
312
+ } else if (creds.token) {
313
+ cloudStatus = 'Cloud: expired (run "fw login" to refresh)';
314
+ }
315
+ }
316
+ } catch { /* credentials not available */ }
317
+
318
+ // Detect first-run: scan for existing workflows and conversation count
319
+ let workflowCount = 0;
320
+ let projectName = path.basename(projectDir);
321
+ let packageDesc = '';
322
+ let isFirstRun = !conversation.title;
323
+ try {
324
+ const fsMod = await import('node:fs');
325
+ const pkgPath = path.join(projectDir, 'package.json');
326
+ if (fsMod.existsSync(pkgPath)) {
327
+ const pkg = JSON.parse(fsMod.readFileSync(pkgPath, 'utf-8'));
328
+ projectName = pkg.name ?? projectName;
329
+ packageDesc = pkg.description ?? '';
330
+ }
331
+ const { execFileSync: fwExec } = await import('node:child_process');
332
+ try {
333
+ const found = fwExec('npx', ['flow-weaver', 'find-workflows', '--json'], {
334
+ encoding: 'utf-8', cwd: projectDir, timeout: 10_000, stdio: ['pipe', 'pipe', 'pipe'],
335
+ });
336
+ const parsed = JSON.parse(found);
337
+ workflowCount = Array.isArray(parsed) ? parsed.length : (parsed.count ?? 0);
338
+ } catch { /* find-workflows not available */ }
339
+ } catch { /* scan failed */ }
340
+
341
+ if (!isDebug) {
342
+ const header = [`weaver assistant v${weaverVersion}`, `flow-weaver v${fwVersion}`];
343
+ if (cloudStatus) header.push(cloudStatus);
344
+ out(`\n ${c.bold(header[0])} ${c.dim(`· ${header.slice(1).join(' · ')}`)}\n`);
345
+ out(` ${c.dim(`Project: ${projectName}`)}\n`);
346
+ if (!cloudStatus) {
347
+ out(` ${c.dim('AI: Local (set ANTHROPIC_API_KEY or run "fw login" to connect)')}\n`);
348
+ }
349
+ if (conversation.title) {
350
+ const ago = Math.round((Date.now() - conversation.lastMessageAt) / 60000);
351
+ out(` ${c.dim(`Resuming: "${conversation.title}" (${conversation.messageCount} messages, ${ago}m ago). /new to start fresh`)}\n`);
352
+ } else if (workflowCount === 0 && isFirstRun) {
353
+ out(` ${c.dim('Welcome! No workflows found yet.')}\n`);
354
+ out(` ${c.dim('Try: "create a hello world workflow" or "what can you do?"')}\n`);
355
+ } else if (workflowCount > 0 && isFirstRun) {
356
+ out(` ${c.dim(`Found ${workflowCount} workflow${workflowCount !== 1 ? 's' : ''}. New conversation.`)}\n`);
357
+ out(` ${c.dim('Try: "validate my workflows" or "show project health"')}\n`);
358
+ } else {
359
+ out(` ${c.dim('Type your request. /help for commands.')}\n`);
360
+ }
361
+ out('\n');
173
362
  }
174
- out(` ${c.dim(`Project: ${path.basename(projectDir)}`)}\n`);
175
- if (conversation.title) {
176
- out(` ${c.dim(`Resuming: "${conversation.title}" (${conversation.messageCount} messages)`)}\n`);
177
- } else {
178
- out(` ${c.dim(`New conversation`)}\n`);
363
+
364
+ // Proactive session greeting with project status (for returning users with data)
365
+ if (!isDebug && !isFirstRun) {
366
+ try {
367
+ const { ProjectModelStore } = await import('./project-model.js');
368
+ const pms = new ProjectModelStore(projectDir);
369
+ const model = await pms.getOrBuild();
370
+ if (model && (model.health.workflows.length > 0 || model.bots.length > 0)) {
371
+ out(` ${c.dim(pms.formatSessionGreeting(model))}\n`);
372
+ }
373
+ } catch { /* project model not available yet */ }
374
+ }
375
+
376
+ // Check for updates (cached, max once per 24h, non-blocking)
377
+ if (!isDebug) {
378
+ try {
379
+ const { checkForUpdates, formatUpdateNotification } = await import('./update-checker.js');
380
+ const updates = await checkForUpdates(projectDir);
381
+ const notification = formatUpdateNotification(updates);
382
+ if (notification) {
383
+ out(` ${c.yellow('⬆')} ${c.dim(notification.split('\n')[0]!)}\n`);
384
+ systemPrompt += `\n\n## Update Available\n\n${notification}\nMention this to the user if relevant.`;
385
+ }
386
+ } catch { /* update check failed — non-fatal */ }
387
+ }
388
+
389
+ // Inject first-run context into system prompt so the assistant adapts
390
+ if (workflowCount === 0 && isFirstRun) {
391
+ systemPrompt += '\n\n## First-Run Context\n\nThis is a NEW user with NO workflows yet. Be welcoming. Suggest creating their first workflow. If they describe what they want to build, offer to create it immediately. Do NOT assume they know Flow Weaver concepts — explain briefly as you go.';
392
+ if (packageDesc) {
393
+ systemPrompt += `\nProject description: "${packageDesc}". Use this to suggest a relevant first workflow.`;
394
+ }
395
+ } else if (workflowCount > 0 && isFirstRun) {
396
+ systemPrompt += `\n\n## First Conversation Context\n\nThis user has ${workflowCount} existing workflow${workflowCount !== 1 ? 's' : ''} but this is their first conversation with you. Offer to validate, describe, or improve their workflows. Be helpful but don't assume they're a beginner.`;
179
397
  }
180
- out(` ${c.dim('Type your request. Ctrl+C to exit. /help for commands.')}\n\n`);
181
398
 
182
399
  // Rich input with history, arrows, tab completion, slash commands
183
400
  const { RichInput } = await import('./rich-input.js');
@@ -200,29 +417,70 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
200
417
  })()
201
418
  : (): Promise<string | null> => richInput!.getInput();
202
419
 
420
+ // Debug mode: collect tool calls and response text per turn
421
+ let debugToolCalls: Array<{ name: string; args: Record<string, unknown>; result: string; isError: boolean }> = [];
422
+ const debugStreamToolNames = new Map<string, string>(); // id -> name for CLI provider tool tracking
423
+ let debugResponseText = '';
424
+ let debugInsightNudge: string | undefined;
425
+ let debugTurnCount = 0;
426
+
427
+ let lastStreamType = '';
203
428
  const onStreamEvent = (event: StreamEvent) => {
204
429
  if (event.type === 'text_delta') {
205
- out(event.text);
430
+ if (lastStreamType === 'thinking_delta' && !isDebug) out('\n\n');
431
+ if (isDebug) { debugResponseText += event.text; }
432
+ else { out(event.text); }
206
433
  } else if (event.type === 'thinking_delta') {
207
- out(c.dim(event.text));
434
+ if (!isDebug) out(c.dim(event.text));
208
435
  }
436
+ // Capture tool events from stream (CLI provider handles tools via MCP,
437
+ // so onToolEvent never fires — we catch them here instead)
438
+ if (isDebug) {
439
+ const e = event as Record<string, unknown>;
440
+ if (event.type === 'tool_use_start') {
441
+ debugStreamToolNames.set(String(e.id), String(e.name));
442
+ }
443
+ if (event.type === 'tool_result') {
444
+ debugToolCalls.push({
445
+ name: debugStreamToolNames.get(String(e.id)) ?? 'unknown',
446
+ args: {},
447
+ result: String(e.result ?? '').slice(0, 2000),
448
+ isError: !!e.isError,
449
+ });
450
+ }
451
+ }
452
+ lastStreamType = event.type;
209
453
  };
210
454
 
455
+ let currentToolName = '';
456
+ let currentToolArgs: Record<string, unknown> = {};
211
457
  const onToolEvent = (event: { type: string; name: string; args?: Record<string, unknown>; result?: string; isError?: boolean }) => {
212
458
  if (event.type === 'tool_call_start') {
213
- const preview = toolPreview(event.name, event.args ?? {});
214
- out(`\n ${c.cyan('◆')} ${event.name}${preview ? c.dim(`(${preview})`) : ''}\n`);
459
+ currentToolName = event.name;
460
+ currentToolArgs = event.args ?? {};
461
+ if (!isDebug) {
462
+ const preview = toolPreview(event.name, event.args ?? {});
463
+ out(`\n ${c.cyan('◆')} ${event.name}${preview ? c.dim(`(${preview})`) : ''}\n`);
464
+ }
215
465
  }
216
466
  if (event.type === 'tool_call_result') {
217
- const icon = event.isError ? c.red('✗') : c.dim('→');
218
- const raw = event.result ?? '';
219
- // Show full output for diagram/describe/docs tools; truncate others
220
- const isVerboseTool = VERBOSE_TOOL_NAMES.has(event.name);
221
- if (isVerboseTool && raw.length > 150) {
222
- out(` ${icon}\n${raw}\n`);
467
+ if (isDebug) {
468
+ debugToolCalls.push({
469
+ name: currentToolName,
470
+ args: currentToolArgs,
471
+ result: (event.result ?? '').slice(0, 2000),
472
+ isError: !!event.isError,
473
+ });
223
474
  } else {
224
- const result = raw.replace(/\n/g, ' ').slice(0, 200);
225
- out(` ${icon} ${result}\n`);
475
+ const icon = event.isError ? c.red('✗') : c.dim('→');
476
+ const raw = event.result ?? '';
477
+ const isVerboseTool = VERBOSE_TOOL_NAMES.has(event.name);
478
+ if (isVerboseTool && raw.length > 150) {
479
+ out(` ${icon}\n${raw}\n`);
480
+ } else {
481
+ const result = raw.replace(/\n/g, ' ').slice(0, 200);
482
+ out(` ${icon} ${result}\n`);
483
+ }
226
484
  }
227
485
  }
228
486
  };
@@ -236,7 +494,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
236
494
  conversationId: conversation.id,
237
495
  onClear: () => { history.length = 0; },
238
496
  onExit: () => { shouldExit = true; },
239
- onNew: () => { history.length = 0; conversation = store.create(projectDir); },
497
+ onNew: async () => { history.length = 0; conversation = await store.create(projectDir); },
240
498
  onVerbose: () => { out(` ${c.dim('Verbose toggling not yet wired to streaming.')}\n`); },
241
499
  };
242
500
 
@@ -256,7 +514,7 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
256
514
  const parsed = JSON.parse(result);
257
515
  const errorCount = parsed.errorCount ?? parsed.errors?.length ?? 0;
258
516
  if (errorCount > 0) {
259
- out(`\n ${c.yellow('⚠')} ${filename} changed: ${errorCount} validation error(s)\n`);
517
+ out(`\n ${c.yellow('⚠')} ${opts.watchDir}/${filename}: ${errorCount} validation error(s). Ask me to fix them.\n`);
260
518
  out(` ${c.dim('Type a message to fix, or ignore.')}\n`);
261
519
  }
262
520
  } catch { /* validation failed or not a workflow — ignore */ }
@@ -265,6 +523,9 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
265
523
  } catch { /* watch not available */ }
266
524
  }
267
525
 
526
+ // Track which insights have been nudged (by ID) to avoid repetition
527
+ const nudgedInsightIds = new Set<string>();
528
+
268
529
  // Main conversation loop
269
530
  while (!shouldExit) {
270
531
  const input = await getNextInput();
@@ -273,10 +534,29 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
273
534
 
274
535
  // Handle slash commands
275
536
  if (input.startsWith('/')) {
276
- const handled = await handleSlashCommand(input, slashCtx);
277
- if (handled) continue;
278
- // Unknown slash command — tell user
279
- out(` ${c.dim('Unknown command. Type /help for available commands.')}\n\n`);
537
+ if (isDebug) {
538
+ // Capture slash command output for debug JSON
539
+ let slashOutput = '';
540
+ const debugSlashCtx = { ...slashCtx, out: (s: string) => { slashOutput += s; } };
541
+ const handled = await handleSlashCommand(input, debugSlashCtx);
542
+ if (handled) {
543
+ debugTurnCount++;
544
+ // Strip ANSI codes for clean debug output
545
+ const clean = slashOutput.replace(/\x1b\[[0-9;]*m/g, '').trim();
546
+ process.stdout.write(JSON.stringify({
547
+ turn: debugTurnCount,
548
+ input,
549
+ slashCommand: true,
550
+ response: clean,
551
+ conversationId: conversation.id,
552
+ }) + '\n');
553
+ continue;
554
+ }
555
+ } else {
556
+ const handled = await handleSlashCommand(input, slashCtx);
557
+ if (handled) continue;
558
+ }
559
+ if (!isDebug) out(` ${c.dim('Unknown command. Type /help for available commands.')}\n\n`);
280
560
  continue;
281
561
  }
282
562
 
@@ -334,20 +614,97 @@ export async function runAssistant(opts: AssistantOptions): Promise<void> {
334
614
  // Token-aware compression
335
615
  compressHistory(history);
336
616
 
617
+ // Proactive insight surfacing — update system prompt for next turn
618
+ // Max 3 nudges per session to avoid being annoying
619
+ if (nudgedInsightIds.size < 3) {
620
+ try {
621
+ const { ProjectModelStore } = await import('./project-model.js');
622
+ const { InsightEngine } = await import('./insight-engine.js');
623
+ const pms = new ProjectModelStore(projectDir);
624
+ const model = await pms.getOrBuild();
625
+ const engine = new InsightEngine();
626
+ const insights = engine.analyze(model).filter(i => i.confidence >= 0.6);
627
+
628
+ if (insights.length > 0) {
629
+ // Skip insights already nudged (by ID) or mentioned in conversation
630
+ const unsurfaced = insights.filter(insight =>
631
+ !nudgedInsightIds.has(insight.id) &&
632
+ !history.some(m =>
633
+ m.role === 'assistant' &&
634
+ typeof m.content === 'string' &&
635
+ m.content.includes(insight.title)
636
+ )
637
+ );
638
+ if (unsurfaced.length > 0) {
639
+ const top = unsurfaced[0]!;
640
+ nudgedInsightIds.add(top.id);
641
+ const nudge = `\n\n[PROACTIVE CONTEXT: ${top.title}. ${top.description}${top.suggestion ? ` Suggestion: ${top.suggestion}` : ''}. Mention this naturally if relevant, or bring it up if there's a lull.]`;
642
+ if (isDebug) debugInsightNudge = nudge;
643
+ if (!systemPrompt.includes('[PROACTIVE CONTEXT:')) {
644
+ systemPrompt += nudge;
645
+ } else {
646
+ systemPrompt = systemPrompt.replace(/\n\n\[PROACTIVE CONTEXT:.*\]/s, nudge);
647
+ }
648
+ } else {
649
+ // Remove stale nudge from system prompt
650
+ systemPrompt = systemPrompt.replace(/\n\n\[PROACTIVE CONTEXT:.*\]/s, '');
651
+ }
652
+ }
653
+ } catch { /* insights not available */ }
654
+ }
655
+
656
+ // Check steering engine for time/event-based nudges
657
+ if (steeringEngine) {
658
+ const steerMsg = steeringEngine.check();
659
+ if (steerMsg) {
660
+ // Append to system prompt for next turn
661
+ if (systemPrompt.includes('[STEER') || systemPrompt.includes('[CONTEXT NOTE]') || systemPrompt.includes('[URGENT STEER]')) {
662
+ systemPrompt = systemPrompt.replace(/\n\n\[(CONTEXT NOTE|STEER|URGENT STEER)\].*$/s, '\n\n' + steerMsg);
663
+ } else {
664
+ systemPrompt += '\n\n' + steerMsg;
665
+ }
666
+ }
667
+ }
668
+
669
+ // Debug mode: emit structured NDJSON per turn
670
+ if (isDebug) {
671
+ debugTurnCount++;
672
+ const debugOutput: AssistantDebugTurn = {
673
+ turn: debugTurnCount,
674
+ input,
675
+ toolCalls: debugToolCalls,
676
+ response: debugResponseText,
677
+ tokensUsed,
678
+ systemPromptLength: systemPrompt.length,
679
+ insightNudge: debugInsightNudge,
680
+ conversationId: conversation.id,
681
+ };
682
+ process.stdout.write(JSON.stringify(debugOutput) + '\n');
683
+ // Reset for next turn
684
+ debugToolCalls = [];
685
+ debugStreamToolNames.clear();
686
+ debugResponseText = '';
687
+ debugInsightNudge = undefined;
688
+ }
689
+
337
690
  if (!result.success && result.summary) {
338
- out(`\n ${c.red(result.summary)}\n`);
691
+ if (!isDebug) out(`\n ${c.red(result.summary)}\n`);
339
692
  }
340
693
  } catch (err: unknown) {
341
694
  const msg = err instanceof Error ? err.message : String(err);
342
- out(`\n ${c.red('Error:')} ${msg}\n`);
695
+ if (isDebug) {
696
+ process.stdout.write(JSON.stringify({ turn: ++debugTurnCount, error: msg, conversationId: conversation.id }) + '\n');
697
+ } else {
698
+ out(`\n ${c.red('Error:')} ${msg}\n`);
699
+ }
343
700
  }
344
701
 
345
- out('\n');
702
+ if (!isDebug) out('\n');
346
703
  }
347
704
 
348
705
  watcher?.close();
349
706
  richInput?.destroy();
350
- out(`\n ${c.dim('Goodbye.')}\n\n`);
707
+ if (!isDebug) out(`\n ${c.dim('Goodbye.')}\n\n`);
351
708
  }
352
709
 
353
710
  function toolPreview(name: string, args: Record<string, unknown>): string {