@synergenius/flow-weaver-pack-weaver 0.9.134 → 0.9.136

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/ai-client.d.ts +9 -0
  2. package/dist/bot/ai-client.d.ts.map +1 -1
  3. package/dist/bot/ai-client.js +59 -0
  4. package/dist/bot/ai-client.js.map +1 -1
  5. package/dist/bot/behavior-defaults.d.ts +4 -0
  6. package/dist/bot/behavior-defaults.d.ts.map +1 -1
  7. package/dist/bot/behavior-defaults.js +17 -0
  8. package/dist/bot/behavior-defaults.js.map +1 -1
  9. package/dist/bot/capability-registry.d.ts +20 -0
  10. package/dist/bot/capability-registry.d.ts.map +1 -0
  11. package/dist/bot/capability-registry.js +205 -0
  12. package/dist/bot/capability-registry.js.map +1 -0
  13. package/dist/bot/capability-types.d.ts +23 -0
  14. package/dist/bot/capability-types.d.ts.map +1 -0
  15. package/dist/bot/capability-types.js +9 -0
  16. package/dist/bot/capability-types.js.map +1 -0
  17. package/dist/bot/estimate-tokens.d.ts +12 -0
  18. package/dist/bot/estimate-tokens.d.ts.map +1 -0
  19. package/dist/bot/estimate-tokens.js +18 -0
  20. package/dist/bot/estimate-tokens.js.map +1 -0
  21. package/dist/bot/profile-types.d.ts +4 -0
  22. package/dist/bot/profile-types.d.ts.map +1 -1
  23. package/dist/bot/system-prompt.d.ts +11 -0
  24. package/dist/bot/system-prompt.d.ts.map +1 -1
  25. package/dist/bot/system-prompt.js +31 -0
  26. package/dist/bot/system-prompt.js.map +1 -1
  27. package/dist/bot/types.d.ts +1 -0
  28. package/dist/bot/types.d.ts.map +1 -1
  29. package/dist/node-types/plan-task.d.ts.map +1 -1
  30. package/dist/node-types/plan-task.js +49 -45
  31. package/dist/node-types/plan-task.js.map +1 -1
  32. package/dist/ui/capability-editor.js +657 -0
  33. package/dist/ui/profile-card.js +25 -0
  34. package/dist/ui/profile-editor.js +615 -316
  35. package/dist/ui/swarm-dashboard.js +643 -319
  36. package/flowweaver.manifest.json +1 -1
  37. package/package.json +2 -2
  38. package/src/bot/ai-client.ts +75 -0
  39. package/src/bot/behavior-defaults.ts +20 -0
  40. package/src/bot/capability-registry.ts +230 -0
  41. package/src/bot/capability-types.ts +23 -0
  42. package/src/bot/estimate-tokens.ts +16 -0
  43. package/src/bot/profile-types.ts +4 -0
  44. package/src/bot/system-prompt.ts +33 -0
  45. package/src/bot/types.ts +1 -0
  46. package/src/node-types/plan-task.ts +51 -44
  47. package/src/ui/capability-editor.tsx +420 -0
  48. package/src/ui/profile-card.tsx +20 -0
  49. package/src/ui/profile-editor.tsx +100 -6
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifestVersion": 2,
3
3
  "name": "@synergenius/flow-weaver-pack-weaver",
4
- "version": "0.9.134",
4
+ "version": "0.9.136",
5
5
  "description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
6
6
  "engineVersion": ">=0.22.10",
7
7
  "categories": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver-pack-weaver",
3
- "version": "0.9.134",
3
+ "version": "0.9.136",
4
4
  "description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -70,7 +70,7 @@
70
70
  "dev:install": "npm run dev && node -e \"const p=process.argv[1]; if(!p){console.error('Usage: npm run dev:install -- /path/to/project');process.exit(1)} require('child_process').execSync('npm install --no-save /tmp/synergenius-flow-weaver-pack-weaver-'+require('./package.json').version+'.tgz',{cwd:p,stdio:'inherit'}); require('child_process').execSync('find '+p+' -name fw-exec-\\* -type f -delete',{stdio:'pipe'}); console.log('✓ Installed in '+p+' (cache cleaned)')\" --"
71
71
  },
