@synergenius/flowweaver-pack-weaver 0.1.0

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 (49) hide show
  1. package/dist/bot/agent-provider.d.ts +24 -0
  2. package/dist/bot/agent-provider.d.ts.map +1 -0
  3. package/dist/bot/agent-provider.js +95 -0
  4. package/dist/bot/agent-provider.js.map +1 -0
  5. package/dist/bot/bot-agent-channel.d.ts +30 -0
  6. package/dist/bot/bot-agent-channel.d.ts.map +1 -0
  7. package/dist/bot/bot-agent-channel.js +44 -0
  8. package/dist/bot/bot-agent-channel.js.map +1 -0
  9. package/dist/bot/cli-provider.d.ts +12 -0
  10. package/dist/bot/cli-provider.d.ts.map +1 -0
  11. package/dist/bot/cli-provider.js +50 -0
  12. package/dist/bot/cli-provider.js.map +1 -0
  13. package/dist/bot/index.d.ts +11 -0
  14. package/dist/bot/index.d.ts.map +1 -0
  15. package/dist/bot/index.js +7 -0
  16. package/dist/bot/index.js.map +1 -0
  17. package/dist/bot/notifications.d.ts +18 -0
  18. package/dist/bot/notifications.d.ts.map +1 -0
  19. package/dist/bot/notifications.js +144 -0
  20. package/dist/bot/notifications.js.map +1 -0
  21. package/dist/bot/runner.d.ts +8 -0
  22. package/dist/bot/runner.d.ts.map +1 -0
  23. package/dist/bot/runner.js +123 -0
  24. package/dist/bot/runner.js.map +1 -0
  25. package/dist/bot/system-prompt.d.ts +6 -0
  26. package/dist/bot/system-prompt.d.ts.map +1 -0
  27. package/dist/bot/system-prompt.js +161 -0
  28. package/dist/bot/system-prompt.js.map +1 -0
  29. package/dist/bot/types.d.ts +44 -0
  30. package/dist/bot/types.d.ts.map +1 -0
  31. package/dist/bot/types.js +2 -0
  32. package/dist/bot/types.js.map +1 -0
  33. package/dist/docs/docs/weaver-config.md +141 -0
  34. package/dist/docs/weaver-config.md +141 -0
  35. package/dist/index.d.ts +9 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +9 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/templates/index.d.ts +5 -0
  40. package/dist/templates/index.d.ts.map +1 -0
  41. package/dist/templates/index.js +4 -0
  42. package/dist/templates/index.js.map +1 -0
  43. package/dist/templates/weaver-template.d.ts +11 -0
  44. package/dist/templates/weaver-template.d.ts.map +1 -0
  45. package/dist/templates/weaver-template.js +672 -0
  46. package/dist/templates/weaver-template.js.map +1 -0
  47. package/flowweaver.manifest.json +42 -0
  48. package/package.json +45 -0
  49. package/templates.js +1 -0
