@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.
- package/dist/bot/agent-loop.d.ts +20 -0
- package/dist/bot/agent-loop.d.ts.map +1 -0
- package/dist/bot/agent-loop.js +331 -0
- package/dist/bot/agent-loop.js.map +1 -0
- package/dist/bot/agent-provider.d.ts.map +1 -1
- package/dist/bot/agent-provider.js +3 -2
- package/dist/bot/agent-provider.js.map +1 -1
- package/dist/bot/approvals.js +17 -8
- package/dist/bot/approvals.js.map +1 -1
- package/dist/bot/assistant-core.d.ts +17 -0
- package/dist/bot/assistant-core.d.ts.map +1 -1
- package/dist/bot/assistant-core.js +418 -60
- package/dist/bot/assistant-core.js.map +1 -1
- package/dist/bot/assistant-tools.d.ts +1 -1
- package/dist/bot/assistant-tools.d.ts.map +1 -1
- package/dist/bot/assistant-tools.js +283 -9
- package/dist/bot/assistant-tools.js.map +1 -1
- package/dist/bot/bot-agent-channel.d.ts.map +1 -1
- package/dist/bot/bot-agent-channel.js +2 -0
- package/dist/bot/bot-agent-channel.js.map +1 -1
- package/dist/bot/bot-manager.d.ts +4 -0
- package/dist/bot/bot-manager.d.ts.map +1 -1
- package/dist/bot/bot-manager.js +72 -27
- package/dist/bot/bot-manager.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +6 -5
- package/dist/bot/conversation-store.d.ts.map +1 -1
- package/dist/bot/conversation-store.js +98 -42
- package/dist/bot/conversation-store.js.map +1 -1
- package/dist/bot/cost-store.d.ts +3 -0
- package/dist/bot/cost-store.d.ts.map +1 -1
- package/dist/bot/cost-store.js +21 -10
- package/dist/bot/cost-store.js.map +1 -1
- package/dist/bot/cost-tracker.d.ts.map +1 -1
- package/dist/bot/cost-tracker.js +14 -1
- package/dist/bot/cost-tracker.js.map +1 -1
- package/dist/bot/cron-parser.d.ts.map +1 -1
- package/dist/bot/cron-parser.js +2 -0
- package/dist/bot/cron-parser.js.map +1 -1
- package/dist/bot/cron-scheduler.d.ts.map +1 -1
- package/dist/bot/cron-scheduler.js +1 -0
- package/dist/bot/cron-scheduler.js.map +1 -1
- package/dist/bot/device-connection.d.ts +13 -0
- package/dist/bot/device-connection.d.ts.map +1 -0
- package/dist/bot/device-connection.js +102 -0
- package/dist/bot/device-connection.js.map +1 -0
- package/dist/bot/error-classifier.d.ts.map +1 -1
- package/dist/bot/error-classifier.js +5 -0
- package/dist/bot/error-classifier.js.map +1 -1
- package/dist/bot/file-lock.d.ts.map +1 -1
- package/dist/bot/file-lock.js +13 -3
- package/dist/bot/file-lock.js.map +1 -1
- package/dist/bot/file-watcher.d.ts.map +1 -1
- package/dist/bot/file-watcher.js +1 -0
- package/dist/bot/file-watcher.js.map +1 -1
- package/dist/bot/genesis-prompt-context.d.ts +5 -0
- package/dist/bot/genesis-prompt-context.d.ts.map +1 -1
- package/dist/bot/genesis-prompt-context.js +55 -0
- package/dist/bot/genesis-prompt-context.js.map +1 -1
- package/dist/bot/genesis-store.d.ts +4 -0
- package/dist/bot/genesis-store.d.ts.map +1 -1
- package/dist/bot/genesis-store.js +79 -12
- package/dist/bot/genesis-store.js.map +1 -1
- package/dist/bot/improve-loop.d.ts +46 -0
- package/dist/bot/improve-loop.d.ts.map +1 -0
- package/dist/bot/improve-loop.js +592 -0
- package/dist/bot/improve-loop.js.map +1 -0
- package/dist/bot/insight-engine.d.ts +12 -0
- package/dist/bot/insight-engine.d.ts.map +1 -0
- package/dist/bot/insight-engine.js +256 -0
- package/dist/bot/insight-engine.js.map +1 -0
- package/dist/bot/knowledge-store.d.ts.map +1 -1
- package/dist/bot/knowledge-store.js +4 -1
- package/dist/bot/knowledge-store.js.map +1 -1
- package/dist/bot/pipeline-runner.d.ts.map +1 -1
- package/dist/bot/pipeline-runner.js +12 -4
- package/dist/bot/pipeline-runner.js.map +1 -1
- package/dist/bot/project-model.d.ts +25 -0
- package/dist/bot/project-model.d.ts.map +1 -0
- package/dist/bot/project-model.js +372 -0
- package/dist/bot/project-model.js.map +1 -0
- package/dist/bot/response-formatter.js +2 -3
- package/dist/bot/response-formatter.js.map +1 -1
- package/dist/bot/run-store.d.ts.map +1 -1
- package/dist/bot/run-store.js +10 -2
- package/dist/bot/run-store.js.map +1 -1
- package/dist/bot/safe-path.d.ts +1 -1
- package/dist/bot/safe-path.d.ts.map +1 -1
- package/dist/bot/safe-path.js +20 -1
- package/dist/bot/safe-path.js.map +1 -1
- package/dist/bot/safety.d.ts +10 -2
- package/dist/bot/safety.d.ts.map +1 -1
- package/dist/bot/safety.js +45 -2
- package/dist/bot/safety.js.map +1 -1
- package/dist/bot/session-state.d.ts +4 -0
- package/dist/bot/session-state.d.ts.map +1 -1
- package/dist/bot/session-state.js +52 -9
- package/dist/bot/session-state.js.map +1 -1
- package/dist/bot/slash-commands.d.ts.map +1 -1
- package/dist/bot/slash-commands.js +109 -3
- package/dist/bot/slash-commands.js.map +1 -1
- package/dist/bot/steering-engine.d.ts +67 -0
- package/dist/bot/steering-engine.d.ts.map +1 -0
- package/dist/bot/steering-engine.js +198 -0
- package/dist/bot/steering-engine.js.map +1 -0
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +62 -25
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +5 -2
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/task-queue.d.ts +6 -1
- package/dist/bot/task-queue.d.ts.map +1 -1
- package/dist/bot/task-queue.js +43 -4
- package/dist/bot/task-queue.js.map +1 -1
- package/dist/bot/tool-registry.d.ts +1 -1
- package/dist/bot/tool-registry.d.ts.map +1 -1
- package/dist/bot/tool-registry.js +65 -4
- package/dist/bot/tool-registry.js.map +1 -1
- package/dist/bot/trust-calculator.d.ts +34 -0
- package/dist/bot/trust-calculator.d.ts.map +1 -0
- package/dist/bot/trust-calculator.js +67 -0
- package/dist/bot/trust-calculator.js.map +1 -0
- package/dist/bot/types.d.ts +97 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/update-checker.d.ts +21 -0
- package/dist/bot/update-checker.d.ts.map +1 -0
- package/dist/bot/update-checker.js +129 -0
- package/dist/bot/update-checker.js.map +1 -0
- package/dist/bot/weaver-tools.d.ts.map +1 -1
- package/dist/bot/weaver-tools.js +11 -4
- package/dist/bot/weaver-tools.js.map +1 -1
- package/dist/cli-bridge.d.ts +2 -0
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +3 -1
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +10 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +141 -24
- package/dist/cli-handlers.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +749 -0
- package/dist/cli.js.map +1 -0
- package/dist/docs/weaver-config.md +15 -9
- package/dist/handlers/on-execution-completed.d.ts +11 -0
- package/dist/handlers/on-execution-completed.d.ts.map +1 -0
- package/dist/handlers/on-execution-completed.js +25 -0
- package/dist/handlers/on-execution-completed.js.map +1 -0
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +33 -0
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/genesis-approve.d.ts.map +1 -1
- package/dist/node-types/genesis-approve.js +28 -3
- package/dist/node-types/genesis-approve.js.map +1 -1
- package/dist/node-types/genesis-observe.d.ts.map +1 -1
- package/dist/node-types/genesis-observe.js +23 -13
- package/dist/node-types/genesis-observe.js.map +1 -1
- package/dist/node-types/genesis-propose.d.ts.map +1 -1
- package/dist/node-types/genesis-propose.js +8 -0
- package/dist/node-types/genesis-propose.js.map +1 -1
- package/dist/node-types/genesis-update-history.d.ts.map +1 -1
- package/dist/node-types/genesis-update-history.js +13 -0
- package/dist/node-types/genesis-update-history.js.map +1 -1
- package/dist/templates/weaver-template.d.ts +11 -0
- package/dist/templates/weaver-template.d.ts.map +1 -0
- package/dist/templates/weaver-template.js +53 -0
- package/dist/templates/weaver-template.js.map +1 -0
- package/dist/workflows/weaver-bot-session.d.ts +65 -0
- package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
- package/dist/workflows/weaver-bot-session.js +68 -0
- package/dist/workflows/weaver-bot-session.js.map +1 -0
- package/dist/workflows/weaver.d.ts +24 -0
- package/dist/workflows/weaver.d.ts.map +1 -0
- package/dist/workflows/weaver.js +28 -0
- package/dist/workflows/weaver.js.map +1 -0
- package/flowweaver.manifest.json +28 -1
- package/package.json +6 -3
- package/src/bot/agent-provider.ts +3 -2
- package/src/bot/approvals.ts +16 -8
- package/src/bot/assistant-core.ts +420 -63
- package/src/bot/assistant-tools.ts +291 -9
- package/src/bot/bot-agent-channel.ts +2 -0
- package/src/bot/bot-manager.ts +70 -29
- package/src/bot/conversation-store.ts +87 -42
- package/src/bot/cost-store.ts +20 -9
- package/src/bot/cost-tracker.ts +13 -1
- package/src/bot/cron-parser.ts +1 -0
- package/src/bot/cron-scheduler.ts +1 -0
- package/src/bot/device-connection.ts +102 -0
- package/src/bot/error-classifier.ts +5 -0
- package/src/bot/file-lock.ts +12 -2
- package/src/bot/file-watcher.ts +1 -0
- package/src/bot/genesis-prompt-context.ts +61 -0
- package/src/bot/genesis-store.ts +68 -16
- package/src/bot/improve-loop.ts +651 -0
- package/src/bot/insight-engine.ts +273 -0
- package/src/bot/knowledge-store.ts +4 -1
- package/src/bot/pipeline-runner.ts +11 -6
- package/src/bot/project-model.ts +404 -0
- package/src/bot/response-formatter.ts +2 -3
- package/src/bot/run-store.ts +5 -2
- package/src/bot/safe-path.ts +20 -1
- package/src/bot/safety.ts +57 -3
- package/src/bot/session-state.ts +47 -7
- package/src/bot/slash-commands.ts +111 -3
- package/src/bot/steering-engine.ts +233 -0
- package/src/bot/step-executor.ts +66 -26
- package/src/bot/system-prompt.ts +5 -2
- package/src/bot/task-queue.ts +40 -4
- package/src/bot/tool-registry.ts +67 -5
- package/src/bot/trust-calculator.ts +87 -0
- package/src/bot/types.ts +104 -0
- package/src/bot/update-checker.ts +138 -0
- package/src/bot/weaver-tools.ts +10 -4
- package/src/cli-bridge.ts +4 -1
- package/src/cli-handlers.ts +150 -29
- package/src/handlers/on-execution-completed.ts +30 -0
- package/src/mcp-tools.ts +38 -0
- package/src/node-types/genesis-approve.ts +28 -3
- package/src/node-types/genesis-observe.ts +23 -12
- package/src/node-types/genesis-propose.ts +8 -0
- package/src/node-types/genesis-update-history.ts +12 -0
- package/src/ui/evolution-panel.tsx +96 -0
- package/src/ui/insights-widget.tsx +77 -0
package/src/cli-handlers.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { ExecutionEvent, WeaverConfig, RunRecord, RunOutcome, RunCostSummar
|
|
|
14
14
|
import { AuditStore } from './bot/audit-store.js';
|
|
15
15
|
|
|
16
16
|
export interface ParsedArgs {
|
|
17
|
-
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'status' | 'genesis' | 'audit' | 'init' | 'assistant' | 'examples' | 'doctor';
|
|
17
|
+
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'status' | 'genesis' | 'audit' | 'init' | 'assistant' | 'examples' | 'doctor' | 'improve';
|
|
18
18
|
file?: string;
|
|
19
19
|
verbose: boolean;
|
|
20
20
|
dryRun: boolean;
|
|
@@ -69,6 +69,15 @@ export interface ParsedArgs {
|
|
|
69
69
|
assistantList?: boolean;
|
|
70
70
|
assistantDelete?: string;
|
|
71
71
|
assistantWatch?: string;
|
|
72
|
+
assistantMessage?: string;
|
|
73
|
+
assistantDebug?: boolean;
|
|
74
|
+
assistantDebugMessages?: string[];
|
|
75
|
+
// improve
|
|
76
|
+
improveMaxCycles?: number;
|
|
77
|
+
improveMaxFailures?: number;
|
|
78
|
+
improveProtected?: string[];
|
|
79
|
+
improveTestCmd?: string;
|
|
80
|
+
improveBuildCmd?: string;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
export function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -257,6 +266,8 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
257
266
|
}
|
|
258
267
|
} else if (arg === 'genesis') {
|
|
259
268
|
result.command = 'genesis';
|
|
269
|
+
} else if (arg === 'improve') {
|
|
270
|
+
result.command = 'improve';
|
|
260
271
|
} else if (arg === 'init') {
|
|
261
272
|
result.command = 'init';
|
|
262
273
|
} else if (arg === '--init') {
|
|
@@ -266,8 +277,13 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
266
277
|
} else if (arg === '--watch-dir' && i + 1 < args.length) {
|
|
267
278
|
i++;
|
|
268
279
|
result.assistantWatch = args[i];
|
|
280
|
+
} else if ((arg === '-m' || arg === '--message') && i + 1 < args.length) {
|
|
281
|
+
i++;
|
|
282
|
+
result.assistantMessage = args[i];
|
|
269
283
|
} else if (arg === '--new') {
|
|
270
284
|
result.assistantNew = true;
|
|
285
|
+
} else if (arg === '--debug') {
|
|
286
|
+
result.assistantDebug = true;
|
|
271
287
|
} else if (arg === '--resume' && i + 1 < args.length) {
|
|
272
288
|
i++;
|
|
273
289
|
result.assistantResume = args[i];
|
|
@@ -290,6 +306,21 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
290
306
|
} else if (arg === '--project-dir' && i + 1 < args.length) {
|
|
291
307
|
i++;
|
|
292
308
|
result.file = args[i];
|
|
309
|
+
} else if (arg === '--max-cycles' && i + 1 < args.length) {
|
|
310
|
+
i++;
|
|
311
|
+
result.improveMaxCycles = parseInt(args[i]!, 10) || undefined;
|
|
312
|
+
} else if (arg === '--max-failures' && i + 1 < args.length) {
|
|
313
|
+
i++;
|
|
314
|
+
result.improveMaxFailures = parseInt(args[i]!, 10) || undefined;
|
|
315
|
+
} else if (arg === '--protected' && i + 1 < args.length) {
|
|
316
|
+
i++;
|
|
317
|
+
result.improveProtected = args[i]!.split(',').map(s => s.trim());
|
|
318
|
+
} else if (arg === '--test-cmd' && i + 1 < args.length) {
|
|
319
|
+
i++;
|
|
320
|
+
result.improveTestCmd = args[i];
|
|
321
|
+
} else if (arg === '--build-cmd' && i + 1 < args.length) {
|
|
322
|
+
i++;
|
|
323
|
+
result.improveBuildCmd = args[i];
|
|
293
324
|
} else if (arg === 'run') {
|
|
294
325
|
// skip, next arg is the file
|
|
295
326
|
} else if (!arg.startsWith('-')) {
|
|
@@ -1620,7 +1651,7 @@ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
|
|
|
1620
1651
|
if (opts.assistantDelete) {
|
|
1621
1652
|
const { ConversationStore } = await import('./bot/conversation-store.js');
|
|
1622
1653
|
const store = new ConversationStore();
|
|
1623
|
-
store.delete(opts.assistantDelete);
|
|
1654
|
+
await store.delete(opts.assistantDelete);
|
|
1624
1655
|
console.log(` Deleted conversation ${opts.assistantDelete}`);
|
|
1625
1656
|
return;
|
|
1626
1657
|
}
|
|
@@ -1659,7 +1690,14 @@ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
|
|
|
1659
1690
|
}) as import('@synergenius/flow-weaver/agent').AgentProvider;
|
|
1660
1691
|
} else if (providerType === 'anthropic' || (providerType === 'auto' && process.env.ANTHROPIC_API_KEY)) {
|
|
1661
1692
|
const apiKey = process.env.ANTHROPIC_API_KEY ?? (typeof providerSetting === 'object' ? (providerSetting as { apiKey?: string }).apiKey : undefined);
|
|
1662
|
-
if (!apiKey) {
|
|
1693
|
+
if (!apiKey) {
|
|
1694
|
+
console.error('\n No API key found.\n');
|
|
1695
|
+
console.error(' Set one of:');
|
|
1696
|
+
console.error(' export ANTHROPIC_API_KEY=sk-...');
|
|
1697
|
+
console.error(' fw login (platform credentials)');
|
|
1698
|
+
console.error(' flow-weaver weaver init (auto-detect provider)\n');
|
|
1699
|
+
process.exit(1);
|
|
1700
|
+
}
|
|
1663
1701
|
provider = createAnthropicProvider({ apiKey, model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
|
|
1664
1702
|
} else {
|
|
1665
1703
|
provider = createClaudeCliProvider({ model: typeof providerSetting === 'object' ? providerSetting.model : undefined });
|
|
@@ -1669,6 +1707,51 @@ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
|
|
|
1669
1707
|
const { runAssistant } = await import('./bot/assistant-core.js');
|
|
1670
1708
|
const executor = createAssistantExecutor(projectDir);
|
|
1671
1709
|
|
|
1710
|
+
// Debug mode: pass messages as inputMessages, enable structured output
|
|
1711
|
+
// Must be checked BEFORE single-message mode since both use -m flag
|
|
1712
|
+
if (opts.assistantDebug) {
|
|
1713
|
+
const messages = opts.assistantMessage ? [opts.assistantMessage] : [];
|
|
1714
|
+
// Read additional messages from stdin if not a TTY
|
|
1715
|
+
if (!process.stdin.isTTY) {
|
|
1716
|
+
const chunks: string[] = [];
|
|
1717
|
+
for await (const chunk of process.stdin) {
|
|
1718
|
+
chunks.push(typeof chunk === 'string' ? chunk : chunk.toString());
|
|
1719
|
+
}
|
|
1720
|
+
const stdinMessages = chunks.join('').trim().split('\n').filter(Boolean);
|
|
1721
|
+
messages.push(...stdinMessages);
|
|
1722
|
+
}
|
|
1723
|
+
if (messages.length === 0) {
|
|
1724
|
+
console.error('Debug mode requires messages via -m "msg" or stdin (one per line)');
|
|
1725
|
+
process.exit(1);
|
|
1726
|
+
}
|
|
1727
|
+
await runAssistant({
|
|
1728
|
+
provider,
|
|
1729
|
+
tools: ASSISTANT_TOOLS,
|
|
1730
|
+
executor,
|
|
1731
|
+
projectDir,
|
|
1732
|
+
inputMessages: messages,
|
|
1733
|
+
resumeId: opts.assistantResume,
|
|
1734
|
+
newConversation: !opts.assistantResume,
|
|
1735
|
+
debug: true,
|
|
1736
|
+
});
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// Single message mode: -m "message" — run one message, print result, exit
|
|
1741
|
+
if (opts.assistantMessage) {
|
|
1742
|
+
const { runAgentLoop } = await import('@synergenius/flow-weaver/agent');
|
|
1743
|
+
const result = await runAgentLoop(provider, ASSISTANT_TOOLS, executor, [{ role: 'user', content: opts.assistantMessage }], {
|
|
1744
|
+
maxIterations: 20,
|
|
1745
|
+
onStreamEvent: (e) => { if (e.type === 'text_delta') process.stdout.write(e.text); },
|
|
1746
|
+
onToolEvent: (e) => {
|
|
1747
|
+
if (e.type === 'tool_call_start') process.stderr.write(`\n ◆ ${e.name}\n`);
|
|
1748
|
+
if (e.type === 'tool_call_result') process.stderr.write(` → ${(e.result ?? '').replace(/\n/g, ' ').slice(0, 200)}\n`);
|
|
1749
|
+
},
|
|
1750
|
+
});
|
|
1751
|
+
process.stdout.write('\n');
|
|
1752
|
+
process.exit(result.success ? 0 : 1);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1672
1755
|
await runAssistant({
|
|
1673
1756
|
provider,
|
|
1674
1757
|
tools: ASSISTANT_TOOLS,
|
|
@@ -1965,6 +2048,9 @@ const COMMAND_HELP: Record<string, string> = {
|
|
|
1965
2048
|
eject: 'eject [--workflow bot|genesis] Export managed workflows',
|
|
1966
2049
|
audit: 'audit [runId] [--limit N] View audit log',
|
|
1967
2050
|
assistant: 'assistant AI-powered assistant for managing bots and workflows',
|
|
2051
|
+
examples: 'examples Show example tasks and commands',
|
|
2052
|
+
doctor: 'doctor Validate setup and check connectivity',
|
|
2053
|
+
status: 'status Get bot session status',
|
|
1968
2054
|
};
|
|
1969
2055
|
|
|
1970
2056
|
export function printHelp(command?: string): void {
|
|
@@ -1975,26 +2061,50 @@ export function printHelp(command?: string): void {
|
|
|
1975
2061
|
}
|
|
1976
2062
|
|
|
1977
2063
|
console.log(`
|
|
1978
|
-
Weaver — AI-powered workflow automation for Flow Weaver
|
|
1979
|
-
|
|
1980
|
-
Usage: flow-weaver weaver <command> [options]
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2064
|
+
Weaver — AI-powered workflow automation for Flow Weaver
|
|
2065
|
+
|
|
2066
|
+
Usage: flow-weaver weaver <command> [options]
|
|
2067
|
+
|
|
2068
|
+
Get started:
|
|
2069
|
+
assistant AI assistant for managing bots and workflows
|
|
2070
|
+
bot <task> Create or modify a workflow from a task
|
|
2071
|
+
init Create .weaver.json config
|
|
2072
|
+
doctor Validate setup and check connectivity
|
|
2073
|
+
|
|
2074
|
+
Workflows:
|
|
2075
|
+
run <file> Execute a workflow with AI agent channel
|
|
2076
|
+
watch <file> Re-run on file changes
|
|
2077
|
+
genesis [--init] Self-evolution cycle on a workflow
|
|
2078
|
+
|
|
2079
|
+
Management:
|
|
2080
|
+
session Start continuous task queue processing
|
|
2081
|
+
queue <cmd> [task] Manage task queue (add, list, clear, remove)
|
|
2082
|
+
status Get bot session status
|
|
2083
|
+
steer <cmd> Control a running session (pause, resume, cancel)
|
|
2084
|
+
|
|
2085
|
+
Info:
|
|
2086
|
+
history [id] Show execution history
|
|
2087
|
+
costs [--since 7d] Show AI token usage and costs
|
|
2088
|
+
audit [runId] View audit log
|
|
2089
|
+
examples Show example tasks and commands
|
|
2090
|
+
providers List available AI providers
|
|
2091
|
+
|
|
2092
|
+
Advanced:
|
|
2093
|
+
pipeline <config.json> Run multi-stage pipeline
|
|
2094
|
+
cron <schedule> <file> Schedule workflow execution
|
|
2095
|
+
dashboard [file] Start live execution dashboard
|
|
2096
|
+
eject [--workflow name] Export managed workflows
|
|
2097
|
+
|
|
2098
|
+
Options:
|
|
2099
|
+
-h, --help Show help
|
|
2100
|
+
-v, --verbose Show detailed output
|
|
2101
|
+
-c, --config <path> Path to .weaver.json config
|
|
2102
|
+
--approval <mode> Approval mode (auto|prompt|web|timeout-auto)
|
|
2103
|
+
|
|
2104
|
+
Quick start:
|
|
2105
|
+
flow-weaver weaver init
|
|
2106
|
+
flow-weaver weaver bot "Create a hello world workflow"
|
|
2107
|
+
flow-weaver weaver assistant
|
|
1998
2108
|
`);
|
|
1999
2109
|
}
|
|
2000
2110
|
|
|
@@ -2123,16 +2233,27 @@ export async function handleInit(opts: ParsedArgs): Promise<void> {
|
|
|
2123
2233
|
console.log(` provider: ${provider}`);
|
|
2124
2234
|
console.log(' approval: auto');
|
|
2125
2235
|
console.log('');
|
|
2126
|
-
console.log('
|
|
2127
|
-
console.log(' \x1b[36mweaver
|
|
2128
|
-
console.log(' \x1b[36mweaver assistant\x1b[0m');
|
|
2236
|
+
console.log(' \x1b[1mNext step:\x1b[0m');
|
|
2237
|
+
console.log(' \x1b[36mweaver assistant\x1b[0m \x1b[2m# start your AI companion\x1b[0m');
|
|
2129
2238
|
console.log('');
|
|
2130
|
-
console.log('
|
|
2131
|
-
console.log('
|
|
2132
|
-
console.log(' weaver doctor \x1b[2m# validate your setup\x1b[0m');
|
|
2239
|
+
console.log(' The assistant will read your project and help you create');
|
|
2240
|
+
console.log(' your first workflow. Just tell it what you want to build.');
|
|
2133
2241
|
console.log('');
|
|
2134
2242
|
}
|
|
2135
2243
|
|
|
2244
|
+
export async function handleImprove(opts: ParsedArgs): Promise<void> {
|
|
2245
|
+
const projectDir = opts.file ? path.resolve(opts.file) : process.cwd();
|
|
2246
|
+
const { runImproveLoop, DEFAULT_PROTECTED } = await import('./bot/improve-loop.js');
|
|
2247
|
+
await runImproveLoop({
|
|
2248
|
+
maxCycles: opts.improveMaxCycles ?? 0,
|
|
2249
|
+
maxConsecutiveFailures: opts.improveMaxFailures ?? 5,
|
|
2250
|
+
protectedPatterns: opts.improveProtected ?? DEFAULT_PROTECTED,
|
|
2251
|
+
testCommand: opts.improveTestCmd ?? 'npx vitest run',
|
|
2252
|
+
buildCommand: opts.improveBuildCmd,
|
|
2253
|
+
projectDir,
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2136
2257
|
export async function handleExamples(_opts: ParsedArgs): Promise<void> {
|
|
2137
2258
|
console.log('');
|
|
2138
2259
|
console.log(' \x1b[1mWeaver Examples\x1b[0m');
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform event handler: invalidates project model cache when executions complete.
|
|
3
|
+
* Registered in flowweaver.manifest.json as an event subscription.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'node:crypto';
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as os from 'node:os';
|
|
10
|
+
|
|
11
|
+
export async function onExecutionCompleted(event: {
|
|
12
|
+
deploymentId?: string;
|
|
13
|
+
wsPath?: string;
|
|
14
|
+
status?: string;
|
|
15
|
+
executionTimeMs?: number;
|
|
16
|
+
}): Promise<void> {
|
|
17
|
+
const projectDir = event.wsPath;
|
|
18
|
+
if (!projectDir) return;
|
|
19
|
+
|
|
20
|
+
// Invalidate project model cache directly (same logic as ProjectModelStore.invalidate)
|
|
21
|
+
try {
|
|
22
|
+
const hash8 = crypto.createHash('sha256').update(projectDir).digest('hex').slice(0, 8);
|
|
23
|
+
const modelPath = path.join(os.homedir(), '.weaver', 'projects', hash8, 'model.json');
|
|
24
|
+
if (fs.existsSync(modelPath)) {
|
|
25
|
+
fs.unlinkSync(modelPath);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// Non-fatal — cache will be rebuilt on next access
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/mcp-tools.ts
CHANGED
|
@@ -271,4 +271,42 @@ export async function registerMcpTools(mcp: McpServer): Promise<void> {
|
|
|
271
271
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
272
272
|
},
|
|
273
273
|
);
|
|
274
|
+
|
|
275
|
+
mcp.tool(
|
|
276
|
+
'fw_weaver_insights',
|
|
277
|
+
'Get project health, insights, trust level, and cost summary. Returns structured JSON with actionable recommendations.',
|
|
278
|
+
{
|
|
279
|
+
projectDir: z.string().describe('Project directory path'),
|
|
280
|
+
},
|
|
281
|
+
async (args) => {
|
|
282
|
+
try {
|
|
283
|
+
const { ProjectModelStore } = await import('./bot/project-model.js');
|
|
284
|
+
const { InsightEngine } = await import('./bot/insight-engine.js');
|
|
285
|
+
const pms = new ProjectModelStore(args.projectDir as string);
|
|
286
|
+
const model = await pms.getOrBuild();
|
|
287
|
+
const engine = new InsightEngine();
|
|
288
|
+
const insights = engine.analyze(model);
|
|
289
|
+
return {
|
|
290
|
+
content: [{
|
|
291
|
+
type: 'text',
|
|
292
|
+
text: JSON.stringify({
|
|
293
|
+
health: model.health,
|
|
294
|
+
bots: model.bots,
|
|
295
|
+
insights: insights.slice(0, 10),
|
|
296
|
+
trust: model.trust,
|
|
297
|
+
cost: model.cost,
|
|
298
|
+
evolution: {
|
|
299
|
+
totalCycles: model.evolution.totalCycles,
|
|
300
|
+
successRate: model.evolution.successRate,
|
|
301
|
+
},
|
|
302
|
+
}, null, 2),
|
|
303
|
+
}],
|
|
304
|
+
};
|
|
305
|
+
} catch (err: unknown) {
|
|
306
|
+
return {
|
|
307
|
+
content: [{ type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
);
|
|
274
312
|
}
|
|
@@ -66,8 +66,33 @@ export async function genesisApprove(
|
|
|
66
66
|
return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// Non-auto modes
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Non-auto modes: try interactive approval, fall back to reject
|
|
70
|
+
let approved = false;
|
|
71
|
+
let rejectionReason = 'Non-interactive environment';
|
|
72
|
+
|
|
73
|
+
if (approvalMode === 'prompt') {
|
|
74
|
+
try {
|
|
75
|
+
const readline = await import('node:readline');
|
|
76
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
77
|
+
const answer = await new Promise<string>(resolve => {
|
|
78
|
+
rl.question('\x1b[33m? Approve this proposal? (y/N/reason): \x1b[0m', resolve);
|
|
79
|
+
});
|
|
80
|
+
rl.close();
|
|
81
|
+
const trimmed = answer.trim().toLowerCase();
|
|
82
|
+
if (trimmed === 'y' || trimmed === 'yes') {
|
|
83
|
+
approved = true;
|
|
84
|
+
} else {
|
|
85
|
+
rejectionReason = trimmed && trimmed !== 'n' && trimmed !== 'no' ? answer.trim() : 'User rejected';
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Non-interactive — fall through to reject
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
context.approved = approved;
|
|
93
|
+
context.rejectionReason = approved ? undefined : rejectionReason;
|
|
94
|
+
console.log(approved
|
|
95
|
+
? '\x1b[32m→ Approved by user\x1b[0m'
|
|
96
|
+
: `\x1b[33m→ Rejected: ${rejectionReason}\x1b[0m`);
|
|
72
97
|
return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
|
|
73
98
|
}
|
|
@@ -46,22 +46,28 @@ export async function genesisObserve(
|
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
48
|
const files: Record<string, string> = {};
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
if (fs.existsSync(srcDir) && fs.statSync(srcDir).isDirectory()) dirsToScan.push(srcDir);
|
|
49
|
+
const fileContents: Record<string, string> = {};
|
|
50
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'coverage', '.next', '.turbo']);
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
function walkDir(dir: string): void {
|
|
54
53
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
55
54
|
for (const entry of entries) {
|
|
56
|
-
if (entry.
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
if (!SKIP_DIRS.has(entry.name)) {
|
|
57
|
+
walkDir(path.join(dir, entry.name));
|
|
58
|
+
}
|
|
59
|
+
} else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
57
60
|
const filePath = path.join(dir, entry.name);
|
|
58
61
|
const relPath = path.relative(projectDir, filePath);
|
|
59
62
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
63
|
+
fileContents[relPath] = content;
|
|
60
64
|
files[relPath] = crypto.createHash('sha256').update(content).digest('hex');
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
walkDir(projectDir);
|
|
70
|
+
|
|
65
71
|
let packageJson: Record<string, unknown> | null = null;
|
|
66
72
|
const pkgPath = path.join(projectDir, 'package.json');
|
|
67
73
|
if (fs.existsSync(pkgPath)) {
|
|
@@ -83,17 +89,20 @@ export async function genesisObserve(
|
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
const existingWorkflows: string[] = [];
|
|
86
|
-
for (const [relPath,
|
|
87
|
-
const filePath = path.join(projectDir, relPath);
|
|
88
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
92
|
+
for (const [relPath, content] of Object.entries(fileContents)) {
|
|
89
93
|
if (content.includes('@flowWeaver workflow')) {
|
|
90
94
|
existingWorkflows.push(relPath);
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
let workflowHash = '';
|
|
99
|
+
let targetPath = '';
|
|
100
|
+
if (config.targetWorkflow) {
|
|
101
|
+
targetPath = path.resolve(projectDir, config.targetWorkflow);
|
|
102
|
+
const targetRelPath = path.relative(projectDir, targetPath);
|
|
103
|
+
const targetContent = fileContents[targetRelPath] ?? fs.readFileSync(targetPath, 'utf-8');
|
|
104
|
+
workflowHash = crypto.createHash('sha256').update(targetContent).digest('hex');
|
|
105
|
+
}
|
|
97
106
|
|
|
98
107
|
const fingerprint: GenesisFingerprint = {
|
|
99
108
|
timestamp: new Date().toISOString(),
|
|
@@ -108,7 +117,9 @@ export async function genesisObserve(
|
|
|
108
117
|
console.log(`\x1b[36m→ Fingerprint: ${Object.keys(files).length} files, ${existingWorkflows.length} workflows\x1b[0m`);
|
|
109
118
|
|
|
110
119
|
context.fingerprintJson = JSON.stringify(fingerprint);
|
|
111
|
-
|
|
120
|
+
if (targetPath) {
|
|
121
|
+
context.workflowDescription = await getWorkflowDescription(targetPath);
|
|
122
|
+
}
|
|
112
123
|
|
|
113
124
|
return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
|
|
114
125
|
} catch (err: unknown) {
|
|
@@ -40,10 +40,18 @@ export async function genesisPropose(
|
|
|
40
40
|
graceRemaining: context.escrowGraceRemaining ?? 0,
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
// Inject project intelligence
|
|
44
|
+
let insightContext = '';
|
|
45
|
+
try {
|
|
46
|
+
const { getGenesisInsightContext } = await import('../bot/genesis-prompt-context.js');
|
|
47
|
+
insightContext = await getGenesisInsightContext(env.projectDir);
|
|
48
|
+
} catch { /* insights not available */ }
|
|
49
|
+
|
|
43
50
|
const userPrompt = [
|
|
44
51
|
'## Current Workflow Structure',
|
|
45
52
|
context.workflowDescription || '(no description available)',
|
|
46
53
|
'',
|
|
54
|
+
...(insightContext ? [insightContext, ''] : []),
|
|
47
55
|
'## Project Diff Since Last Cycle',
|
|
48
56
|
JSON.stringify(diff, null, 2),
|
|
49
57
|
'',
|
|
@@ -60,6 +60,18 @@ export function genesisUpdateHistory(ctx: string): { ctx: string } {
|
|
|
60
60
|
if (fingerprint) {
|
|
61
61
|
store.saveFingerprint(fingerprint);
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
// Invalidate project model cache so next access rebuilds with fresh data
|
|
65
|
+
try {
|
|
66
|
+
// Use sync require-like approach: delete the cached model.json directly
|
|
67
|
+
const crypto = require('node:crypto') as typeof import('node:crypto');
|
|
68
|
+
const fsMod = require('node:fs') as typeof import('node:fs');
|
|
69
|
+
const pathMod = require('node:path') as typeof import('node:path');
|
|
70
|
+
const osMod = require('node:os') as typeof import('node:os');
|
|
71
|
+
const hash8 = crypto.createHash('sha256').update(env.projectDir).digest('hex').slice(0, 8);
|
|
72
|
+
const modelPath = pathMod.join(osMod.homedir(), '.weaver', 'projects', hash8, 'model.json');
|
|
73
|
+
if (fsMod.existsSync(modelPath)) fsMod.unlinkSync(modelPath);
|
|
74
|
+
} catch { /* project model not available */ }
|
|
63
75
|
} catch (err: unknown) {
|
|
64
76
|
const msg = err instanceof Error ? err.message : String(err);
|
|
65
77
|
console.error(`\x1b[31m→ Failed to save history: ${msg}\x1b[0m`);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weaver Evolution Panel — shows Genesis cycle history and operation effectiveness.
|
|
3
|
+
* Loaded dynamically by the platform's pack UI contributions loader.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
interface EvolutionData {
|
|
9
|
+
evolution: {
|
|
10
|
+
totalCycles: number;
|
|
11
|
+
successRate: number;
|
|
12
|
+
byOperationType: Record<string, { proposed: number; applied: number; effectiveness: number }>;
|
|
13
|
+
recentCycles: Array<{ id: string; outcome: string; proposal?: { summary: string; impactLevel: string } }>;
|
|
14
|
+
};
|
|
15
|
+
trust: { phase: number; score: number };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function WeaverEvolutionPanel({ projectDir }: { projectDir?: string }) {
|
|
19
|
+
const [data, setData] = useState<EvolutionData | null>(null);
|
|
20
|
+
const [error, setError] = useState<string | null>(null);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!projectDir) return;
|
|
24
|
+
fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`)
|
|
25
|
+
.then(r => r.json())
|
|
26
|
+
.then(setData)
|
|
27
|
+
.catch(e => setError(e.message));
|
|
28
|
+
}, [projectDir]);
|
|
29
|
+
|
|
30
|
+
if (error) return <div style={{ padding: 16, color: '#ef4444' }}>Error: {error}</div>;
|
|
31
|
+
if (!data) return <div style={{ padding: 16, opacity: 0.5 }}>Loading evolution data...</div>;
|
|
32
|
+
|
|
33
|
+
const { evolution, trust } = data;
|
|
34
|
+
const outcomeColor: Record<string, string> = {
|
|
35
|
+
applied: '#22c55e',
|
|
36
|
+
'rolled-back': '#ef4444',
|
|
37
|
+
rejected: '#f59e0b',
|
|
38
|
+
'no-change': '#6b7280',
|
|
39
|
+
error: '#ef4444',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13 }}>
|
|
44
|
+
<div style={{ marginBottom: 16 }}>
|
|
45
|
+
<div style={{ fontWeight: 600, marginBottom: 4 }}>Trust Level</div>
|
|
46
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
|
|
47
|
+
<span style={{ fontSize: 20, fontWeight: 700 }}>Phase {trust.phase}</span>
|
|
48
|
+
<span style={{ opacity: 0.5 }}>Score: {trust.score}/100</span>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div style={{ marginBottom: 16 }}>
|
|
53
|
+
<div style={{ fontWeight: 600, marginBottom: 4 }}>
|
|
54
|
+
Genesis Cycles: {evolution.totalCycles} ({evolution.totalCycles > 0 ? Math.round(evolution.successRate * 100) : 0}% success)
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{Object.keys(evolution.byOperationType).length > 0 && (
|
|
59
|
+
<div style={{ marginBottom: 16 }}>
|
|
60
|
+
<div style={{ fontWeight: 600, marginBottom: 4 }}>Operation Effectiveness</div>
|
|
61
|
+
{Object.entries(evolution.byOperationType).map(([op, stats]) => (
|
|
62
|
+
<div key={op} style={{ padding: '2px 0', display: 'flex', justifyContent: 'space-between' }}>
|
|
63
|
+
<span>{op}</span>
|
|
64
|
+
<span style={{ opacity: 0.7 }}>{Math.round(stats.effectiveness * 100)}% ({stats.applied}/{stats.proposed})</span>
|
|
65
|
+
</div>
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{evolution.recentCycles.length > 0 && (
|
|
71
|
+
<div>
|
|
72
|
+
<div style={{ fontWeight: 600, marginBottom: 4 }}>Recent Cycles</div>
|
|
73
|
+
{evolution.recentCycles.slice(-5).reverse().map((cycle) => (
|
|
74
|
+
<div key={cycle.id} style={{ padding: '4px 0', borderBottom: '1px solid rgba(128,128,128,0.1)' }}>
|
|
75
|
+
<span style={{ color: outcomeColor[cycle.outcome] ?? '#6b7280', fontWeight: 600, marginRight: 8 }}>
|
|
76
|
+
{cycle.outcome}
|
|
77
|
+
</span>
|
|
78
|
+
<span style={{ opacity: 0.7 }}>{cycle.id}</span>
|
|
79
|
+
{cycle.proposal && (
|
|
80
|
+
<div style={{ opacity: 0.6, paddingLeft: 8 }}>{cycle.proposal.summary}</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{evolution.totalCycles === 0 && (
|
|
88
|
+
<div style={{ opacity: 0.5, textAlign: 'center', padding: 20 }}>
|
|
89
|
+
No genesis cycles yet. Use /genesis in the assistant to start evolving bot workflows.
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default WeaverEvolutionPanel;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weaver Insights Dashboard Widget — shows project health, insights, and cost summary.
|
|
3
|
+
* Loaded dynamically by the platform's pack UI contributions loader.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
interface InsightsData {
|
|
9
|
+
health: { overall: number; workflows: Array<{ file: string; score: number; trend: string }> };
|
|
10
|
+
bots: Array<{ name: string; ejected: boolean; successRate: number; totalTasksRun: number }>;
|
|
11
|
+
insights: Array<{ severity: string; title: string; description: string; confidence: number }>;
|
|
12
|
+
cost: { last7Days: number; trend: string };
|
|
13
|
+
trust: { phase: number; score: number };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function WeaverInsightsWidget({ projectDir }: { projectDir?: string }) {
|
|
17
|
+
const [data, setData] = useState<InsightsData | null>(null);
|
|
18
|
+
const [error, setError] = useState<string | null>(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!projectDir) return;
|
|
22
|
+
fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`)
|
|
23
|
+
.then(r => r.json())
|
|
24
|
+
.then(setData)
|
|
25
|
+
.catch(e => setError(e.message));
|
|
26
|
+
}, [projectDir]);
|
|
27
|
+
|
|
28
|
+
if (error) return <div style={{ padding: 16, color: '#ef4444' }}>Error: {error}</div>;
|
|
29
|
+
if (!data) return <div style={{ padding: 16, opacity: 0.5 }}>Loading insights...</div>;
|
|
30
|
+
|
|
31
|
+
const severityColor: Record<string, string> = {
|
|
32
|
+
critical: '#ef4444',
|
|
33
|
+
warning: '#f59e0b',
|
|
34
|
+
info: '#6b7280',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13 }}>
|
|
39
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
|
|
40
|
+
<span style={{ fontSize: 28, fontWeight: 700 }}>{data.health.overall}</span>
|
|
41
|
+
<span style={{ opacity: 0.6 }}>/100 health</span>
|
|
42
|
+
<span style={{ marginLeft: 'auto', opacity: 0.5 }}>
|
|
43
|
+
Phase {data.trust.phase} · ${data.cost.last7Days.toFixed(2)}/7d ({data.cost.trend})
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{data.insights.length > 0 && (
|
|
48
|
+
<div style={{ marginBottom: 12 }}>
|
|
49
|
+
<div style={{ fontWeight: 600, marginBottom: 6 }}>Insights</div>
|
|
50
|
+
{data.insights.slice(0, 3).map((insight, i) => (
|
|
51
|
+
<div key={i} style={{ padding: '4px 0', borderBottom: '1px solid rgba(128,128,128,0.1)' }}>
|
|
52
|
+
<span style={{ color: severityColor[insight.severity] ?? '#6b7280', fontWeight: 600, marginRight: 8 }}>
|
|
53
|
+
{insight.severity.toUpperCase()}
|
|
54
|
+
</span>
|
|
55
|
+
<span>{insight.title}</span>
|
|
56
|
+
<span style={{ opacity: 0.5, marginLeft: 8 }}>{Math.round(insight.confidence * 100)}%</span>
|
|
57
|
+
</div>
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
|
|
62
|
+
{data.bots.length > 0 && (
|
|
63
|
+
<div>
|
|
64
|
+
<div style={{ fontWeight: 600, marginBottom: 6 }}>Bots</div>
|
|
65
|
+
{data.bots.map((bot, i) => (
|
|
66
|
+
<div key={i} style={{ padding: '2px 0' }}>
|
|
67
|
+
{bot.name}: {Math.round(bot.successRate * 100)}% success ({bot.totalTasksRun} tasks)
|
|
68
|
+
{bot.ejected && <span style={{ opacity: 0.5 }}> · ejected</span>}
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default WeaverInsightsWidget;
|