72
72
  "devDependencies": {
73
- "@synergenius/flow-weaver": "^0.23.5",
73
+ "@synergenius/flow-weaver": "^0.24.0",
74
74
  "@types/node": "^25.3.5",
75
75
  "esbuild": "^0.27.4",
76
76
  "tsx": "^4.0.0",
@@ -2,6 +2,7 @@ import { execSync, spawn } from 'node:child_process';
2
2
  import { trackChild } from './child-process-tracker.js';
3
3
  import type { ProviderInfo } from './types.js';
4
4
  import type { TriageResult, PromptSection } from './system-prompt.js';
5
+ import type { CapabilityDefinition } from './capability-types.js';
5
6
  import { resolveModelTier } from './behavior-defaults.js';
6
7
 
7
8
  // Strip CLAUDECODE from child env so nested claude CLI invocations work.
@@ -490,3 +491,77 @@ export async function callTriage(
490
491
  return null;
491
492
  }
492
493
  }
494
+
495
+ // ---------------------------------------------------------------------------
496
+ // Capability-aware triage — selects capabilities by name
497
+ // ---------------------------------------------------------------------------
498
+
499
+ /**
500
+ * Build the triage system prompt dynamically from available capabilities.
501
+ */
502
+ function buildCapabilityTriagePrompt(available: CapabilityDefinition[]): string {
503
+ const capList = available
504
+ .filter(c => c.name !== 'core') // core is always loaded, don't ask AI about it
505
+ .map(c => `- ${c.name}: ${c.description}`)
506
+ .join('\n');
507
+
508
+ return `You select capabilities for an AI workflow bot. Given a task instruction, determine which capabilities the bot needs from the available list.
509
+
510
+ Return ONLY valid JSON: { "capabilities": ["cap-name-1", "cap-name-2"] }
511
+
512
+ Available capabilities:
513
+ ${capList}
514
+
515
+ Rules:
516
+ - Select ONLY capabilities from the available list.
517
+ - For file creation, modification, or code tasks, always include "file-ops".
518
+ - For tasks involving shell commands, builds, or tests, include "shell".
519
+ - Only select Flow Weaver capabilities (fw-*) when the task explicitly involves workflows, annotations, or validation.
520
+ - Select the MINIMUM set needed — fewer capabilities = faster, cheaper execution.`;
521
+ }
522
+
523
+ /**
524
+ * Capability-aware triage: selects which capabilities a task needs from
525
+ * the profile's available pool. Uses the fast tier (Haiku).
526
+ *
527
+ * Always ensures 'core' is in the result.
528
+ * Returns null on any failure — caller should fall back to all capabilities.
529
+ */
530
+ export async function callCapabilityTriage(
531
+ pInfo: Pick<ProviderInfo, 'type' | 'apiKey'>,
532
+ instruction: string,
533
+ available: CapabilityDefinition[],
534
+ ): Promise<string[] | null> {
535
+ try {
536
+ const triageModel = resolveModelTier('fast', 'anthropic');
537
+ const triagePInfo = { ...pInfo, model: triageModel, maxTokens: 256 };
538
+ const systemPrompt = buildCapabilityTriagePrompt(available);
539
+
540
+ const text = await callAI(triagePInfo, systemPrompt, instruction, 256);
541
+ const parsed = parseJsonResponse(text) as { capabilities?: string[] };
542
+
543
+ if (!parsed.capabilities || !Array.isArray(parsed.capabilities)) return null;
544
+
545
+ // Build set of available names for validation
546
+ const availableNames = new Set(available.map(c => c.name));
547
+
548
+ // Filter to only valid, available capabilities
549
+ const selected = parsed.capabilities.filter(
550
+ (name): name is string => typeof name === 'string' && availableNames.has(name),
551
+ );
552
+
553
+ // Safety: always include core
554
+ if (!selected.includes('core') && availableNames.has('core')) {
555
+ selected.unshift('core');
556
+ }
557
+
558
+ // Safety: if nothing selected (besides core), add file-ops as minimum
559
+ if (selected.length <= 1 && availableNames.has('file-ops')) {
560
+ selected.push('file-ops');
561
+ }
562
+
563
+ return selected;
564
+ } catch {
565
+ return null;
566
+ }
567
+ }
@@ -133,6 +133,24 @@ const STRATEGY_PHASE_OVERRIDES: Record<CostStrategy, Record<string, Partial<Phas
133
133
  },