@@ -0,0 +1,672 @@
1
+ export const weaverTemplate = {
2
+ id: 'weaver',
3
+ name: 'Weaver: Autonomous workflow runner',
4
+ description: 'AI-powered runner that executes any Flow Weaver workflow autonomously, with auto-detected providers and notification support',
5
+ category: 'automation',
6
+ generate: (_opts) => {
7
+ return `import { execSync } from 'node:child_process';
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import * as os from 'node:os';
11
+
12
+ // ============================================================
13
+ // Types
14
+ // ============================================================
15
+
16
+ interface WeaverConfig {
17
+ provider?: 'auto' | 'anthropic' | 'claude-cli' | 'copilot-cli' | { name: string; model?: string; maxTokens?: number };
18
+ target?: string;
19
+ approval?: 'auto' | 'timeout-auto' | { mode: string; timeoutSeconds?: number };
20
+ notify?: Array<{ channel: 'discord' | 'slack' | 'webhook'; url: string; events?: string[]; headers?: Record<string, string> }>;
21
+ }
22
+
23
+ interface ProviderInfo {
24
+ type: 'anthropic' | 'claude-cli' | 'copilot-cli';
25
+ model?: string;
26
+ maxTokens?: number;
27
+ apiKey?: string;
28
+ }
29
+
30
+ // ============================================================
31
+ // Helpers
32
+ // ============================================================
33
+
34
+ function run(cmd: string, cwd: string): string {
35
+ return execSync(cmd, { cwd, encoding: 'utf-8', stdio: 'pipe' }).trim();
36
+ }
37
+
38
+ function runSafe(cmd: string, cwd: string): string {
39
+ try { return run(cmd, cwd); } catch { return ''; }
40
+ }
41
+
42
+ function info(msg: string): void { console.log(\`\\x1b[36m→ \${msg}\\x1b[0m\`); }
43
+ function success(msg: string): void { console.log(\`\\x1b[32m✓ \${msg}\\x1b[0m\`); }
44
+ function warn(msg: string): void { console.log(\`\\x1b[33m! \${msg}\\x1b[0m\`); }
45
+
46
+ function callCli(provider: string, prompt: string): string {
47
+ if (provider === 'claude-cli') {
48
+ return execSync('claude -p --output-format text', {
49
+ input: prompt,
50
+ encoding: 'utf-8',
51
+ stdio: ['pipe', 'pipe', 'pipe'],
52
+ timeout: 120_000,
53
+ }).trim();
54
+ }
55
+ if (provider === 'copilot-cli') {
56
+ return execSync('copilot -p --silent --allow-all-tools', {
57
+ input: prompt,
58
+ encoding: 'utf-8',
59
+ stdio: ['pipe', 'pipe', 'pipe'],
60
+ timeout: 120_000,
61
+ }).trim();
62
+ }
63
+ throw new Error(\`Unknown CLI provider: \${provider}\`);
64
+ }
65
+
66
+ async function callApiAsync(
67
+ apiKey: string,
68
+ model: string,
69
+ maxTokens: number,
70
+ systemPrompt: string,
71
+ userPrompt: string,
72
+ ): Promise<string> {
73
+ const body = JSON.stringify({
74
+ model,
75
+ max_tokens: maxTokens,
76
+ system: systemPrompt,
77
+ messages: [{ role: 'user', content: userPrompt }],
78
+ });
79
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
80
+ method: 'POST',
81
+ headers: {
82
+ 'x-api-key': apiKey,
83
+ 'anthropic-version': '2023-06-01',
84
+ 'content-type': 'application/json',
85
+ },
86
+ body,
87
+ });
88
+ if (!response.ok) {
89
+ const text = await response.text();
90
+ throw new Error(\`Anthropic API error \${response.status}: \${text.slice(0, 200)}\`);
91
+ }
92
+ const json = await response.json() as { content: Array<{ type: string; text: string }> };
93
+ return json.content[0]?.text ?? '';
94
+ }
95
+
96
+ function parseJsonResponse(text: string): Record<string, unknown> {
97
+ let cleaned = text.trim();
98
+ if (cleaned.startsWith('\\\`\\\`\\\`')) {
99
+ cleaned = cleaned.replace(/^\\\`\\\`\\\`(?:json)?\\s*\\n?/, '').replace(/\\n?\\\`\\\`\\\`\\s*$/, '');
100
+ }
101
+ try { return JSON.parse(cleaned); } catch {}
102
+ const match = cleaned.match(/\\{[\\s\\S]*\\}/);
103
+ if (match) return JSON.parse(match[0]);
104
+ throw new Error(\`Failed to parse AI response as JSON: \${text.slice(0, 200)}\`);
105
+ }
106
+
107
+ function sendWebhook(
108
+ config: { channel: string; url: string; headers?: Record<string, string> },
109
+ event: Record<string, unknown>,
110
+ ): void {
111
+ const headers: Record<string, string> = {
112
+ 'content-type': 'application/json',
113
+ ...config.headers,
114
+ };
115
+ let body: string;
116
+ if (config.channel === 'discord') {
117
+ const color = event.success ? 0x22c55e : 0xef4444;
118
+ body = JSON.stringify({
119
+ embeds: [{
120
+ title: \`Weaver: \${event.outcome ?? 'update'}\`,
121
+ description: String(event.summary ?? '').slice(0, 2000),
122
+ color,
123
+ fields: [
124
+ { name: 'Workflow', value: String(event.targetPath ?? 'unknown'), inline: true },
125
+ { name: 'Provider', value: String(event.providerType ?? 'unknown'), inline: true },
126
+ ],
127
+ timestamp: new Date().toISOString(),
128
+ }],
129
+ });
130
+ } else if (config.channel === 'slack') {
131
+ const emoji = event.success ? ':white_check_mark:' : ':x:';
132
+ body = JSON.stringify({
133
+ blocks: [
134
+ { type: 'header', text: { type: 'plain_text', text: \`\${emoji} Weaver: \${event.outcome ?? 'update'}\` } },
135
+ { type: 'section', text: { type: 'mrkdwn', text: String(event.summary ?? '').slice(0, 2000) } },
136
+ ],
137
+ });
138
+ } else {
139
+ body = JSON.stringify(event);
140
+ }
141
+ const headerFlags = Object.entries(headers).map(([k, v]) => \`-H "\${k}: \${v}"\`).join(' ');
142
+ try {
143
+ execSync(\`curl -sS -X POST \${headerFlags} -d @- "\${config.url}"\`, {
144
+ input: body,
145
+ encoding: 'utf-8',
146
+ stdio: ['pipe', 'pipe', 'pipe'],
147
+ timeout: 10_000,
148
+ });
149
+ } catch { /* notification failure is non-fatal */ }
150
+ }
151
+
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
+ async function buildWeaverPrompt(): Promise<string> {
154
+ const FALLBACK = 'You are Weaver, an expert AI companion for Flow Weaver workflows. Respond ONLY with valid JSON. No markdown, no code fences, no explanation outside the JSON structure.';
155
+
156
+ try {
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const docMeta: any = await import('@synergenius/flow-weaver/doc-metadata');
159
+
160
+ const annotations: Array<{ name: string; syntax: string; description: string; category: string }> = docMeta.ALL_ANNOTATIONS ?? [];
161
+ const portMods: Array<{ syntax: string; description: string }> = docMeta.PORT_MODIFIERS ?? [];
162
+ const nodeMods: Array<{ syntax: string; description: string }> = docMeta.NODE_MODIFIERS ?? [];
163
+ const codes: Array<{ code: string; severity: string; title: string; description: string }> = docMeta.VALIDATION_CODES ?? [];
164
+ const commands: Array<{ name: string; description: string; group?: string }> = docMeta.CLI_COMMANDS ?? [];
165
+
166
+ // Format annotations grouped by category
167
+ const groups = new Map<string, typeof annotations>();
168
+ for (const a of annotations) {
169
+ const list = groups.get(a.category) ?? [];
170
+ list.push(a);
171
+ groups.set(a.category, list);
172
+ }
173
+ const annotationLines: string[] = [];
174
+ for (const [category, items] of groups) {
175
+ annotationLines.push('[' + category + ']');
176
+ for (const item of items) annotationLines.push(' ' + item.syntax + ' -- ' + item.description);
177
+ }
178
+
179
+ // Format modifiers
180
+ const modLines: string[] = [];
181
+ if (portMods.length > 0) {
182
+ modLines.push('Port modifiers (after port name):');
183
+ for (const m of portMods) modLines.push(' ' + m.syntax + ' -- ' + m.description);
184
+ }
185
+ if (nodeMods.length > 0) {
186
+ modLines.push('Node instance modifiers (in @node declaration):');
187
+ for (const m of nodeMods) modLines.push(' ' + m.syntax + ' -- ' + m.description);
188
+ }
189
+
190
+ // Format errors (top 15)
191
+ const errors = codes.filter(c => c.severity === 'error').slice(0, 15);
192
+ const errorLines = errors.map(c => ' ' + c.code + ': ' + c.title + ' -- ' + c.description);
193
+
194
+ // Format top-level CLI commands
195
+ const topCmds = commands.filter(c => !c.group);
196
+ const cmdLines = topCmds.map(c => ' flow-weaver ' + c.name + ' -- ' + c.description);
197
+
198
+ return \`You are Weaver, an expert AI companion for Flow Weaver workflows. You have deep knowledge of the entire Flow Weaver ecosystem: annotation grammar, compilation, CLI tools, node patterns, error diagnosis, and the Genesis self-evolution protocol.
199
+
200
+ ## Core Mental Model
201
+
202
+ The code IS the workflow. Flow Weaver workflows are plain TypeScript files with JSDoc annotations above functions. The compiler reads annotations and generates deterministic execution code between @flow-weaver-body-start/end markers. Compiled code is standalone with no runtime dependency on flow-weaver.
203
+
204
+ Three block types:
205
+ - @flowWeaver nodeType: A reusable function (node) with typed inputs/outputs
206
+ - @flowWeaver workflow: A DAG orchestration that wires node instances together
207
+ - @flowWeaver pattern: A reusable fragment with boundary ports (IN/OUT instead of Start/Exit)
208
+
209
+ ## Annotation Grammar
210
+
211
+ \${annotationLines.join('\\n')}
212
+
213
+ \${modLines.join('\\n')}
214
+
215
+ ## Node Execution Model
216
+
217
+ Expression nodes (@expression):
218
+ - No execute/onSuccess/onFailure params. Just inputs and return value.
219
+ - throw = failure path, return = success path
220
+ - Synchronous. Use execSync for shell commands.
221
+ - Preferred for deterministic operations.
222
+
223
+ Standard nodes:
224
+ - execute: boolean param gates execution
225
+ - Return { onSuccess: boolean, onFailure: boolean, ...outputs }
226
+ - Can be async for I/O operations
227
+
228
+ Async agent nodes:
229
+ - Use (globalThis as any).__fw_agent_channel__ to pause workflow
230
+ - Call channel.request({ agentId, context, prompt }) which returns a Promise
231
+ - Workflow suspends until agent responds
232
+ - NOT @expression (must be async)
233
+
234
+ Pass-through pattern:
235
+ - FW auto-connects ports by matching names on adjacent nodes
236
+ - To forward data through intermediate nodes, declare it as both @input and @output with the same name
237
+ - For non-adjacent wiring, use @connect sourceNode.port -> targetNode.port
238
+
239
+ Data flow:
240
+ - @path A -> B -> C: Linear execution path (sugar for multiple @connect)
241
+ - @autoConnect: Auto-wire all nodes in declaration order
242
+ - @connect: Explicit port-to-port wiring
243
+ - Merge strategies for fan-in: FIRST, LAST, COLLECT, MERGE, CONCAT
244
+
245
+ ## CLI Commands
246
+
247
+ \${cmdLines.join('\\n')}
248
+
249
+ Key workflows:
250
+ flow-weaver compile <file> -- Generate executable code (in-place)
251
+ flow-weaver validate <file> -- Check for errors without compiling
252
+ flow-weaver modify <op> --file <f> -- Structural changes (addNode, removeNode, addConnection, removeConnection)
253
+ flow-weaver implement <file> -- Replace declare stubs with implementations
254
+ flow-weaver diff <a> <b> -- Semantic diff between two workflow versions
255
+ flow-weaver diagram <file> -f ascii-compact -- Generate ASCII diagram
256
+
257
+ ## Validation Errors
258
+
259
+ \${errorLines.join('\\n')}
260
+
261
+ When you encounter validation errors, suggest the specific fix. Common resolutions:
262
+ - UNKNOWN_NODE_TYPE: Ensure the referenced function has @flowWeaver nodeType annotation
263
+ - MISSING_REQUIRED_INPUT: Add a @connect from a source port or make the input optional with [brackets]
264
+ - UNKNOWN_SOURCE_PORT / UNKNOWN_TARGET_PORT: Check port name spelling in @connect
265
+ - CYCLE_DETECTED: Break the cycle; use scoped iteration (@scope, @map) instead of circular dependencies
266
+
267
+ ## Genesis Protocol
268
+
269
+ Genesis is a 17-step self-evolving workflow engine:
270
+ 1. Load config (.genesis/config.json with intent, focus, constraints, approval thresholds)
271
+ 2. Observe project (fingerprint: files, package.json, git, CI, tests, existing FW workflows)
272
+ 3. Load task workflow (genesis-task.ts)
273
+ 4. Diff fingerprint against last cycle
274
+ 5. Check stabilize mode (3+ rollbacks/rejections = read-only, or explicit flag)
275
+ 6. Wait for agent (YOU decide what evolutions to propose)
276
+ 7. Propose evolution (map your decisions to FwModifyOperation[])
277
+ 8. Validate proposal (budget: max 3 cost units per cycle. addNode=1, removeNode=1, addConnection=1, removeConnection=1, implementNode=2)
278
+ 9. Snapshot current task workflow for rollback
279
+ 10. Apply changes via flow-weaver CLI
280
+ 11. Compile and validate (auto-rollback on failure)
281
+ 12. Diff workflow (semantic diff)
282
+ 13. Check approval threshold (CRITICAL > BREAKING > MINOR > COSMETIC)
283
+ 14. Wait for approval (if impact >= threshold)
284
+ 15. Commit or rollback based on approval
285
+ 16. Update history (.genesis/history.json)
286
+ 17. Report summary
287
+
288
+ When stabilize mode is active, only fix-up operations are allowed: removeNode, removeConnection, implementNode. No new nodes or connections.
289
+
290
+ ## Response Format
291
+
292
+ Return ONLY valid JSON. No markdown, no code fences, no explanation outside the JSON structure. Your response must parse with JSON.parse() directly.\`;
293
+ } catch {
294
+ return FALLBACK;
295
+ }
296
+ }
297
+
298
+ // ============================================================
299
+ // Node Types
300
+ // ============================================================
301
+
302
+ /**
303
+ * @flowWeaver nodeType
304
+ * @expression
305
+ * @label Load Config
306
+ * @input [projectDir] [order:0] - Project root directory (defaults to cwd)
307
+ * @output projectDir [order:0] - Project root directory (pass-through)
308
+ * @output config [order:1] - Weaver configuration (JSON)
309
+ */
310
+ function loadConfig(projectDir?: string): { projectDir: string; config: string } {
311
+ const dir = projectDir || process.cwd();
312
+ const configPath = path.join(dir, '.weaver.json');
313
+ let config: WeaverConfig = { provider: 'auto' };
314
+ if (fs.existsSync(configPath)) {
315
+ config = { ...config, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) };
316
+ info(\`Loaded config from \${configPath}\`);
317
+ } else {
318
+ info('No .weaver.json found, using defaults (provider: auto)');
319
+ }
320
+ return { projectDir: dir, config: JSON.stringify(config) };
321
+ }
322
+
323
+ /**
324
+ * @flowWeaver nodeType
325
+ * @expression
326
+ * @label Detect Provider
327
+ * @input projectDir [order:0] - Project root directory
328
+ * @input config [order:1] - Weaver configuration (JSON)
329
+ * @output projectDir [order:0] - Project root directory (pass-through)
330
+ * @output config [order:1] - Config (pass-through)
331
+ * @output providerType [order:2] - Resolved provider type
332
+ * @output providerInfo [order:3] - Provider details (JSON)
333
+ */
334
+ function detectProvider(projectDir: string, config: string): {
335
+ projectDir: string; config: string;
336
+ providerType: string; providerInfo: string;
337
+ } {
338
+ const cfg: WeaverConfig = JSON.parse(config);
339
+ let providerSetting = cfg.provider ?? 'auto';
340
+
341
+ let type: string;
342
+ let model: string | undefined;
343
+ let maxTokens: number | undefined;
344
+
345
+ if (typeof providerSetting === 'object') {
346
+ type = providerSetting.name;
347
+ model = providerSetting.model;
348
+ maxTokens = providerSetting.maxTokens;
349
+ } else if (providerSetting !== 'auto') {
350
+ type = providerSetting;
351
+ } else {
352
+ // Auto-detection
353
+ if (process.env.ANTHROPIC_API_KEY) {
354
+ type = 'anthropic';
355
+ } else if (runSafe('which claude', projectDir)) {
356
+ type = 'claude-cli';
357
+ } else if (runSafe('which copilot', projectDir)) {
358
+ type = 'copilot-cli';
359
+ } else {
360
+ throw new Error(
361
+ 'No AI provider found. Options:\\n' +
362
+ ' 1. Set ANTHROPIC_API_KEY environment variable\\n' +
363
+ ' 2. Install Claude CLI: https://docs.anthropic.com/claude-code\\n' +
364
+ ' 3. Install GitHub Copilot CLI: https://github.com/features/copilot'
365
+ );
366
+ }
367
+ }
368
+
369
+ const providerInfo: ProviderInfo = {
370
+ type: type as ProviderInfo['type'],
371
+ model: model ?? (type === 'anthropic' ? 'claude-sonnet-4-6' : undefined),
372
+ maxTokens: maxTokens ?? (type === 'anthropic' ? 4096 : undefined),
373
+ apiKey: type === 'anthropic' ? process.env.ANTHROPIC_API_KEY : undefined,
374
+ };
375
+
376
+ if (type === 'anthropic' && !providerInfo.apiKey) {
377
+ throw new Error('Provider is "anthropic" but ANTHROPIC_API_KEY is not set');
378
+ }
379
+
380
+ const label = providerInfo.model ? \`\${type} (\${providerInfo.model})\` : type;
381
+ info(\`Provider: \${label}\`);
382
+
383
+ return {
384
+ projectDir, config,
385
+ providerType: type,
386
+ providerInfo: JSON.stringify(providerInfo),
387
+ };
388
+ }
389
+
390
+ /**
391
+ * @flowWeaver nodeType
392
+ * @expression
393
+ * @label Resolve Target
394
+ * @input projectDir [order:0] - Project root directory
395
+ * @input config [order:1] - Config (JSON)
396
+ * @input providerType [order:2] - Provider type (pass-through)
397
+ * @input providerInfo [order:3] - Provider info (pass-through)
398
+ * @output projectDir [order:0] - Project root directory (pass-through)
399
+ * @output config [order:1] - Config (pass-through)
400
+ * @output providerType [order:2] - Provider type (pass-through)
401
+ * @output providerInfo [order:3] - Provider info (pass-through)
402
+ * @output targetPath [order:4] - Absolute path to target workflow
403
+ */
404
+ function resolveTarget(
405
+ projectDir: string, config: string, providerType: string, providerInfo: string,
406
+ ): {
407
+ projectDir: string; config: string; providerType: string; providerInfo: string;
408
+ targetPath: string;
409
+ } {
410
+ const cfg: WeaverConfig = JSON.parse(config);
411
+
412
+ if (cfg.target) {
413
+ const abs = path.resolve(projectDir, cfg.target);
414
+ if (!fs.existsSync(abs)) throw new Error(\`Target workflow not found: \${abs}\`);
415
+ info(\`Target: \${abs}\`);
416
+ return { projectDir, config, providerType, providerInfo, targetPath: abs };
417
+ }
418
+
419
+ // Scan for workflow files (depth 2)
420
+ const found: string[] = [];
421
+ const scan = (dir: string, depth: number): void => {
422
+ if (depth > 2) return;
423
+ let entries: fs.Dirent[];
424
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
425
+ for (const entry of entries) {
426
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
427
+ const full = path.join(dir, entry.name);
428
+ if (entry.isDirectory()) { scan(full, depth + 1); }
429
+ else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
430
+ try {
431
+ const content = fs.readFileSync(full, 'utf-8').slice(0, 2000);
432
+ if (content.includes('@flowWeaver workflow')) found.push(full);
433
+ } catch { /* skip */ }
434
+ }
435
+ }
436
+ };
437
+ scan(projectDir, 0);
438
+
439
+ if (found.length === 0) {
440
+ throw new Error(\`No workflow files found in \${projectDir}. Set "target" in .weaver.json or pass a file path.\`);
441
+ }
442
+ if (found.length > 1) {
443
+ throw new Error(
444
+ \`Multiple workflows found. Set "target" in .weaver.json to pick one:\\n\` +
445
+ found.map(f => \` - \${path.relative(projectDir, f)}\`).join('\\n')
446
+ );
447
+ }
448
+
449
+ info(\`Target: \${found[0]}\`);
450
+ return { projectDir, config, providerType, providerInfo, targetPath: found[0]! };
451
+ }
452
+
453
+ /**
454
+ * @flowWeaver nodeType
455
+ * @label Execute Target
456
+ * @input projectDir [order:0] - Project root directory
457
+ * @input config [order:1] - Config (JSON)
458
+ * @input providerType [order:2] - Provider type
459
+ * @input providerInfo [order:3] - Provider info (JSON)
460
+ * @input targetPath [order:4] - Absolute path to target workflow
461
+ * @output projectDir [order:0] - Project root directory (pass-through)
462
+ * @output config [order:1] - Config (pass-through)
463
+ * @output targetPath [order:2] - Target path (pass-through)
464
+ * @output resultJson [order:3] - Workflow execution result (JSON)
465
+ * @output onSuccess [order:-2] - On Success
466
+ * @output onFailure [order:-1] - On Failure
467
+ */
468
+ async function executeTarget(
469
+ execute: boolean,
470
+ projectDir: string,
471
+ config: string,
472
+ providerType: string,
473
+ providerInfo: string,
474
+ targetPath: string,
475
+ ): Promise<{
476
+ onSuccess: boolean; onFailure: boolean;
477
+ projectDir: string; config: string; targetPath: string; resultJson: string;
478
+ }> {
479
+ if (!execute) {
480
+ return {
481
+ onSuccess: true, onFailure: false,
482
+ projectDir, config, targetPath,
483
+ resultJson: JSON.stringify({ success: true, summary: 'Dry run', outcome: 'skipped' }),
484
+ };
485
+ }
486
+
487
+ const pInfo: ProviderInfo = JSON.parse(providerInfo);
488
+ const cfg: WeaverConfig = JSON.parse(config);
489
+
490
+ // Build expert system prompt from flow-weaver's doc-metadata
491
+ const systemPrompt = await buildWeaverPrompt();
492
+
493
+ // Resolve approval settings
494
+ const approvalSetting = cfg.approval ?? 'auto';
495
+ const approvalMode = typeof approvalSetting === 'string' ? approvalSetting : approvalSetting.mode;
496
+
497
+ // Create agent channel
498
+ const agentChannel = {
499
+ request: async (req: { agentId: string; context: Record<string, unknown>; prompt: string }) => {
500
+ // Handle approval requests
501
+ if (req.agentId.includes('approval')) {
502
+ if (approvalMode === 'auto') {
503
+ info('Auto-approving');
504
+ return { approved: true, reason: 'auto-approved' };
505
+ }
506
+ if (approvalMode === 'timeout-auto') {
507
+ const timeout = typeof approvalSetting === 'object' ? (approvalSetting.timeoutSeconds ?? 300) : 300;
508
+ info(\`Waiting \${timeout}s before auto-approving...\`);
509
+ await new Promise(resolve => setTimeout(resolve, timeout * 1000));
510
+ return { approved: true, reason: 'timeout-auto-approved' };
511
+ }
512
+ return { approved: true, reason: 'default-approved' };
513
+ }
514
+
515
+ // Regular agent decisions
516
+ const contextStr = typeof req.context === 'string'
517
+ ? req.context
518
+ : JSON.stringify(req.context, null, 2);
519
+ const userPrompt = \`Context:\\n\${contextStr}\\n\\nInstructions:\\n\${req.prompt}\`;
520
+
521
+ let text: string;
522
+ if (pInfo.type === 'anthropic') {
523
+ text = await callApiAsync(
524
+ pInfo.apiKey!,
525
+ pInfo.model ?? 'claude-sonnet-4-6',
526
+ pInfo.maxTokens ?? 4096,
527
+ systemPrompt,
528
+ userPrompt,
529
+ );
530
+ } else {
531
+ text = callCli(pInfo.type, systemPrompt + '\\n\\n' + userPrompt);
532
+ }
533
+
534
+ return parseJsonResponse(text);
535
+ },
536
+ onPause: () => new Promise<never>(() => {}),
537
+ resume: () => {},
538
+ fail: () => {},
539
+ };
540
+
541
+ try {
542
+ info(\`Executing: \${targetPath}\`);
543
+ const startTime = Date.now();
544
+
545
+ const mod = '@synergenius/flow-weaver/dist/mcp/workflow-executor.js';
546
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
547
+ const { executeWorkflowFromFile } = await (import(mod) as Promise<any>);
548
+ const execResult = await executeWorkflowFromFile(targetPath, {}, {
549
+ agentChannel,
550
+ includeTrace: false,
551
+ production: true,
552
+ });
553
+
554
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
555
+ const result = execResult.result as { onSuccess?: boolean; summary?: string } | null;
556
+ const ok = result?.onSuccess ?? false;
557
+ const summary = result?.summary ?? 'No summary';
558
+
559
+ if (ok) success(\`Completed in \${elapsed}s: \${summary}\`);
560
+ else warn(\`Failed after \${elapsed}s: \${summary}\`);
561
+
562
+ const resultObj = {
563
+ success: ok,
564
+ summary,
565
+ outcome: ok ? 'completed' : 'failed',
566
+ functionName: execResult.functionName,
567
+ executionTime: Number(elapsed),
568
+ };
569
+
570
+ return {
571
+ onSuccess: ok, onFailure: !ok,
572
+ projectDir, config, targetPath,
573
+ resultJson: JSON.stringify(resultObj),
574
+ };
575
+ } catch (err: unknown) {
576
+ const msg = err instanceof Error ? err.message : String(err);
577
+ warn(\`Error: \${msg}\`);
578
+ return {
579
+ onSuccess: false, onFailure: true,
580
+ projectDir, config, targetPath,
581
+ resultJson: JSON.stringify({ success: false, summary: msg, outcome: 'error' }),
582
+ };
583
+ }
584
+ }
585
+
586
+ /**
587
+ * @flowWeaver nodeType
588
+ * @expression
589
+ * @label Notify Result
590
+ * @input projectDir [order:0] - Project root directory
591
+ * @input config [order:1] - Config (JSON)
592
+ * @input targetPath [order:2] - Target path
593
+ * @input resultJson [order:3] - Result (JSON)
594
+ * @output projectDir [order:0] - Project root directory (pass-through)
595
+ * @output targetPath [order:1] - Target path (pass-through)
596
+ * @output resultJson [order:2] - Result (pass-through)
597
+ */
598
+ function sendNotify(
599
+ projectDir: string, config: string, targetPath: string, resultJson: string,
600
+ ): { projectDir: string; targetPath: string; resultJson: string } {
601
+ const cfg: WeaverConfig = JSON.parse(config);
602
+ const result = JSON.parse(resultJson);
603
+ const channels = cfg.notify ?? [];
604
+
605
+ for (const ch of channels) {
606
+ const events = ch.events ?? ['workflow-complete', 'error'];
607
+ const eventType = result.success ? 'workflow-complete' : 'error';
608
+ if (!events.includes(eventType)) continue;
609
+ sendWebhook(ch, { ...result, targetPath, providerType: cfg.provider, projectDir });
610
+ }
611
+
612
+ if (channels.length > 0) info(\`Sent \${channels.length} notification(s)\`);
613
+ return { projectDir, targetPath, resultJson };
614
+ }
615
+
616
+ /**
617
+ * @flowWeaver nodeType
618
+ * @expression
619
+ * @label Report
620
+ * @input projectDir [order:0] - Project root directory
621
+ * @input targetPath [order:1] - Target workflow path
622
+ * @input resultJson [order:2] - Result (JSON)
623
+ * @output summary [order:0] - Summary string
624
+ */
625
+ function report(projectDir: string, targetPath: string, resultJson: string): { summary: string } {
626
+ const result = JSON.parse(resultJson);
627
+ const relPath = path.relative(projectDir, targetPath);
628
+ const lines = [
629
+ \`Weaver: \${result.outcome} (\${relPath})\`,
630
+ result.summary,
631
+ ];
632
+ if (result.executionTime) lines.push(\`Time: \${result.executionTime}s\`);
633
+ success(lines[0]!);
634
+ return { summary: lines.join('\\n') };
635
+ }
636
+
637
+ // ============================================================
638
+ // Workflow
639
+ // ============================================================
640
+
641
+ /**
642
+ * @flowWeaver workflow
643
+ * @node cfg loadConfig [color: "teal"] [icon: "settings"] [position: 100 0]
644
+ * @node detect detectProvider [color: "cyan"] [icon: "search"] [position: 250 0]
645
+ * @node target resolveTarget [color: "blue"] [icon: "code"] [position: 400 0]
646
+ * @node exec executeTarget [color: "purple"] [icon: "psychology"] [position: 550 0]
647
+ * @node notify sendNotify [color: "yellow"] [icon: "notifications"] [position: 700 0]
648
+ * @node rep report [color: "green"] [icon: "description"] [position: 850 0]
649
+ * @path Start -> cfg -> detect -> target -> exec -> notify -> rep -> Exit
650
+ * @position Start 0 0
651
+ * @position Exit 1000 0
652
+ * @connect rep.summary -> Exit.summary
653
+ * @param execute [order:-1] - Execute
654
+ * @param params [order:0] - Params
655
+ * @returns onSuccess [order:-2] - On Success
656
+ * @returns onFailure [order:-1] - On Failure
657
+ * @returns summary [order:0] - Summary text
658
+ */
659
+ export async function weaver(
660
+ execute: boolean,
661
+ params: Record<string, never> = {},
662
+ __abortSignal__?: AbortSignal,
663
+ ): Promise<{ onSuccess: boolean; onFailure: boolean; summary: string | null }> {
664
+ // @flow-weaver-body-start
665
+ // (auto-generated by compiler)
666
+ // @flow-weaver-body-end
667
+ return { onSuccess: false, onFailure: true, summary: null };
668
+ }
669
+ `;
670
+ },
671
+ };
672
+ //# sourceMappingURL=weaver-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weaver-template.js","sourceRoot":"","sources":["../../src/templates/weaver-template.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,cAAc,GAAqB;IAC9C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,oCAAoC;IAC1C,WAAW,EAAE,8HAA8H;IAC3I,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QAClB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAspBV,CAAC;IACA,CAAC;CACF,CAAC"}