@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bot/ai-client.d.ts +22 -2
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +168 -20
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/assistant-core.d.ts +25 -0
- package/dist/bot/assistant-core.d.ts.map +1 -0
- package/dist/bot/assistant-core.js +265 -0
- package/dist/bot/assistant-core.js.map +1 -0
- package/dist/bot/assistant-tools.d.ts +9 -0
- package/dist/bot/assistant-tools.d.ts.map +1 -0
- package/dist/bot/assistant-tools.js +602 -0
- package/dist/bot/assistant-tools.js.map +1 -0
- package/dist/bot/audit-logger.d.ts.map +1 -1
- package/dist/bot/audit-logger.js +9 -5
- package/dist/bot/audit-logger.js.map +1 -1
- package/dist/bot/audit-store.d.ts.map +1 -1
- package/dist/bot/audit-store.js +3 -11
- package/dist/bot/audit-store.js.map +1 -1
- package/dist/bot/bot-manager.d.ts +49 -0
- package/dist/bot/bot-manager.d.ts.map +1 -0
- package/dist/bot/bot-manager.js +279 -0
- package/dist/bot/bot-manager.js.map +1 -0
- package/dist/bot/child-process-tracker.d.ts +6 -0
- package/dist/bot/child-process-tracker.d.ts.map +1 -0
- package/dist/bot/child-process-tracker.js +35 -0
- package/dist/bot/child-process-tracker.js.map +1 -0
- package/dist/bot/cli-provider.d.ts.map +1 -1
- package/dist/bot/cli-provider.js +13 -8
- package/dist/bot/cli-provider.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +40 -0
- package/dist/bot/conversation-store.d.ts.map +1 -0
- package/dist/bot/conversation-store.js +182 -0
- package/dist/bot/conversation-store.js.map +1 -0
- package/dist/bot/cost-store.d.ts.map +1 -1
- package/dist/bot/cost-store.js +10 -14
- package/dist/bot/cost-store.js.map +1 -1
- package/dist/bot/error-guide.d.ts +10 -0
- package/dist/bot/error-guide.d.ts.map +1 -0
- package/dist/bot/error-guide.js +34 -0
- package/dist/bot/error-guide.js.map +1 -0
- package/dist/bot/genesis-store.d.ts.map +1 -1
- package/dist/bot/genesis-store.js +11 -20
- package/dist/bot/genesis-store.js.map +1 -1
- package/dist/bot/index.d.ts +3 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +3 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/knowledge-store.d.ts +17 -0
- package/dist/bot/knowledge-store.d.ts.map +1 -0
- package/dist/bot/knowledge-store.js +53 -0
- package/dist/bot/knowledge-store.js.map +1 -0
- package/dist/bot/pipeline-runner.d.ts.map +1 -1
- package/dist/bot/pipeline-runner.js +8 -1
- package/dist/bot/pipeline-runner.js.map +1 -1
- package/dist/bot/retry-utils.d.ts +19 -0
- package/dist/bot/retry-utils.d.ts.map +1 -0
- package/dist/bot/retry-utils.js +64 -0
- package/dist/bot/retry-utils.js.map +1 -0
- package/dist/bot/run-store.d.ts.map +1 -1
- package/dist/bot/run-store.js +2 -10
- package/dist/bot/run-store.js.map +1 -1
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +24 -3
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/safe-json.d.ts +32 -0
- package/dist/bot/safe-json.d.ts.map +1 -0
- package/dist/bot/safe-json.js +56 -0
- package/dist/bot/safe-json.js.map +1 -0
- package/dist/bot/safe-path.d.ts +18 -0
- package/dist/bot/safe-path.d.ts.map +1 -0
- package/dist/bot/safe-path.js +40 -0
- package/dist/bot/safe-path.js.map +1 -0
- package/dist/bot/session-state.d.ts.map +1 -1
- package/dist/bot/session-state.js +3 -1
- package/dist/bot/session-state.js.map +1 -1
- package/dist/bot/steering.js +1 -1
- package/dist/bot/steering.js.map +1 -1
- package/dist/bot/step-executor.d.ts +10 -5
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +252 -3
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/system-prompt.d.ts +1 -1
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +69 -43
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/task-decomposer.d.ts +24 -0
- package/dist/bot/task-decomposer.d.ts.map +1 -0
- package/dist/bot/task-decomposer.js +75 -0
- package/dist/bot/task-decomposer.js.map +1 -0
- package/dist/bot/task-queue.d.ts +17 -4
- package/dist/bot/task-queue.d.ts.map +1 -1
- package/dist/bot/task-queue.js +102 -14
- package/dist/bot/task-queue.js.map +1 -1
- package/dist/bot/terminal-renderer.d.ts +60 -0
- package/dist/bot/terminal-renderer.d.ts.map +1 -0
- package/dist/bot/terminal-renderer.js +205 -0
- package/dist/bot/terminal-renderer.js.map +1 -0
- package/dist/bot/types.d.ts +7 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts +18 -0
- package/dist/bot/weaver-tools.d.ts.map +1 -0
- package/dist/bot/weaver-tools.js +215 -0
- package/dist/bot/weaver-tools.js.map +1 -0
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +10 -3
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +15 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +742 -28
- package/dist/cli-handlers.js.map +1 -1
- package/dist/handlers/on-bot-completed.d.ts +21 -0
- package/dist/handlers/on-bot-completed.d.ts.map +1 -0
- package/dist/handlers/on-bot-completed.js +28 -0
- package/dist/handlers/on-bot-completed.js.map +1 -0
- package/dist/handlers/on-execution-failure.d.ts +23 -0
- package/dist/handlers/on-execution-failure.d.ts.map +1 -0
- package/dist/handlers/on-execution-failure.js +28 -0
- package/dist/handlers/on-execution-failure.js.map +1 -0
- package/dist/handlers/scheduled-run.d.ts +24 -0
- package/dist/handlers/scheduled-run.d.ts.map +1 -0
- package/dist/handlers/scheduled-run.js +25 -0
- package/dist/handlers/scheduled-run.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-tools.js +2 -2
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/abort-task.d.ts.map +1 -1
- package/dist/node-types/abort-task.js +4 -3
- package/dist/node-types/abort-task.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts +38 -0
- package/dist/node-types/agent-execute.d.ts.map +1 -0
- package/dist/node-types/agent-execute.js +256 -0
- package/dist/node-types/agent-execute.js.map +1 -0
- package/dist/node-types/bot-report.d.ts +5 -3
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +39 -7
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/build-context.d.ts +3 -3
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +108 -24
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/detect-provider.d.ts +2 -2
- package/dist/node-types/detect-provider.d.ts.map +1 -1
- package/dist/node-types/detect-provider.js +3 -1
- package/dist/node-types/detect-provider.js.map +1 -1
- package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
- package/dist/node-types/exec-validate-retry.js +43 -6
- package/dist/node-types/exec-validate-retry.js.map +1 -1
- package/dist/node-types/execute-plan.d.ts.map +1 -1
- package/dist/node-types/execute-plan.js +31 -8
- package/dist/node-types/execute-plan.js.map +1 -1
- package/dist/node-types/execute-target.d.ts.map +1 -1
- package/dist/node-types/execute-target.js +3 -1
- package/dist/node-types/execute-target.js.map +1 -1
- package/dist/node-types/fix-errors.d.ts.map +1 -1
- package/dist/node-types/fix-errors.js +21 -5
- package/dist/node-types/fix-errors.js.map +1 -1
- package/dist/node-types/genesis-observe.d.ts.map +1 -1
- package/dist/node-types/genesis-observe.js +3 -1
- package/dist/node-types/genesis-observe.js.map +1 -1
- package/dist/node-types/genesis-report.js +4 -1
- package/dist/node-types/genesis-report.js.map +1 -1
- package/dist/node-types/git-ops.d.ts.map +1 -1
- package/dist/node-types/git-ops.js +98 -4
- package/dist/node-types/git-ops.js.map +1 -1
- package/dist/node-types/index.d.ts +2 -0
- package/dist/node-types/index.d.ts.map +1 -1
- package/dist/node-types/index.js +2 -0
- package/dist/node-types/index.js.map +1 -1
- package/dist/node-types/load-config.d.ts +2 -2
- package/dist/node-types/load-config.d.ts.map +1 -1
- package/dist/node-types/load-config.js.map +1 -1
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +14 -2
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/node-types/read-workflow.js +8 -2
- package/dist/node-types/read-workflow.js.map +1 -1
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +35 -26
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/node-types/send-notify.js +2 -1
- package/dist/node-types/send-notify.js.map +1 -1
- package/dist/node-types/validate-gate.d.ts +18 -0
- package/dist/node-types/validate-gate.d.ts.map +1 -0
- package/dist/node-types/validate-gate.js +96 -0
- package/dist/node-types/validate-gate.js.map +1 -0
- package/dist/workflows/genesis-task.d.ts +20 -12
- package/dist/workflows/genesis-task.d.ts.map +1 -1
- package/dist/workflows/genesis-task.js +20 -12
- package/dist/workflows/genesis-task.js.map +1 -1
- package/dist/workflows/weaver-agent.d.ts +35 -0
- package/dist/workflows/weaver-agent.d.ts.map +1 -0
- package/dist/workflows/weaver-agent.js +777 -0
- package/dist/workflows/weaver-agent.js.map +1 -0
- package/dist/workflows/weaver-bot-batch.d.ts +19 -26
- package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
- package/dist/workflows/weaver-bot-batch.js +1043 -27
- package/dist/workflows/weaver-bot-batch.js.map +1 -1
- package/dist/workflows/weaver-bot.d.ts +21 -35
- package/dist/workflows/weaver-bot.d.ts.map +1 -1
- package/dist/workflows/weaver-bot.js +1119 -36
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +113 -2
- package/package.json +5 -2
- package/src/bot/ai-client.ts +180 -19
- package/src/bot/assistant-core.ts +306 -0
- package/src/bot/assistant-tools.ts +605 -0
- package/src/bot/audit-logger.ts +6 -5
- package/src/bot/audit-store.ts +3 -12
- package/src/bot/bot-manager.ts +293 -0
- package/src/bot/child-process-tracker.ts +40 -0
- package/src/bot/cli-provider.ts +13 -8
- package/src/bot/conversation-store.ts +222 -0
- package/src/bot/cost-store.ts +11 -12
- package/src/bot/error-guide.ts +34 -0
- package/src/bot/genesis-store.ts +11 -17
- package/src/bot/index.ts +5 -0
- package/src/bot/knowledge-store.ts +59 -0
- package/src/bot/pipeline-runner.ts +7 -1
- package/src/bot/retry-utils.ts +76 -0
- package/src/bot/run-store.ts +2 -11
- package/src/bot/runner.ts +26 -3
- package/src/bot/safe-json.ts +76 -0
- package/src/bot/safe-path.ts +44 -0
- package/src/bot/session-state.ts +2 -1
- package/src/bot/steering.ts +1 -1
- package/src/bot/step-executor.ts +313 -5
- package/src/bot/system-prompt.ts +70 -47
- package/src/bot/task-decomposer.ts +100 -0
- package/src/bot/task-queue.ts +119 -15
- package/src/bot/terminal-renderer.ts +241 -0
- package/src/bot/types.ts +8 -0
- package/src/bot/weaver-tools.ts +225 -0
- package/src/cli-bridge.ts +14 -3
- package/src/cli-handlers.ts +760 -29
- package/src/handlers/on-bot-completed.ts +48 -0
- package/src/handlers/on-execution-failure.ts +42 -0
- package/src/handlers/scheduled-run.ts +42 -0
- package/src/index.ts +5 -0
- package/src/mcp-tools.ts +2 -2
- package/src/node-types/abort-task.ts +5 -4
- package/src/node-types/agent-execute.ts +306 -0
- package/src/node-types/bot-report.ts +40 -9
- package/src/node-types/build-context.ts +112 -25
- package/src/node-types/detect-provider.ts +4 -3
- package/src/node-types/exec-validate-retry.ts +47 -8
- package/src/node-types/execute-plan.ts +32 -8
- package/src/node-types/execute-target.ts +2 -1
- package/src/node-types/fix-errors.ts +20 -5
- package/src/node-types/genesis-observe.ts +2 -1
- package/src/node-types/genesis-report.ts +1 -1
- package/src/node-types/git-ops.ts +93 -4
- package/src/node-types/index.ts +2 -0
- package/src/node-types/load-config.ts +3 -3
- package/src/node-types/plan-task.ts +15 -3
- package/src/node-types/read-workflow.ts +2 -2
- package/src/node-types/receive-task.ts +31 -26
- package/src/node-types/send-notify.ts +1 -1
- package/src/node-types/validate-gate.ts +112 -0
- package/src/workflows/genesis-task.ts +20 -12
- package/src/workflows/weaver-agent.ts +799 -0
- package/src/workflows/weaver-bot-batch.ts +1049 -27
- package/src/workflows/weaver-bot.ts +1123 -36
|
@@ -4,9 +4,9 @@ import * as path from 'node:path';
|
|
|
4
4
|
import type { WeaverContext } from '../bot/types.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Builds the knowledge bundle the AI needs for planning.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Builds the knowledge bundle the AI needs for planning.
|
|
8
|
+
* Adaptive: for modify tasks, includes only grammar + referenced node types.
|
|
9
|
+
* For create tasks, includes full authoring context + templates.
|
|
10
10
|
*
|
|
11
11
|
* @flowWeaver nodeType
|
|
12
12
|
* @expression
|
|
@@ -21,31 +21,81 @@ export function weaverBuildContext(ctx: string): { ctx: string } {
|
|
|
21
21
|
const task = JSON.parse(context.taskJson!) as { mode?: string; targets?: string[] };
|
|
22
22
|
const sections: string[] = [];
|
|
23
23
|
|
|
24
|
+
if (task.mode === 'modify' && task.targets?.length) {
|
|
25
|
+
// Adaptive context: minimal grammar + target files + referenced node types
|
|
26
|
+
sections.push(...buildModifyContext(projectDir, task.targets));
|
|
27
|
+
} else {
|
|
28
|
+
// Full context for create tasks or unknown modes
|
|
29
|
+
sections.push(...buildFullContext(projectDir, task.mode));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const bundle = sections.join('\n\n---\n\n');
|
|
33
|
+
// Output handled by session renderer; keep a dim line for debugging
|
|
34
|
+
if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Context: ${bundle.length} chars\x1b[0m\n`);
|
|
35
|
+
|
|
36
|
+
context.contextBundle = bundle;
|
|
37
|
+
return { ctx: JSON.stringify(context) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Minimal context for modify tasks: grammar + annotations + target sources + referenced node types. */
|
|
41
|
+
function buildModifyContext(projectDir: string, targets: string[]): string[] {
|
|
42
|
+
const sections: string[] = [];
|
|
43
|
+
|
|
44
|
+
// Minimal grammar (jsdoc-grammar + advanced-annotations only — skip concepts/scaffold/patterns)
|
|
24
45
|
try {
|
|
25
|
-
const ctxOutput = execFileSync(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
timeout: 30_000,
|
|
29
|
-
|
|
30
|
-
}).trim();
|
|
46
|
+
const ctxOutput = execFileSync(
|
|
47
|
+
'flow-weaver',
|
|
48
|
+
['context', '--topics', 'jsdoc-grammar,advanced-annotations', '--profile', 'assistant'],
|
|
49
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30_000, cwd: projectDir },
|
|
50
|
+
).trim();
|
|
31
51
|
if (ctxOutput) sections.push(ctxOutput);
|
|
32
|
-
} catch {
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (process.env.WEAVER_VERBOSE) console.error('[build-context] modify context unavailable:', err);
|
|
33
54
|
sections.push('(flow-weaver context not available)');
|
|
34
55
|
}
|
|
35
56
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
// Target file sources + referenced node type sources
|
|
58
|
+
const includedFiles = new Set<string>();
|
|
59
|
+
for (const target of targets) {
|
|
60
|
+
const filePath = path.isAbsolute(target) ? target : path.resolve(projectDir, target);
|
|
61
|
+
try {
|
|
62
|
+
if (!fs.existsSync(filePath)) continue;
|
|
63
|
+
const source = fs.readFileSync(filePath, 'utf-8');
|
|
64
|
+
sections.push(`## Target: ${target}\n\n\`\`\`typescript\n${source}\n\`\`\``);
|
|
65
|
+
includedFiles.add(filePath);
|
|
66
|
+
|
|
67
|
+
// Extract import paths to find referenced node type files
|
|
68
|
+
const nodeTypeSources = extractReferencedNodeTypes(filePath, source, projectDir);
|
|
69
|
+
for (const [relPath, ntSource] of nodeTypeSources) {
|
|
70
|
+
const absPath = path.resolve(projectDir, relPath);
|
|
71
|
+
if (!includedFiles.has(absPath)) {
|
|
72
|
+
includedFiles.add(absPath);
|
|
73
|
+
sections.push(`## Node Type: ${relPath}\n\n\`\`\`typescript\n${ntSource}\n\`\`\``);
|
|
43
74
|
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
75
|
+
}
|
|
76
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] unreadable file:', err); }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return sections;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Full context for create tasks: full authoring preset + templates. */
|
|
83
|
+
function buildFullContext(projectDir: string, mode?: string): string[] {
|
|
84
|
+
const sections: string[] = [];
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const ctxOutput = execFileSync(
|
|
88
|
+
'flow-weaver',
|
|
89
|
+
['context', 'authoring', '--profile', 'assistant'],
|
|
90
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30_000, cwd: projectDir },
|
|
91
|
+
).trim();
|
|
92
|
+
if (ctxOutput) sections.push(ctxOutput);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (process.env.WEAVER_VERBOSE) console.error('[build-context] full context unavailable:', err);
|
|
95
|
+
sections.push('(flow-weaver context not available)');
|
|
46
96
|
}
|
|
47
97
|
|
|
48
|
-
if (
|
|
98
|
+
if (mode === 'create') {
|
|
49
99
|
try {
|
|
50
100
|
const templates = execFileSync('flow-weaver', ['list', 'templates'], {
|
|
51
101
|
encoding: 'utf-8',
|
|
@@ -54,12 +104,49 @@ export function weaverBuildContext(ctx: string): { ctx: string } {
|
|
|
54
104
|
cwd: projectDir,
|
|
55
105
|
}).trim();
|
|
56
106
|
if (templates) sections.push(`## Available Templates\n\n${templates}`);
|
|
57
|
-
} catch {
|
|
107
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] templates not available:', err); }
|
|
58
108
|
}
|
|
59
109
|
|
|
60
|
-
|
|
61
|
-
|
|
110
|
+
return sections;
|
|
111
|
+
}
|
|
62
112
|
|
|
63
|
-
|
|
64
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Extract node type file sources referenced by a workflow file's imports.
|
|
115
|
+
* Parses import statements to find relative imports from node-types directories.
|
|
116
|
+
*/
|
|
117
|
+
function extractReferencedNodeTypes(
|
|
118
|
+
filePath: string,
|
|
119
|
+
source: string,
|
|
120
|
+
projectDir: string,
|
|
121
|
+
): Array<[relPath: string, source: string]> {
|
|
122
|
+
const results: Array<[string, string]> = [];
|
|
123
|
+
const dir = path.dirname(filePath);
|
|
124
|
+
|
|
125
|
+
// Match: import { ... } from '../node-types/foo.js' or './node-types/bar'
|
|
126
|
+
const importRegex = /import\s+(?:type\s+)?{[^}]+}\s+from\s+['"]([^'"]+)['"]/g;
|
|
127
|
+
let match: RegExpExecArray | null;
|
|
128
|
+
|
|
129
|
+
while ((match = importRegex.exec(source)) !== null) {
|
|
130
|
+
const importPath = match[1];
|
|
131
|
+
// Only include relative imports that look like node type files
|
|
132
|
+
if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;
|
|
133
|
+
if (!importPath.includes('node-type') && !importPath.includes('node_type')) continue;
|
|
134
|
+
|
|
135
|
+
// Resolve the import to an absolute path
|
|
136
|
+
let resolved = path.resolve(dir, importPath);
|
|
137
|
+
// Try with .ts extension if no extension
|
|
138
|
+
if (!fs.existsSync(resolved)) {
|
|
139
|
+
if (fs.existsSync(resolved + '.ts')) resolved = resolved + '.ts';
|
|
140
|
+
else if (fs.existsSync(resolved.replace(/\.js$/, '.ts'))) resolved = resolved.replace(/\.js$/, '.ts');
|
|
141
|
+
else continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const ntSource = fs.readFileSync(resolved, 'utf-8');
|
|
146
|
+
const relPath = path.relative(projectDir, resolved);
|
|
147
|
+
results.push([relPath, ntSource]);
|
|
148
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[build-context] unreadable node type:', err); }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return results;
|
|
65
152
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import type {
|
|
2
|
+
import type { BotConfig, WeaverEnv, ProviderInfo } from '../bot/types.js';
|
|
3
3
|
|
|
4
4
|
const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
|
|
5
5
|
|
|
@@ -23,7 +23,7 @@ function whichSafe(cmd: string, cwd: string): string {
|
|
|
23
23
|
* @output env [order:0] - Weaver environment bundle
|
|
24
24
|
* @output onFailure [hidden]
|
|
25
25
|
*/
|
|
26
|
-
export function weaverDetectProvider(projectDir: string, config:
|
|
26
|
+
export function weaverDetectProvider(projectDir: string, config: BotConfig): {
|
|
27
27
|
env: WeaverEnv;
|
|
28
28
|
} {
|
|
29
29
|
const providerSetting = config.provider ?? 'auto';
|
|
@@ -69,7 +69,8 @@ export function weaverDetectProvider(projectDir: string, config: WeaverConfig):
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const label = providerInfo.model ? `${type} (${providerInfo.model})` : type;
|
|
72
|
-
|
|
72
|
+
// Provider info now shown by session renderer; keep for verbose/debug
|
|
73
|
+
if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Provider: ${label}\x1b[0m\n`);
|
|
73
74
|
|
|
74
75
|
return {
|
|
75
76
|
env: { projectDir, config, providerType: type, providerInfo },
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
|
-
import type { WeaverContext } from '../bot/types.js';
|
|
5
|
-
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
4
|
+
import type { WeaverContext, StepLogEntry } from '../bot/types.js';
|
|
5
|
+
import { callAI, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
|
|
6
6
|
import { executeStep } from '../bot/step-executor.js';
|
|
7
7
|
import { validateFiles } from '../bot/file-validator.js';
|
|
8
8
|
import { auditEmit } from '../bot/audit-logger.js';
|
|
@@ -38,19 +38,29 @@ export async function weaverExecValidateRetry(
|
|
|
38
38
|
|
|
39
39
|
const { providerInfo: pInfo, projectDir } = env;
|
|
40
40
|
const maxAttempts = 3;
|
|
41
|
+
const taskDeadline = Date.now() + 180_000; // 3 minute max per task
|
|
41
42
|
let currentPlan = JSON.parse(context.planJson!);
|
|
42
43
|
let allFilesModified: string[] = [];
|
|
44
|
+
let allStepLog: StepLogEntry[] = [];
|
|
43
45
|
let lastExecResult: Record<string, unknown> = {};
|
|
44
46
|
let lastValidation: Array<{ file: string; valid: boolean; errors: string[] }> = [];
|
|
45
47
|
let allValid = false;
|
|
48
|
+
let prevErrorCount = Infinity;
|
|
46
49
|
|
|
47
50
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
51
|
+
// Task timeout — stop if we've exceeded the deadline
|
|
52
|
+
if (Date.now() > taskDeadline) {
|
|
53
|
+
console.error('\x1b[33m→ Task timeout (3 min), moving on\x1b[0m');
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
console.log(`\x1b[36m→ Attempt ${attempt}/${maxAttempts}\x1b[0m`);
|
|
49
58
|
auditEmit('step-start', { attempt, stepCount: currentPlan.steps?.length });
|
|
50
59
|
|
|
51
60
|
const execResult = await executePlanSteps(currentPlan, projectDir);
|
|
52
61
|
lastExecResult = execResult;
|
|
53
62
|
allFilesModified = [...new Set([...allFilesModified, ...execResult.filesModified])];
|
|
63
|
+
allStepLog.push(...execResult.stepLog);
|
|
54
64
|
auditEmit('step-complete', { attempt, filesModified: execResult.filesModified, errors: execResult.errors });
|
|
55
65
|
|
|
56
66
|
const validation = await validateFiles(execResult.filesModified, projectDir);
|
|
@@ -59,6 +69,13 @@ export async function weaverExecValidateRetry(
|
|
|
59
69
|
const errorCount = validation.filter(v => !v.valid).length;
|
|
60
70
|
auditEmit('validation-run', { attempt, allValid, errorCount });
|
|
61
71
|
|
|
72
|
+
// Stop retrying if errors didn't decrease — AI is stuck
|
|
73
|
+
if (attempt > 1 && errorCount >= prevErrorCount) {
|
|
74
|
+
console.error(`\x1b[33m→ Errors not decreasing (${errorCount} >= ${prevErrorCount}), stopping retry\x1b[0m`);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
prevErrorCount = errorCount;
|
|
78
|
+
|
|
62
79
|
// Collect design warnings from valid files
|
|
63
80
|
const designWarnings = validation
|
|
64
81
|
.filter(v => v.valid && v.warnings.length > 0)
|
|
@@ -97,16 +114,23 @@ export async function weaverExecValidateRetry(
|
|
|
97
114
|
try {
|
|
98
115
|
const mod = await import('../bot/system-prompt.js');
|
|
99
116
|
systemPrompt = await mod.buildSystemPrompt();
|
|
100
|
-
} catch {
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (process.env.WEAVER_VERBOSE) console.error('[exec-validate-retry] system prompt build failed:', err);
|
|
101
119
|
systemPrompt = 'You are Weaver. Return ONLY valid JSON.';
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
|
|
122
|
+
// Include step outputs so the AI has context from discovery steps
|
|
123
|
+
const outputContext = Object.entries(execResult.stepOutputs)
|
|
124
|
+
.map(([id, out]) => `--- Output of ${id} ---\n${out}`)
|
|
125
|
+
.join('\n\n');
|
|
126
|
+
|
|
127
|
+
const fixPrompt = `The following validation errors occurred:\n${errors}\n\n${outputContext ? `Discovery step outputs:\n${outputContext}\n\n` : ''}Provide a CONCRETE fix plan. Every patch-file step MUST include "file" (absolute path from the outputs above) and "patches" array with exact "find"/"replace" strings. Do NOT use placeholders.`;
|
|
105
128
|
|
|
106
129
|
const text = await callAI(pInfo, systemPrompt, fixPrompt, 8192);
|
|
107
130
|
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
const parsed = parseJsonResponse(text);
|
|
132
|
+
currentPlan = normalizePlan(parsed);
|
|
133
|
+
console.log(`\x1b[36m→ Fix plan: ${currentPlan.summary} (${currentPlan.steps.length} steps)\x1b[0m`);
|
|
110
134
|
} catch (err: unknown) {
|
|
111
135
|
const msg = err instanceof Error ? err.message : String(err);
|
|
112
136
|
console.error(`\x1b[31m→ Fix planning failed: ${msg}\x1b[0m`);
|
|
@@ -118,6 +142,7 @@ export async function weaverExecValidateRetry(
|
|
|
118
142
|
context.resultJson = JSON.stringify(lastExecResult);
|
|
119
143
|
context.validationResultJson = JSON.stringify(lastValidation);
|
|
120
144
|
context.filesModified = JSON.stringify(allFilesModified);
|
|
145
|
+
context.stepLogJson = JSON.stringify(allStepLog);
|
|
121
146
|
context.allValid = allValid;
|
|
122
147
|
|
|
123
148
|
return { onSuccess: allValid, onFailure: !allValid, ctx: JSON.stringify(context) };
|
|
@@ -126,9 +151,11 @@ export async function weaverExecValidateRetry(
|
|
|
126
151
|
async function executePlanSteps(
|
|
127
152
|
plan: { steps: Array<{ id: string; operation: string; description: string; args: Record<string, unknown> }> },
|
|
128
153
|
projectDir: string,
|
|
129
|
-
): Promise<{ success: boolean; filesModified: string[]; errors: string[]; stepsCompleted: number; stepsTotal: number }> {
|
|
154
|
+
): Promise<{ success: boolean; filesModified: string[]; errors: string[]; stepsCompleted: number; stepsTotal: number; stepLog: StepLogEntry[]; stepOutputs: Record<string, string> }> {
|
|
130
155
|
const filesModified: string[] = [];
|
|
131
156
|
const errors: string[] = [];
|
|
157
|
+
const stepLog: StepLogEntry[] = [];
|
|
158
|
+
const stepOutputs: Record<string, string> = {};
|
|
132
159
|
let completed = 0;
|
|
133
160
|
const steps = plan.steps ?? [];
|
|
134
161
|
|
|
@@ -136,23 +163,35 @@ async function executePlanSteps(
|
|
|
136
163
|
const steering = checkSteeringSignal();
|
|
137
164
|
if (steering === 'cancel') {
|
|
138
165
|
errors.push(`Cancelled at step ${step.id}`);
|
|
166
|
+
stepLog.push({ step: step.id, status: 'error', detail: 'Cancelled by steering signal' });
|
|
139
167
|
break;
|
|
140
168
|
}
|
|
141
169
|
|
|
142
170
|
try {
|
|
143
171
|
const result = await executeStep(step, projectDir);
|
|
172
|
+
if (result.blocked) {
|
|
173
|
+
console.error(`\x1b[33m ⚠ ${step.id}: ${result.blockReason}\x1b[0m`);
|
|
174
|
+
stepLog.push({ step: step.id, status: 'blocked', detail: result.blockReason });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
144
177
|
if (result.file) filesModified.push(result.file);
|
|
145
178
|
if (result.files) filesModified.push(...result.files);
|
|
179
|
+
// Capture step output for feeding into fix prompts
|
|
180
|
+
if (result.output) {
|
|
181
|
+
stepOutputs[step.id] = result.output.slice(0, 4000); // cap at 4k to fit in prompt
|
|
182
|
+
}
|
|
146
183
|
completed++;
|
|
147
184
|
console.log(`\x1b[32m + ${step.id}: ${step.description}\x1b[0m`);
|
|
185
|
+
stepLog.push({ step: step.id, status: 'ok', detail: step.description });
|
|
148
186
|
} catch (err: unknown) {
|
|
149
187
|
const msg = err instanceof Error ? err.message : String(err);
|
|
150
188
|
errors.push(`${step.id}: ${msg}`);
|
|
151
189
|
console.error(`\x1b[31m x ${step.id}: ${msg}\x1b[0m`);
|
|
190
|
+
stepLog.push({ step: step.id, status: 'error', detail: msg });
|
|
152
191
|
}
|
|
153
192
|
}
|
|
154
193
|
|
|
155
|
-
return { success: errors.length === 0, filesModified: [...new Set(filesModified)], errors, stepsCompleted: completed, stepsTotal: steps.length };
|
|
194
|
+
return { success: errors.length === 0, filesModified: [...new Set(filesModified)], errors, stepsCompleted: completed, stepsTotal: steps.length, stepLog, stepOutputs };
|
|
156
195
|
}
|
|
157
196
|
|
|
158
197
|
function checkSteeringSignal(): 'cancel' | null {
|
|
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import type { WeaverEnv } from '../bot/types.js';
|
|
5
|
-
import { executeStep } from '../bot/step-executor.js';
|
|
5
|
+
import { executeStep, resetPlanFileCounter } from '../bot/step-executor.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Executes plan steps via the flow-weaver CLI. Checks steering
|
|
@@ -39,6 +39,7 @@ export async function weaverExecutePlan(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const { projectDir } = env;
|
|
42
|
+
resetPlanFileCounter(); // Reset per-plan write counter for safety guards
|
|
42
43
|
const plan = JSON.parse(planJson) as { steps: Array<{ id: string; operation: string; description: string; args: Record<string, unknown> }> };
|
|
43
44
|
const filesModified: string[] = [];
|
|
44
45
|
const filesCreated: string[] = [];
|
|
@@ -55,10 +56,27 @@ export async function weaverExecutePlan(
|
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
for (
|
|
59
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
60
|
+
const step = plan.steps[i];
|
|
61
|
+
// Defensive: ensure step has required fields
|
|
62
|
+
if (!step || typeof step !== 'object') {
|
|
63
|
+
errors.push(`step-${i + 1}: Malformed step (not an object)`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const stepId = step.id ?? `step-${i + 1}`;
|
|
67
|
+
const stepDesc = step.description ?? step.operation ?? 'unknown';
|
|
68
|
+
if (!step.operation) {
|
|
69
|
+
errors.push(`${stepId}: Missing "operation" field`);
|
|
70
|
+
console.error(`\x1b[31m x ${stepId}: Missing operation\x1b[0m`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!step.args) {
|
|
74
|
+
step.args = {};
|
|
75
|
+
}
|
|
76
|
+
|
|
59
77
|
const steering = checkSteering();
|
|
60
78
|
if (steering === 'cancel') {
|
|
61
|
-
output.push(`Cancelled at
|
|
79
|
+
output.push(`Cancelled at ${stepId}`);
|
|
62
80
|
break;
|
|
63
81
|
}
|
|
64
82
|
if (steering === 'pause') {
|
|
@@ -68,18 +86,24 @@ export async function weaverExecutePlan(
|
|
|
68
86
|
|
|
69
87
|
try {
|
|
70
88
|
const result = await executeStep(step, projectDir);
|
|
89
|
+
if (result.blocked) {
|
|
90
|
+
errors.push(`${stepId}: BLOCKED - ${result.blockReason}`);
|
|
91
|
+
output.push(`${stepId}: BLOCKED - ${result.blockReason}`);
|
|
92
|
+
console.error(`\x1b[33m ⚠ ${stepId}: ${result.blockReason}\x1b[0m`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
71
95
|
if (result.file) {
|
|
72
96
|
if (result.created) filesCreated.push(result.file);
|
|
73
97
|
else filesModified.push(result.file);
|
|
74
98
|
}
|
|
75
99
|
completed++;
|
|
76
|
-
output.push(`${
|
|
77
|
-
console.log(`\x1b[32m + ${
|
|
100
|
+
output.push(`${stepId}: ${stepDesc} - done`);
|
|
101
|
+
console.log(`\x1b[32m + ${stepId}: ${stepDesc}\x1b[0m`);
|
|
78
102
|
} catch (err: unknown) {
|
|
79
103
|
const msg = err instanceof Error ? err.message : String(err);
|
|
80
|
-
errors.push(`${
|
|
81
|
-
output.push(`${
|
|
82
|
-
console.error(`\x1b[31m x ${
|
|
104
|
+
errors.push(`${stepId}: ${msg}`);
|
|
105
|
+
output.push(`${stepId}: FAILED - ${msg}`);
|
|
106
|
+
console.error(`\x1b[31m x ${stepId}: ${msg}\x1b[0m`);
|
|
83
107
|
}
|
|
84
108
|
}
|
|
85
109
|
|
|
@@ -137,7 +137,8 @@ When stabilize mode is active, only fix-up operations are allowed: removeNode, r
|
|
|
137
137
|
## Response Format
|
|
138
138
|
|
|
139
139
|
Return ONLY valid JSON. No markdown, no code fences, no explanation outside the JSON structure.`;
|
|
140
|
-
} catch {
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (process.env.WEAVER_VERBOSE) console.error('[execute-target] prompt build failed:', err);
|
|
141
142
|
return FALLBACK;
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WeaverEnv } from '../bot/types.js';
|
|
2
|
-
import { callAI, parseJsonResponse } from '../bot/ai-client.js';
|
|
2
|
+
import { callAI, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* When validation fails, sends errors + context to the AI and
|
|
@@ -42,18 +42,33 @@ export async function weaverFixErrors(
|
|
|
42
42
|
try {
|
|
43
43
|
const mod = await import('../bot/system-prompt.js');
|
|
44
44
|
systemPrompt = await mod.buildSystemPrompt();
|
|
45
|
-
} catch {
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (process.env.WEAVER_VERBOSE) console.error('[fix-errors] system prompt build failed:', err);
|
|
46
47
|
systemPrompt = 'You are Weaver. Return ONLY valid JSON.';
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
const errorSummary = errors.map(e => `${e.file}: ${e.errors.join(', ')}`).join('\n');
|
|
50
|
-
const userPrompt = `The following validation errors occurred
|
|
51
|
+
const userPrompt = `The following validation errors occurred:
|
|
52
|
+
${errorSummary}
|
|
53
|
+
|
|
54
|
+
Provide a fix plan as JSON: {"steps": [...], "summary": "..."}
|
|
55
|
+
|
|
56
|
+
Each step MUST have: "id" (string), "operation" (string), "description" (string), "args" (object).
|
|
57
|
+
|
|
58
|
+
Available operations for fixes:
|
|
59
|
+
- patch-file: Surgical find-and-replace. args: { "file": "path", "patches": [{ "find": "old text", "replace": "new text" }] }
|
|
60
|
+
PREFERRED for fixing @input annotations. Example: { "find": "@input portName", "replace": "@input [portName]" }
|
|
61
|
+
- run-shell: Execute a command. args: { "command": "..." }
|
|
62
|
+
- read-file: Read file content. args: { "file": "path" }
|
|
63
|
+
|
|
64
|
+
Return ONLY valid JSON. No explanation outside the JSON.`;
|
|
51
65
|
|
|
52
66
|
try {
|
|
53
67
|
const text = await callAI(pInfo, systemPrompt, userPrompt, 8192);
|
|
54
68
|
|
|
55
|
-
const
|
|
56
|
-
|
|
69
|
+
const parsed = parseJsonResponse(text);
|
|
70
|
+
const plan = normalizePlan(parsed);
|
|
71
|
+
console.log(`\x1b[36m→ Fix plan: ${plan.summary} (${plan.steps.length} steps)\x1b[0m`);
|
|
57
72
|
return { onSuccess: true, onFailure: false, env, taskJson, fixPlanJson: JSON.stringify(plan) };
|
|
58
73
|
} catch (err: unknown) {
|
|
59
74
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -77,8 +77,9 @@ export async function genesisObserve(
|
|
|
77
77
|
gitCommit = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
78
78
|
cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
79
79
|
}).trim();
|
|
80
|
-
} catch {
|
|
80
|
+
} catch (err) {
|
|
81
81
|
// Not a git repo or git unavailable
|
|
82
|
+
if (process.env.WEAVER_VERBOSE) console.error('[genesis-observe] git unavailable:', err);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
const existingWorkflows: string[] = [];
|
|
@@ -34,7 +34,7 @@ export function genesisReport(successCtx?: string, failCtx?: string, proposeFail
|
|
|
34
34
|
try {
|
|
35
35
|
const result = JSON.parse(context.applyResultJson) as { applied: number; failed: number };
|
|
36
36
|
summary += ` (applied: ${result.applied}, failed: ${result.failed})`;
|
|
37
|
-
} catch {
|
|
37
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[genesis-report] applyResultJson parse failed:', err); }
|
|
38
38
|
}
|
|
39
39
|
summary += elapsed ? ` [${elapsed}]` : '';
|
|
40
40
|
console.log(`\n\x1b[31m${summary}\x1b[0m\n`);
|
|
@@ -1,7 +1,62 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import * as path from 'node:path';
|
|
2
3
|
import type { WeaverContext } from '../bot/types.js';
|
|
3
4
|
import { auditEmit } from '../bot/audit-logger.js';
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Review staged diff for suspicious changes before committing.
|
|
8
|
+
* Returns an array of issues found (empty = safe to commit).
|
|
9
|
+
*/
|
|
10
|
+
function reviewStagedDiff(cwd: string): string[] {
|
|
11
|
+
const issues: string[] = [];
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const diff = execFileSync('git', ['diff', '--cached', '--stat'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
15
|
+
if (!diff) return issues;
|
|
16
|
+
|
|
17
|
+
// Check for large deletions (file became empty or nearly empty)
|
|
18
|
+
const numstat = execFileSync('git', ['diff', '--cached', '--numstat'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
19
|
+
for (const line of numstat.split('\n').filter(Boolean)) {
|
|
20
|
+
const [added, deleted, file] = line.split('\t');
|
|
21
|
+
if (!file || !added || !deleted) continue;
|
|
22
|
+
const addedN = parseInt(added, 10) || 0;
|
|
23
|
+
const deletedN = parseInt(deleted, 10) || 0;
|
|
24
|
+
|
|
25
|
+
// Flag: file lost >80% of its content with minimal additions
|
|
26
|
+
if (deletedN > 50 && addedN < 5 && deletedN > addedN * 10) {
|
|
27
|
+
issues.push(`${file}: deleted ${deletedN} lines, added only ${addedN} (possible truncation)`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Flag: file became empty (0 additions, all deletions)
|
|
31
|
+
if (addedN === 0 && deletedN > 10) {
|
|
32
|
+
issues.push(`${file}: file emptied (${deletedN} lines deleted, 0 added)`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for sensitive patterns in added lines
|
|
37
|
+
const patchDiff = execFileSync('git', ['diff', '--cached', '-U0'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
38
|
+
const sensitivePatterns = [
|
|
39
|
+
/api[_-]?key\s*[:=]\s*["'][^"']{20,}/i,
|
|
40
|
+
/secret\s*[:=]\s*["'][^"']{10,}/i,
|
|
41
|
+
/password\s*[:=]\s*["'][^"']{5,}/i,
|
|
42
|
+
];
|
|
43
|
+
for (const line of patchDiff.split('\n')) {
|
|
44
|
+
if (!line.startsWith('+') || line.startsWith('+++')) continue;
|
|
45
|
+
for (const pattern of sensitivePatterns) {
|
|
46
|
+
if (pattern.test(line)) {
|
|
47
|
+
issues.push('Possible credential/secret in staged changes');
|
|
48
|
+
return issues; // One is enough to block
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
// If diff commands fail, don't block — let commit proceed
|
|
54
|
+
if (process.env.WEAVER_VERBOSE) console.error('[git-ops] diff review failed:', err);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return issues;
|
|
58
|
+
}
|
|
59
|
+
|
|
5
60
|
/**
|
|
6
61
|
* Git operations on created/modified files: stage, commit, branch.
|
|
7
62
|
* Runs in parallel with notifications after execution.
|
|
@@ -48,25 +103,59 @@ export function weaverGitOps(ctx: string): { ctx: string } {
|
|
|
48
103
|
}
|
|
49
104
|
}
|
|
50
105
|
|
|
51
|
-
//
|
|
106
|
+
// Get actually-changed files from git (avoids phantom commits)
|
|
107
|
+
let changedFiles: Set<string>;
|
|
108
|
+
try {
|
|
109
|
+
const diff = execFileSync('git', ['diff', '--name-only'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
110
|
+
const untracked = execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
111
|
+
changedFiles = new Set([...diff.split('\n'), ...untracked.split('\n')].filter(Boolean));
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (process.env.WEAVER_VERBOSE) console.error('[git-ops] git diff failed, using filesModified fallback:', err);
|
|
114
|
+
changedFiles = new Set(files); // fallback: trust filesModified
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Stage only files that are both in filesModified AND actually changed
|
|
118
|
+
let staged = 0;
|
|
52
119
|
for (const file of files) {
|
|
120
|
+
// Resolve to relative path for comparison
|
|
121
|
+
const relative = path.relative(projectDir, path.resolve(projectDir, file));
|
|
122
|
+
if (!changedFiles.has(relative) && !changedFiles.has(file)) continue;
|
|
53
123
|
try {
|
|
54
124
|
execFileSync('git', ['add', file], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
125
|
+
staged++;
|
|
55
126
|
} catch { /* ignore unstaged files */ }
|
|
56
127
|
}
|
|
57
128
|
|
|
129
|
+
if (staged === 0) {
|
|
130
|
+
results.push('No actual changes to commit');
|
|
131
|
+
auditEmit('git-operation', { branch: gitConfig.branch, filesCount: 0, results });
|
|
132
|
+
context.gitResultJson = JSON.stringify({ skipped: true, reason: 'no actual changes', results });
|
|
133
|
+
return { ctx: JSON.stringify(context) };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Diff review: check for suspicious changes before committing
|
|
137
|
+
const diffIssues = reviewStagedDiff(projectDir);
|
|
138
|
+
if (diffIssues.length > 0) {
|
|
139
|
+
// Unstage and skip commit — something looks wrong
|
|
140
|
+
try { execFileSync('git', ['reset', 'HEAD'], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }); } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[git-ops] reset failed:', err); }
|
|
141
|
+
results.push(`Commit blocked: ${diffIssues.join('; ')}`);
|
|
142
|
+
auditEmit('git-operation', { branch: gitConfig.branch, filesCount: 0, results, blocked: true });
|
|
143
|
+
context.gitResultJson = JSON.stringify({ skipped: true, reason: 'diff review failed', issues: diffIssues, results });
|
|
144
|
+
return { ctx: JSON.stringify(context) };
|
|
145
|
+
}
|
|
146
|
+
|
|
58
147
|
// Commit
|
|
59
148
|
const prefix = gitConfig.commitPrefix ?? 'weaver:';
|
|
60
|
-
const commitMsg = `${prefix} bot task (${
|
|
149
|
+
const commitMsg = `${prefix} bot task (${staged} file${staged === 1 ? '' : 's'})`;
|
|
61
150
|
try {
|
|
62
151
|
execFileSync('git', ['commit', '-m', commitMsg], { cwd: projectDir, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
63
152
|
results.push(`Committed: ${commitMsg}`);
|
|
64
|
-
|
|
153
|
+
if (process.env.WEAVER_VERBOSE) process.stderr.write(`\x1b[2m Git: ${commitMsg}\x1b[0m\n`);
|
|
65
154
|
} catch {
|
|
66
155
|
results.push('Nothing to commit');
|
|
67
156
|
}
|
|
68
157
|
|
|
69
|
-
auditEmit('git-operation', { branch: gitConfig.branch, filesCount:
|
|
158
|
+
auditEmit('git-operation', { branch: gitConfig.branch, filesCount: staged, results });
|
|
70
159
|
context.gitResultJson = JSON.stringify({ skipped: false, results });
|
|
71
160
|
return { ctx: JSON.stringify(context) };
|
|
72
161
|
}
|
package/src/node-types/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ export { weaverValidateResult } from './validate-result.js';
|
|
|
17
17
|
export { weaverFixErrors } from './fix-errors.js';
|
|
18
18
|
export { weaverGitOps } from './git-ops.js';
|
|
19
19
|
export { weaverBotReport } from './bot-report.js';
|
|
20
|
+
export { weaverAgentExecute } from './agent-execute.js';
|
|
21
|
+
export { weaverValidateGate } from './validate-gate.js';
|
|
20
22
|
export { genesisLoadConfig } from './genesis-load-config.js';
|
|
21
23
|
export { genesisObserve } from './genesis-observe.js';
|
|
22
24
|
export { genesisDiffFingerprint } from './genesis-diff-fingerprint.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import type {
|
|
3
|
+
import type { BotConfig } from '../bot/types.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Read .weaver.json, merge with defaults, and output the config object.
|
|
@@ -13,10 +13,10 @@ import type { WeaverConfig } from '../bot/types.js';
|
|
|
13
13
|
* @output config [order:1] - Weaver configuration
|
|
14
14
|
* @output onFailure [hidden]
|
|
15
15
|
*/
|
|
16
|
-
export function weaverLoadConfig(projectDir?: string): { projectDir: string; config:
|
|
16
|
+
export function weaverLoadConfig(projectDir?: string): { projectDir: string; config: BotConfig } {
|
|
17
17
|
const dir = projectDir || process.cwd();
|
|
18
18
|
const configPath = path.join(dir, '.weaver.json');
|
|
19
|
-
let config:
|
|
19
|
+
let config: BotConfig = { provider: 'auto' };
|
|
20
20
|
if (fs.existsSync(configPath)) {
|
|
21
21
|
config = { ...config, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) };
|
|
22
22
|
console.log(`\x1b[36m→ Loaded config from ${configPath}\x1b[0m`);
|