134
134
  };
135
135
 
136
+ // ---------------------------------------------------------------------------
137
+ // Default capabilities per strategy
138
+ // ---------------------------------------------------------------------------
139
+
140
+ /** Default capability pools per cost strategy. */
141
+ export const STRATEGY_CAPABILITIES: Record<CostStrategy, string[]> = {
142
+ frugal: ['core', 'file-ops', 'shell', 'context'],
143
+ balanced: ['core', 'file-ops', 'shell', 'task-mgmt', 'fw-grammar', 'fw-validate', 'context'],
144
+ performance: ['core', 'file-ops', 'shell', 'task-mgmt', 'fw-grammar', 'fw-validate', 'fw-genesis', 'fw-cli', 'code-review', 'web', 'context'],
145
+ };
146
+
147
+ /** Default budget per strategy (USD per task). */
148
+ export const STRATEGY_BUDGETS: Record<CostStrategy, number> = {
149
+ frugal: 0.10,
150
+ balanced: 0.50,
151
+ performance: 1.00,
152
+ };
153
+
136
154
  /** Strategy-level escalation and exit protocol defaults. */
137
155
  const STRATEGY_DEFAULTS: Record<CostStrategy, { escalation: ProfileBehavior['escalation']; exitProtocol: ProfileBehavior['exitProtocol'] }> = {
138
156
  frugal: { escalation: { maxAttempts: 2, onExhausted: 'block' }, exitProtocol: { reportConcerns: false, requireEvidence: false } },
@@ -172,6 +190,8 @@ export function buildDefaultBehavior(
172
190
  }
173
191
 
174
192
  return {
193
+ capabilities: STRATEGY_CAPABILITIES[strategy],
194
+ budget: STRATEGY_BUDGETS[strategy],
175
195
  phases,
176
196
  ...STRATEGY_DEFAULTS[strategy],
177
197
  };
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Capability registry — manages built-in and custom capability definitions.
3
+ *
4
+ * Built-in capabilities decompose the monolithic 13k system prompt into
5
+ * composable knowledge modules. Triage selects which capabilities a task
6
+ * needs, and only those get loaded into the prompt.
7
+ */
8
+
9
+ import type { CapabilityDefinition } from './capability-types.js';
10
+ import {
11
+ OP_WRITE_FILE, OP_READ_FILE, OP_PATCH_FILE, OP_LIST_FILES,
12
+ OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS,
13
+ OP_TASK_CREATE,
14
+ } from './operations.js';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Built-in capabilities
18
+ // ---------------------------------------------------------------------------
19
+
20
+ const CAP_CORE: CapabilityDefinition = {
21
+ name: 'core',
22
+ description: 'Bot identity, structured plan output format, and safety rules. Always loaded.',
23
+ prompt: `You are Weaver, an expert AI companion for Flow Weaver workflows.
24
+
25
+ ## Plan Format
26
+ Your plans MUST be structured JSON with concrete steps.
27
+ Each step has: operation (tool name), description (what it does), args (complete arguments).
28
+ Do NOT describe what you would do — actually do it by calling tools.
29
+
30
+ ## Safety Rules
31
+ - Writes that shrink a file by >50% or write empty content are automatically BLOCKED.
32
+ - Blocked shell commands: rm -rf, git push, npm publish, sudo, curl|sh.
33
+ - Always validate BEFORE and AFTER patching.
34
+ - Always read a file before patching it (you need exact strings for find/replace).
35
+ - Use patch_file for modifications, write_file only for new files.
36
+ - Be concise — let tool results speak.`,
37
+ };
38
+
39
+ const CAP_FILE_OPS: CapabilityDefinition = {
40
+ name: 'file-ops',
41
+ description: 'File read/write/patch operations and best practices for file manipulation.',
42
+ tools: [OP_READ_FILE, OP_WRITE_FILE, OP_PATCH_FILE, OP_LIST_FILES],
43
+ prompt: `## File Operations
44
+ - read_file: Read a file and return its content. args: { file }
45
+ - write_file: Write a file. args: { file, content }. Content must be the COMPLETE file.
46
+ - patch_file: Surgical find-and-replace edits. args: { file, patches: [{ find: "old text", replace: "new text" }] }. PREFERRED for modifying existing files.
47
+ - list_files: List files in a directory. args: { directory, pattern? } (pattern is regex)
48
+
49
+ ## Best Practices
50
+ PREFER patch_file over write_file for modifying existing files (surgical edits, no truncation risk).
51
+ Use read_file to understand a file before modifying it.
52
+ Use list_files to discover project structure.
53
+ Writes that shrink a file by >50% or write empty content are automatically BLOCKED.`,
54
+ };
55
+
56
+ const CAP_SHELL: CapabilityDefinition = {
57
+ name: 'shell',
58
+ description: 'Shell command execution for running tests, builds, and inspecting output.',
59
+ tools: [OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS],
60
+ prompt: `## Shell Commands
61
+ - run_shell: Execute a shell command and return output. args: { command }
62
+ Use for: npx vitest, git status, grep, find, etc.
63
+ Examples: { "command": "npx vitest run --reporter verbose" }, { "command": "npx flow-weaver validate src/workflow.ts --json" }
64
+ Blocked: rm -rf, git push, npm publish, sudo, curl|sh (safety policy).
65
+ Use run_shell for running tests (npx vitest), validation (flow-weaver validate), and inspecting output.`,
66
+ };
67
+
68
+ const CAP_TASK_MGMT: CapabilityDefinition = {
69
+ name: 'task-mgmt',
70
+ description: 'Create and manage swarm subtasks for parallel execution.',
71
+ tools: [OP_TASK_CREATE],
72
+ prompt: `## Task Management
73
+ - task_create: Create swarm subtasks for parallel execution. args: { title, description, complexity, subtasks[] }
74
+ - task_list, task_get, task_update: Query and update existing tasks
75
+
76
+ Use task_create to decompose complex work into smaller, independent subtasks that other bots can execute in parallel.`,
77
+ };
78
+
79
+ const CAP_FW_GRAMMAR: CapabilityDefinition = {
80
+ name: 'fw-grammar',
81
+ description: 'Flow Weaver annotation syntax, node types, workflows, patterns, and data flow.',
82
+ prompt: `## Core Mental Model
83
+
84
+ 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.
85
+
86
+ Three block types:
87
+ - @flowWeaver nodeType: A reusable function (node) with typed inputs/outputs
88
+ - @flowWeaver workflow: A DAG orchestration that wires node instances together
89
+ - @flowWeaver pattern: A reusable fragment with boundary ports (IN/OUT instead of Start/Exit)
90
+
91
+ ## Node Execution Model
92
+
93
+ Expression nodes (@expression):
94
+ - No execute/onSuccess/onFailure params. Just inputs and return value.
95
+ - throw = failure path, return = success path
96
+ - Synchronous. Use execSync for shell commands.
97
+ - Preferred for deterministic operations.
98
+
99
+ Standard nodes:
100
+ - execute: boolean param gates execution
101
+ - Return { onSuccess: boolean, onFailure: boolean, ...outputs }
102
+ - Can be async for I/O operations
103
+
104
+ Async agent nodes:
105
+ - Use (globalThis as any).__fw_agent_channel__ to pause workflow
106
+ - Call channel.request({ agentId, context, prompt }) which returns a Promise
107
+ - Workflow suspends until agent responds
108
+ - NOT @expression (must be async)
109
+
110
+ Pass-through pattern:
111
+ - FW auto-connects ports by matching names on adjacent nodes
112
+ - To forward data through intermediate nodes, declare it as both @input and @output with the same name
113
+ - For non-adjacent wiring, use @connect sourceNode.port -> targetNode.port
114
+
115
+ Data flow:
116
+ - @path A -> B -> C: Linear execution path (sugar for multiple @connect)
117
+ - @autoConnect: Auto-wire all nodes in declaration order
118
+ - @connect: Explicit port-to-port wiring
119
+ - Merge strategies for fan-in: FIRST, LAST, COLLECT, MERGE, CONCAT`,
120
+ };
121
+
122
+ const CAP_FW_VALIDATE: CapabilityDefinition = {
123
+ name: 'fw-validate',
124
+ description: 'Flow Weaver validation error codes and common fix patterns.',
125
+ prompt: `## Validation Errors
126
+
127
+ When you encounter validation errors, suggest the specific fix. Common resolutions:
128
+ - UNKNOWN_NODE_TYPE: Ensure the referenced function has @flowWeaver nodeType annotation
129
+ - MISSING_REQUIRED_INPUT: Add a @connect from a source port or make the input optional with [brackets]
130
+ - UNKNOWN_SOURCE_PORT / UNKNOWN_TARGET_PORT: Check port name spelling in @connect
131
+ - CYCLE_DETECTED: Break the cycle; use scoped iteration (@scope, @map) instead of circular dependencies`,
132
+ };
133
+
134
+ const CAP_FW_GENESIS: CapabilityDefinition = {
135
+ name: 'fw-genesis',
136
+ description: 'Flow Weaver genesis protocol for self-evolving workflows.',
137
+ prompt: `## Genesis Protocol
138
+
139
+ Genesis is a 17-step self-evolving workflow engine:
140
+ 1. Load config 2. Observe project 3. Load task workflow 4. Diff fingerprint
141
+ 5. Check stabilize mode 6. Wait for agent 7. Propose evolution 8. Validate proposal
142
+ 9. Snapshot for rollback 10. Apply changes 11. Compile and validate
143
+ 12. Diff workflow 13. Check approval threshold 14. Wait for approval
144
+ 15. Commit or rollback 16. Update history 17. Report summary
145
+
146
+ When stabilize mode is active, only fix-up operations are allowed.`,
147
+ };
148
+
149
+ const CAP_FW_CLI: CapabilityDefinition = {
150
+ name: 'fw-cli',
151
+ description: 'Flow Weaver CLI command reference for compile, validate, diagram, and other operations.',
152
+ prompt: `## CLI Commands
153
+
154
+ Note: compile, validate, modify, diff, diagram, and describe operations are available as direct plan steps (no CLI needed).`,
155
+ };
156
+
157
+ const CAP_CODE_REVIEW: CapabilityDefinition = {
158
+ name: 'code-review',
159
+ description: 'Code review guidelines, quality checklist, and security review patterns.',
160
+ prompt: `## Code Review
161
+
162
+ When reviewing code, check for:
163
+ 1. Correctness: Does the code do what the task asked?
164
+ 2. Security: No hardcoded secrets, no injection vulnerabilities, no exposed APIs
165
+ 3. Style: Consistent with project conventions, proper naming, no dead code
166
+ 4. Testing: Are there tests? Do they cover edge cases?
167
+ 5. Performance: No unnecessary loops, no blocking calls in async code
168
+
169
+ Report concerns with specific file:line references and suggested fixes.`,
170
+ };
171
+
172
+ const CAP_WEB: CapabilityDefinition = {
173
+ name: 'web',
174
+ description: 'Web fetch capability for fetching URLs and external resources.',
175
+ tools: ['web_fetch'],
176
+ prompt: `## Web
177
+ - web_fetch(url): Fetch a URL and return its content. Use for API docs, examples, etc.`,
178
+ };
179
+
180
+ const CAP_CONTEXT: CapabilityDefinition = {
181
+ name: 'context',
182
+ description: 'Project file listings, directory structure, and workspace context.',
183
+ prompt: `## Project Context
184
+
185
+ Use list_files to understand the project structure before making changes.
186
+ The context bundle (when available) provides a snapshot of the workspace.`,
187
+ };
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Registry
191
+ // ---------------------------------------------------------------------------
192
+
193
+ /** All built-in capability definitions. */
194
+ export const BUILT_IN_CAPABILITIES: readonly CapabilityDefinition[] = [
195
+ CAP_CORE,
196
+ CAP_FILE_OPS,
197
+ CAP_SHELL,
198
+ CAP_TASK_MGMT,
199
+ CAP_FW_GRAMMAR,
200
+ CAP_FW_VALIDATE,
201
+ CAP_FW_GENESIS,
202
+ CAP_FW_CLI,
203
+ CAP_CODE_REVIEW,
204
+ CAP_WEB,
205
+ CAP_CONTEXT,
206
+ ];
207
+
208
+ const capabilityMap = new Map<string, CapabilityDefinition>(
209
+ BUILT_IN_CAPABILITIES.map(c => [c.name, c]),
210
+ );
211
+
212
+ /** Get a single capability by name. */
213
+ export function getCapability(name: string): CapabilityDefinition | undefined {
214
+ return capabilityMap.get(name);
215
+ }
216
+
217
+ /** List all available capabilities. */
218
+ export function listCapabilities(): CapabilityDefinition[] {
219
+ return [...BUILT_IN_CAPABILITIES];
220
+ }
221
+
222
+ /**
223
+ * Get multiple capabilities by name, preserving input order.
224
+ * Unknown names are silently skipped.
225
+ */
226
+ export function getCapabilitiesByNames(names: string[]): CapabilityDefinition[] {
227
+ return names
228
+ .map(n => capabilityMap.get(n))
229
+ .filter((c): c is CapabilityDefinition => c !== undefined);
230
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Capability definitions for the bot system.
3
+ *
4
+ * A Capability is a composable unit of knowledge + tools + constraints.
5
+ * Profiles declare which capabilities their bots may use.
6
+ * Triage selects which capabilities a specific task needs.
7
+ */
8
+
9
+ /**
10
+ * A capability definition — bundles knowledge, tools, and constraints.
11
+ * Built-in capabilities ship with the pack. Custom capabilities can be
12
+ * added per-workspace via .weaver/capabilities/.
13
+ */
14
+ export interface CapabilityDefinition {
15
+ /** Unique identifier (e.g. 'typescript', 'fw-grammar'). */
16
+ name: string;
17
+ /** Short description — fed to triage for relevance scoring. */
18
+ description: string;
19
+ /** Knowledge content injected into the system prompt (markdown). */
20
+ prompt: string;
21
+ /** Tool operation names this capability grants (e.g. ['write_file', 'patch_file']). */
22
+ tools?: string[];
23
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Estimate token count for a text string without a tokenizer library.
3
+ *
4
+ * Uses the average of two heuristics:
5
+ * - chars / 3.5 — better for code and mixed content
6
+ * - words × 1.33 — more stable across content types
7
+ *
8
+ * Accuracy: within ~15% for English prose + code. For precise counts
9
+ * use Anthropic's count_tokens API or @anthropic-ai/tokenizer.
10
+ */
11
+ export function estimateTokens(text: string): number {
12
+ if (!text) return 0;
13
+ const byChars = text.length / 3.5;
14
+ const byWords = text.split(/\s+/).filter(Boolean).length * 1.33;
15
+ return Math.ceil((byChars + byWords) / 2);
16
+ }
@@ -93,6 +93,10 @@ export interface PhaseDescriptor {
93
93
  * bot might have analyze/report/suggest instead of plan/execute/review).
94
94
  */
95
95
  export interface ProfileBehavior {
96
+ /** Capability names this profile can use (e.g. ['core', 'file-ops', 'shell']). */
97
+ capabilities?: string[];
98
+ /** Per-task budget limit in USD. */
99
+ budget?: number;
96
100
  /** Per-phase toggles, model overrides, and token budgets. Keys match workflow phase gates. */
97
101
  phases: Record<string, PhaseBehavior>;
98
102
  /** Retry/escalation policy. */
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { FILE_OPERATIONS, OP_TASK_CREATE, OP_PATCH_FILE, OP_WRITE_FILE } from './operations.js';
7
+ import type { CapabilityDefinition } from './capability-types.js';
7
8
 
8
9
  interface AnnotationDoc {
9
10
  name: string;
@@ -323,3 +324,35 @@ Be concise in your text responses — let tool results speak.`;
323
324
 
324
325
  return prompt;
325
326
  }
327
+
328
+ // ---------------------------------------------------------------------------
329
+ // Capability-based prompt assembly
330
+ // ---------------------------------------------------------------------------
331
+
332
+ /**
333
+ * Build a system prompt by concatenating the prompts of selected capabilities.
334
+ * This replaces the section-based buildPromptFromMetadata for capability-aware tasks.
335
+ */
336
+ export function buildPromptFromCapabilities(capabilities: CapabilityDefinition[]): string {
337
+ if (capabilities.length === 0) return '';
338
+ return capabilities.map(c => c.prompt).join('\n\n');
339
+ }
340
+
341
+ /**
342
+ * Collect all tool operation names granted by the selected capabilities.
343
+ * Returns a deduplicated array.
344
+ */
345
+ export function collectToolsFromCapabilities(capabilities: CapabilityDefinition[]): string[] {
346
+ const seen = new Set<string>();
347
+ const tools: string[] = [];
348
+ for (const cap of capabilities) {
349
+ if (!cap.tools) continue;
350
+ for (const t of cap.tools) {
351
+ if (!seen.has(t)) {
352
+ seen.add(t);
353
+ tools.push(t);
354
+ }
355
+ }
356
+ }
357
+ return tools;
358
+ }
package/src/bot/types.ts CHANGED
@@ -622,6 +622,7 @@ export interface WeaverContext {
622
622
  runId?: string;
623
623
  targetPath?: string;
624
624
  taskJson?: string;
625
+ behaviorJson?: string;
625
626
  hasTask?: boolean;
626
627
  contextBundle?: string;
627
628
  planJson?: string;
@@ -1,45 +1,49 @@
1
1
  import type { WeaverContext } from '../bot/types.js';
2
- import { callAI, callAIWithTools, callTriage, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
2
+ import { callAIWithTools, callCapabilityTriage, parseJsonResponse, normalizePlan } from '../bot/ai-client.js';
3
3
  import type { AiTool } from '../bot/ai-client.js';
4
4
  import { auditEmit } from '../bot/audit-logger.js';
5
- import type { PromptSection } from '../bot/system-prompt.js';
6
5
  import { PLAN_OPERATIONS } from '../bot/operations.js';
6
+ import { getCapabilitiesByNames, BUILT_IN_CAPABILITIES } from '../bot/capability-registry.js';
7
7
 
8
8
  // ---------------------------------------------------------------------------
9
9
  // Plan tool definition — passed via native tool_use so the AI returns
10
10
  // structured JSON arguments instead of prose. Works across Anthropic and OpenAI.
11
11
  // ---------------------------------------------------------------------------
12
12
 
13
- const PLAN_TOOL: AiTool = {
14
- name: 'create_plan',
15
- description: 'Create a structured execution plan for this task. Call this tool with the plan steps and summary.',
16
- parameters: {
17
- type: 'object',
18
- properties: {
19
- summary: { type: 'string', description: 'One-line summary of what the plan does' },
20
- steps: {
21
- type: 'array',
22
- description: 'Ordered list of operations to execute',
23
- items: {
24
- type: 'object',
25
- properties: {
26
- operation: {
27
- type: 'string',
28
- description: `Tool to invoke: ${PLAN_OPERATIONS.join(', ')}`,
29
- },
30
- description: { type: 'string', description: 'What this step does' },
31
- args: {
32
- type: 'object',
33
- description: 'Arguments for the operation (e.g. {file, content} for write_file, {file, find, replace} for patch_file)',
13
+ /** Build the create_plan tool definition with dynamic operation list. */
14
+ function buildPlanTool(operations: string[]): AiTool {
15
+ const ops = operations.length > 0 ? operations : PLAN_OPERATIONS as unknown as string[];
16
+ return {
17
+ name: 'create_plan',
18
+ description: 'Create a structured execution plan for this task. Call this tool with the plan steps and summary.',
19
+ parameters: {
20
+ type: 'object',
21
+ properties: {
22
+ summary: { type: 'string', description: 'One-line summary of what the plan does' },
23
+ steps: {
24
+ type: 'array',
25
+ description: 'Ordered list of operations to execute',
26
+ items: {
27
+ type: 'object',
28
+ properties: {
29
+ operation: {
30
+ type: 'string',
31
+ description: `Tool to invoke: ${ops.join(', ')}`,
32
+ },
33
+ description: { type: 'string', description: 'What this step does' },
34
+ args: {
35
+ type: 'object',
36
+ description: 'Arguments for the operation (e.g. {file, content} for write_file, {file, find, replace} for patch_file)',
37
+ },
34
38
  },
39
+ required: ['operation', 'description', 'args'],
35
40
  },
36
- required: ['operation', 'description', 'args'],
37
41
  },
38
42
  },
43
+ required: ['summary', 'steps'],
39
44
  },
40
- required: ['summary', 'steps'],
41
- },
42
- };
45
+ };
46
+ }
43
47
 
44
48
  /**
45
49
  * Sends task + context to the AI provider and gets back a structured
@@ -81,28 +85,24 @@ export async function weaverPlanTask(
81
85
  }
82
86
  const task = JSON.parse(context.taskJson);
83
87
 
84
- // Triage: cheap fast-model call to classify what the task needs.
85
- // Scopes the system prompt to only include relevant sections.
86
- let sections: Set<PromptSection> | undefined;
87
- const triageResult = await callTriage(pInfo, task.instruction);
88
+ // Resolve available capabilities from behavior config or defaults
89
+ const behavior = context.behaviorJson ? JSON.parse(context.behaviorJson) : undefined;
90
+ const availableCapNames: string[] = behavior?.capabilities ?? BUILT_IN_CAPABILITIES.map(c => c.name);
91
+ const availableCaps = getCapabilitiesByNames(availableCapNames);
92
+
93
+ // Capability triage: cheap Haiku call selects which capabilities this task needs
94
+ let selectedCaps = availableCaps; // fallback: all available
95
+ const triageResult = await callCapabilityTriage(pInfo, task.instruction, availableCaps);
88
96
  if (triageResult) {
89
- sections = new Set<PromptSection>();
90
- for (const [key, needed] of Object.entries(triageResult.needs)) {
91
- if (needed) sections.add(key as PromptSection);
92
- }
97
+ selectedCaps = getCapabilitiesByNames(triageResult);
93
98
  }
94
- // If triage failed, sections is undefined full prompt (no degradation)
99
+ // If triage failed, selectedCaps = all available (no degradation)
95
100
 
96
101
  let systemPrompt: string;
97
102
  try {
98
103
  const mod = await import('../bot/system-prompt.js');
99
- const basePrompt = await mod.buildSystemPrompt(sections);
100
- let cliCommands: { name: string; description: string; group?: string; botCompatible?: boolean; options?: { flags: string; arg?: string; description: string }[] }[] = [];
101
- try {
102
- const docMeta = await import('@synergenius/flow-weaver/doc-metadata');
103
- cliCommands = docMeta.CLI_COMMANDS ?? [];
104
- } catch { /* older flow-weaver version */ }
105
- const botPrompt = mod.buildBotSystemPrompt(context.contextBundle!, cliCommands, undefined, sections);
104
+ const basePrompt = mod.buildPromptFromCapabilities(selectedCaps);
105
+ const botPrompt = mod.buildBotSystemPrompt(context.contextBundle!, undefined, context.env?.projectDir);
106
106
  systemPrompt = basePrompt + '\n\n' + botPrompt;
107
107
  } catch (err) {
108
108
  if (process.env.WEAVER_VERBOSE) console.error('[plan-task] system prompt build failed:', err);
@@ -119,9 +119,16 @@ Rules:
119
119
  4. Do NOT plan patch_file unless you know the exact find/replace strings.`;
120
120
 
121
121
  try {
122
+ // Build plan tool with operations scoped to selected capabilities
123
+ const mod2 = await import('../bot/system-prompt.js');
124
+ const grantedTools = mod2.collectToolsFromCapabilities(selectedCaps);
125
+ // Always include respond in the available operations
126
+ const planOps = grantedTools.length > 0 ? [...grantedTools, 'respond'] : PLAN_OPERATIONS as unknown as string[];
127
+ const planTool = buildPlanTool(planOps);
128
+
122
129
  // Use native tool_use when available (Anthropic, OpenAI).
123
130
  // The AI calls create_plan with structured args instead of returning prose.
124
- const result = await callAIWithTools(pInfo, systemPrompt, userPrompt, [PLAN_TOOL], 8192);
131
+ const result = await callAIWithTools(pInfo, systemPrompt, userPrompt, [planTool], 8192);
125
132
 
126
133
  let plan: { steps: Array<{ id: string; operation: string; description: string; args: Record<string, unknown> }>; summary: string };
127
134