moflo 4.9.21 → 4.9.23
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/.claude/agents/analysis/analyze-code-quality.md +0 -121
- package/.claude/agents/analysis/code-analyzer.md +5 -26
- package/.claude/agents/architecture/system-design/arch-system-design.md +0 -119
- package/.claude/agents/base-template-generator.md +0 -1
- package/.claude/agents/core/coder.md +0 -22
- package/.claude/agents/core/planner.md +0 -16
- package/.claude/agents/core/researcher.md +0 -16
- package/.claude/agents/core/reviewer.md +0 -17
- package/.claude/agents/core/tester.md +0 -19
- package/.claude/agents/custom/test-long-runner.md +0 -2
- package/.claude/agents/development/dev-backend-api.md +0 -167
- package/.claude/agents/development/dev-database.md +43 -0
- package/.claude/agents/development/dev-frontend.md +42 -0
- package/.claude/agents/devops/ci-cd/ops-cicd-github.md +0 -112
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +0 -111
- package/.claude/agents/security/security-auditor.md +45 -0
- package/.claude/guidance/shipped/moflo-cli-reference.md +19 -16
- package/.claude/guidance/shipped/moflo-core-guidance.md +0 -2
- package/.claude/guidance/shipped/moflo-guidance-rules.md +5 -5
- package/.claude/guidance/shipped/moflo-spell-runner.md +1 -0
- package/.claude/guidance/shipped/moflo-spell-scheduling.md +225 -0
- package/.claude/guidance/shipped/moflo-spell-troubleshooting.md +1 -0
- package/.claude/helpers/gate.cjs +70 -3
- package/.claude/skills/fl/execution-modes.md +38 -15
- package/.claude/skills/fl/phases.md +67 -0
- package/.claude/skills/spell-schedule/SKILL.md +18 -5
- package/README.md +1 -1
- package/bin/gate.cjs +70 -3
- package/bin/index-guidance.mjs +32 -6
- package/bin/lib/retired-files.mjs +146 -0
- package/bin/session-start-launcher.mjs +116 -8
- package/dist/src/cli/appliance/rvfa-builder.js +1 -1
- package/dist/src/cli/commands/agent.js +3 -9
- package/dist/src/cli/commands/daemon.js +13 -17
- package/dist/src/cli/commands/hooks.js +4 -9
- package/dist/src/cli/commands/index.js +2 -0
- package/dist/src/cli/commands/retire.js +111 -0
- package/dist/src/cli/commands/spell-schedule.js +237 -49
- package/dist/src/cli/hooks/reasoningbank/index.js +7 -7
- package/dist/src/cli/init/executor.js +26 -54
- package/dist/src/cli/init/helpers-generator.js +66 -3
- package/dist/src/cli/init/settings-generator.js +17 -6
- package/dist/src/cli/mcp-tools/agent-tools.js +9 -27
- package/dist/src/cli/mcp-tools/hooks-tools.js +23 -21
- package/dist/src/cli/mcp-tools/memory-tools.js +16 -5
- package/dist/src/cli/memory/bridge-embedder.js +26 -6
- package/dist/src/cli/memory/bridge-entries.js +33 -15
- package/dist/src/cli/memory/controllers/semantic-router.js +18 -12
- package/dist/src/cli/memory/sona-optimizer.js +6 -6
- package/dist/src/cli/neural/domain/services/learning-service.js +3 -3
- package/dist/src/cli/services/agent-router.js +2 -5
- package/dist/src/cli/services/daemon-autostart-lifecycle.js +62 -0
- package/dist/src/cli/services/daemon-dashboard.js +187 -18
- package/dist/src/cli/services/daemon-readiness.js +19 -31
- package/dist/src/cli/services/ephemeral-namespace-purge.js +61 -33
- package/dist/src/cli/services/headless-worker-executor.js +7 -94
- package/dist/src/cli/services/hook-block-hash.js +4 -0
- package/dist/src/cli/services/worker-daemon.js +40 -66
- package/dist/src/cli/shared/events/example-usage.js +6 -6
- package/dist/src/cli/shared/hooks/task-hooks.js +8 -8
- package/dist/src/cli/spells/core/runner.js +12 -0
- package/dist/src/cli/spells/scheduler/scheduler.js +24 -9
- package/dist/src/cli/spells/schema/validator.js +2 -1
- package/dist/src/cli/spells/schema/validators/top-level.js +18 -0
- package/dist/src/cli/version.js +1 -1
- package/package.json +5 -2
- package/retired-files.json +1989 -0
- package/src/cli/data/model-registry.json +2 -2
- package/.claude/agents/consensus/byzantine-coordinator.md +0 -63
- package/.claude/agents/consensus/crdt-synchronizer.md +0 -997
- package/.claude/agents/consensus/gossip-coordinator.md +0 -63
- package/.claude/agents/consensus/performance-benchmarker.md +0 -851
- package/.claude/agents/consensus/quorum-manager.md +0 -823
- package/.claude/agents/consensus/raft-manager.md +0 -63
- package/.claude/agents/consensus/security-manager.md +0 -622
- package/.claude/agents/data/ml/data-ml-model.md +0 -193
- package/.claude/agents/github/code-review-swarm.md +0 -538
- package/.claude/agents/github/github-modes.md +0 -172
- package/.claude/agents/github/issue-tracker.md +0 -311
- package/.claude/agents/github/multi-repo-swarm.md +0 -551
- package/.claude/agents/github/pr-manager.md +0 -183
- package/.claude/agents/github/project-board-sync.md +0 -508
- package/.claude/agents/github/release-manager.md +0 -360
- package/.claude/agents/github/release-swarm.md +0 -580
- package/.claude/agents/github/repo-architect.md +0 -391
- package/.claude/agents/github/swarm-issue.md +0 -566
- package/.claude/agents/github/swarm-pr.md +0 -414
- package/.claude/agents/github/sync-coordinator.md +0 -426
- package/.claude/agents/github/workflow-automation.md +0 -606
- package/.claude/agents/goal/code-goal-planner.md +0 -440
- package/.claude/agents/goal/goal-planner.md +0 -168
- package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +0 -127
- package/.claude/agents/hive-mind/queen-coordinator.md +0 -198
- package/.claude/agents/hive-mind/scout-explorer.md +0 -233
- package/.claude/agents/hive-mind/swarm-memory-manager.md +0 -184
- package/.claude/agents/hive-mind/worker-specialist.md +0 -208
- package/.claude/agents/neural/safla-neural.md +0 -73
- package/.claude/agents/optimization/benchmark-suite.md +0 -665
- package/.claude/agents/optimization/load-balancer.md +0 -431
- package/.claude/agents/optimization/performance-monitor.md +0 -672
- package/.claude/agents/optimization/resource-allocator.md +0 -674
- package/.claude/agents/optimization/topology-optimizer.md +0 -808
- package/.claude/agents/reasoning/goal-planner.md +0 -67
- package/.claude/agents/sona/sona-learning-optimizer.md +0 -74
- package/.claude/agents/sparc/architecture.md +0 -472
- package/.claude/agents/sparc/pseudocode.md +0 -318
- package/.claude/agents/sparc/refinement.md +0 -525
- package/.claude/agents/sparc/specification.md +0 -276
- package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +0 -225
- package/.claude/agents/swarm/adaptive-coordinator.md +0 -391
- package/.claude/agents/swarm/hierarchical-coordinator.md +0 -321
- package/.claude/agents/swarm/mesh-coordinator.md +0 -383
- package/.claude/agents/testing/production-validator.md +0 -395
- package/.claude/agents/testing/tdd-london-swarm.md +0 -244
- package/.claude/agents/v3/adr-architect.md +0 -184
- package/.claude/agents/v3/aidefence-guardian.md +0 -277
- package/.claude/agents/v3/claims-authorizer.md +0 -208
- package/.claude/agents/v3/collective-intelligence-coordinator.md +0 -988
- package/.claude/agents/v3/ddd-domain-expert.md +0 -220
- package/.claude/agents/v3/injection-analyst.md +0 -232
- package/.claude/agents/v3/memory-specialist.md +0 -987
- package/.claude/agents/v3/performance-engineer.md +0 -1225
- package/.claude/agents/v3/pii-detector.md +0 -146
- package/.claude/agents/v3/reasoningbank-learner.md +0 -213
- package/.claude/agents/v3/security-architect-aidefence.md +0 -405
- package/.claude/agents/v3/security-architect.md +0 -865
- package/.claude/agents/v3/security-auditor.md +0 -771
- package/.claude/agents/v3/sparc-orchestrator.md +0 -182
- package/.claude/agents/v3/swarm-memory-manager.md +0 -142
- package/.claude/agents/v3/v3-integration-architect.md +0 -205
- package/.claude/commands/claude-flow-help.md +0 -103
- package/.claude/commands/claude-flow-memory.md +0 -107
- package/.claude/commands/claude-flow-swarm.md +0 -205
- package/.claude/commands/flo-simplify.md +0 -101
- package/.claude/commands/github/README.md +0 -11
- package/.claude/commands/github/code-review-swarm.md +0 -514
- package/.claude/commands/github/code-review.md +0 -25
- package/.claude/commands/github/github-modes.md +0 -146
- package/.claude/commands/github/github-swarm.md +0 -113
- package/.claude/commands/github/issue-tracker.md +0 -284
- package/.claude/commands/github/issue-triage.md +0 -25
- package/.claude/commands/github/multi-repo-swarm.md +0 -519
- package/.claude/commands/github/pr-enhance.md +0 -26
- package/.claude/commands/github/pr-manager.md +0 -164
- package/.claude/commands/github/project-board-sync.md +0 -471
- package/.claude/commands/github/release-manager.md +0 -332
- package/.claude/commands/github/release-swarm.md +0 -544
- package/.claude/commands/github/repo-analyze.md +0 -25
- package/.claude/commands/github/repo-architect.md +0 -361
- package/.claude/commands/github/swarm-issue.md +0 -482
- package/.claude/commands/github/swarm-pr.md +0 -285
- package/.claude/commands/github/sync-coordinator.md +0 -294
- package/.claude/commands/github/workflow-automation.md +0 -442
- package/.claude/commands/hooks/README.md +0 -11
- package/.claude/commands/hooks/overview.md +0 -58
- package/.claude/commands/hooks/post-edit.md +0 -117
- package/.claude/commands/hooks/post-task.md +0 -112
- package/.claude/commands/hooks/pre-edit.md +0 -113
- package/.claude/commands/hooks/pre-task.md +0 -111
- package/.claude/commands/hooks/session-end.md +0 -118
- package/.claude/commands/hooks/setup.md +0 -103
- package/.claude/commands/sparc/analyzer.md +0 -42
- package/.claude/commands/sparc/architect.md +0 -43
- package/.claude/commands/sparc/ask.md +0 -86
- package/.claude/commands/sparc/batch-executor.md +0 -44
- package/.claude/commands/sparc/code.md +0 -78
- package/.claude/commands/sparc/coder.md +0 -44
- package/.claude/commands/sparc/debug.md +0 -72
- package/.claude/commands/sparc/debugger.md +0 -44
- package/.claude/commands/sparc/designer.md +0 -43
- package/.claude/commands/sparc/devops.md +0 -98
- package/.claude/commands/sparc/docs-writer.md +0 -69
- package/.claude/commands/sparc/documenter.md +0 -44
- package/.claude/commands/sparc/innovator.md +0 -44
- package/.claude/commands/sparc/integration.md +0 -72
- package/.claude/commands/sparc/mcp.md +0 -106
- package/.claude/commands/sparc/memory-manager.md +0 -44
- package/.claude/commands/sparc/optimizer.md +0 -44
- package/.claude/commands/sparc/orchestrator.md +0 -116
- package/.claude/commands/sparc/post-deployment-monitoring-mode.md +0 -72
- package/.claude/commands/sparc/refinement-optimization-mode.md +0 -72
- package/.claude/commands/sparc/researcher.md +0 -44
- package/.claude/commands/sparc/reviewer.md +0 -44
- package/.claude/commands/sparc/security-review.md +0 -69
- package/.claude/commands/sparc/sparc-modes.md +0 -139
- package/.claude/commands/sparc/sparc.md +0 -99
- package/.claude/commands/sparc/spec-pseudocode.md +0 -69
- package/.claude/commands/sparc/spell-manager.md +0 -44
- package/.claude/commands/sparc/supabase-admin.md +0 -337
- package/.claude/commands/sparc/swarm-coordinator.md +0 -44
- package/.claude/commands/sparc/tdd.md +0 -44
- package/.claude/commands/sparc/tester.md +0 -44
- package/.claude/commands/sparc/tutorial.md +0 -68
- package/.claude/commands/sparc.md +0 -151
package/bin/gate.cjs
CHANGED
|
@@ -7,7 +7,7 @@ var cp = require('child_process');
|
|
|
7
7
|
var PROJECT_DIR = (process.env.CLAUDE_PROJECT_DIR || process.cwd()).replace(/^\/([a-z])\//i, '$1:/');
|
|
8
8
|
var STATE_FILE = path.join(PROJECT_DIR, '.claude', 'workflow-state.json');
|
|
9
9
|
|
|
10
|
-
var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, simplifySnapshotSha: null, interactionCount: 0, sessionStart: null, lastBlockedAt: null, lastNamespaceHint: '', lastNamespaceHintEmittedBy: {} };
|
|
10
|
+
var STATE_DEFAULTS = { tasksCreated: false, taskCount: 0, memorySearched: false, memorySearchedBy: {}, memoryRequired: true, learningsStored: false, testsRun: false, simplifyRun: false, simplifySnapshotSha: null, interactionCount: 0, sessionStart: null, lastBlockedAt: null, lastNamespaceHint: '', lastNamespaceHintEmittedBy: {}, flMode: null, swarmInitialized: false, hiveInitialized: false };
|
|
11
11
|
|
|
12
12
|
// Per-actor memory-search tracking (#838). The legacy `memorySearched` boolean
|
|
13
13
|
// is session-wide, so once the parent searches memory, every spawned subagent
|
|
@@ -60,7 +60,7 @@ function writeState(s) {
|
|
|
60
60
|
|
|
61
61
|
// Load moflo.yaml gate config (defaults: all enabled)
|
|
62
62
|
function loadGateConfig() {
|
|
63
|
-
var defaults = { memory_first: true, task_create_first: true, context_tracking: true, testing_gate: true, simplify_gate: true, learnings_gate: true };
|
|
63
|
+
var defaults = { memory_first: true, task_create_first: true, context_tracking: true, testing_gate: true, simplify_gate: true, learnings_gate: true, swarm_invocation_gate: true };
|
|
64
64
|
try {
|
|
65
65
|
var yamlPath = path.join(PROJECT_DIR, 'moflo.yaml');
|
|
66
66
|
if (fs.existsSync(yamlPath)) {
|
|
@@ -71,6 +71,7 @@ function loadGateConfig() {
|
|
|
71
71
|
if (/testing_gate:\s*false/i.test(content)) defaults.testing_gate = false;
|
|
72
72
|
if (/simplify_gate:\s*false/i.test(content)) defaults.simplify_gate = false;
|
|
73
73
|
if (/learnings_gate:\s*false/i.test(content)) defaults.learnings_gate = false;
|
|
74
|
+
if (/swarm_invocation_gate:\s*false/i.test(content)) defaults.swarm_invocation_gate = false;
|
|
74
75
|
}
|
|
75
76
|
} catch (e) { /* use defaults */ }
|
|
76
77
|
return defaults;
|
|
@@ -111,6 +112,21 @@ var NS_NAV_RES = [
|
|
|
111
112
|
/\b(class|function|method|component|service|entity|module)\b/,
|
|
112
113
|
];
|
|
113
114
|
|
|
115
|
+
// Detect whether the current prompt invoked /fl or /flo with a swarm/hive flag (#952).
|
|
116
|
+
// When set, check-before-agent BLOCKS the Agent spawn until the matching MCP init
|
|
117
|
+
// (mcp__moflo__swarm_init or mcp__moflo__hive-mind_init) has been recorded — the user
|
|
118
|
+
// explicitly opted in to the protected coordination surface, so falling back to
|
|
119
|
+
// raw Agent dispatch silently regresses headline moflo product capability.
|
|
120
|
+
//
|
|
121
|
+
// SYNC: duplicated verbatim in src/cli/init/helpers-generator.ts.
|
|
122
|
+
function detectFlMode(promptText) {
|
|
123
|
+
var p = promptText || '';
|
|
124
|
+
if (!/^\s*\/(?:fl|flo)\b/i.test(p)) return null;
|
|
125
|
+
if (/(?:^|\s)(?:-s|--swarm)\b/.test(p)) return 'swarm';
|
|
126
|
+
if (/(?:^|\s)(?:-h|--hive)\b/.test(p)) return 'hive';
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
114
130
|
function classifyNamespaceHint(promptText) {
|
|
115
131
|
var lower = (promptText || '').toLowerCase();
|
|
116
132
|
if (NS_TEST_RE.test(lower)) return 'Memory namespace hint: use "tests" for test inventory and coverage lookups.';
|
|
@@ -154,6 +170,12 @@ function applyPromptStateReset(state, promptText) {
|
|
|
154
170
|
// subsequent agents (parent + subagents that spawn their own agents) all
|
|
155
171
|
// see the new classification on their first check-before-agent.
|
|
156
172
|
state.lastNamespaceHintEmittedBy = {};
|
|
173
|
+
// #952 — derive flMode from the user prompt, and reset the matching init
|
|
174
|
+
// flag. Each /fl invocation must call its protected MCP init; the previous
|
|
175
|
+
// prompt's swarm/hive registration does not satisfy this prompt's gate.
|
|
176
|
+
state.flMode = detectFlMode(promptText);
|
|
177
|
+
state.swarmInitialized = false;
|
|
178
|
+
state.hiveInitialized = false;
|
|
157
179
|
}
|
|
158
180
|
// Match npm/yarn/pnpm/bun test, npx vitest|jest|..., bare runners at command-start only,
|
|
159
181
|
// and language-native test commands. The bare-runner arm is anchored so that
|
|
@@ -305,6 +327,47 @@ switch (command) {
|
|
|
305
327
|
writeState(s);
|
|
306
328
|
}
|
|
307
329
|
}
|
|
330
|
+
// #952 — when /fl was invoked with -s/-h, the protected MCP init must run
|
|
331
|
+
// BEFORE any Agent spawn. Hard block: the user explicitly opted in to
|
|
332
|
+
// moflo's coordination surface, so silently dispatching `Agent` calls
|
|
333
|
+
// without `mcp__moflo__swarm_init` / `mcp__moflo__hive-mind_init` is the
|
|
334
|
+
// failure mode this gate exists to prevent (CLAUDE.md "⛔ Protected
|
|
335
|
+
// functionality — swarm + hive-mind"). Other Agent uses remain advisory.
|
|
336
|
+
if (config.swarm_invocation_gate) {
|
|
337
|
+
if (s.flMode === 'swarm' && !s.swarmInitialized) {
|
|
338
|
+
process.stderr.write('BLOCKED: /fl was invoked with -s/--swarm but mcp__moflo__swarm_init has not been called.\n');
|
|
339
|
+
process.stderr.write('Run mcp__moflo__swarm_init first, then mcp__moflo__agent_spawn for each role, then dispatch Agent.\n');
|
|
340
|
+
process.stderr.write('See .claude/skills/fl/execution-modes.md "SWARM mode" and CLAUDE.md "⛔ Protected functionality".\n');
|
|
341
|
+
process.stderr.write('Disable via moflo.yaml: gates: swarm_invocation_gate: false\n');
|
|
342
|
+
process.exit(2);
|
|
343
|
+
}
|
|
344
|
+
if (s.flMode === 'hive' && !s.hiveInitialized) {
|
|
345
|
+
process.stderr.write('BLOCKED: /fl was invoked with -h/--hive but mcp__moflo__hive-mind_init has not been called.\n');
|
|
346
|
+
process.stderr.write('Run mcp__moflo__hive-mind_init first, then dispatch Agent or hive-mind workers.\n');
|
|
347
|
+
process.stderr.write('See .claude/skills/fl/execution-modes.md "HIVE-MIND mode" and CLAUDE.md "⛔ Protected functionality".\n');
|
|
348
|
+
process.stderr.write('Disable via moflo.yaml: gates: swarm_invocation_gate: false\n');
|
|
349
|
+
process.exit(2);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
case 'record-swarm-init': {
|
|
355
|
+
// #952 — wired to mcp__moflo__swarm_init PostToolUse. Marks the gate
|
|
356
|
+
// satisfied so subsequent Agent spawns under /fl -s pass.
|
|
357
|
+
var s = readState();
|
|
358
|
+
if (!s.swarmInitialized) {
|
|
359
|
+
s.swarmInitialized = true;
|
|
360
|
+
writeState(s);
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case 'record-hive-init': {
|
|
365
|
+
// #952 — wired to mcp__moflo__hive-mind_init PostToolUse.
|
|
366
|
+
var s = readState();
|
|
367
|
+
if (!s.hiveInitialized) {
|
|
368
|
+
s.hiveInitialized = true;
|
|
369
|
+
writeState(s);
|
|
370
|
+
}
|
|
308
371
|
break;
|
|
309
372
|
}
|
|
310
373
|
case 'check-before-scan': {
|
|
@@ -508,7 +571,11 @@ switch (command) {
|
|
|
508
571
|
break;
|
|
509
572
|
}
|
|
510
573
|
case 'session-reset': {
|
|
511
|
-
|
|
574
|
+
// Derive from STATE_DEFAULTS so adding a new state field requires only one
|
|
575
|
+
// edit (the defaults object) — the literal that used to live here drifted
|
|
576
|
+
// every time a field was added and is what motivated #952's audit of state
|
|
577
|
+
// shape consistency.
|
|
578
|
+
writeState(Object.assign({}, STATE_DEFAULTS, { sessionStart: new Date().toISOString() }));
|
|
512
579
|
break;
|
|
513
580
|
}
|
|
514
581
|
default:
|
package/bin/index-guidance.mjs
CHANGED
|
@@ -131,6 +131,18 @@ function loadGuidanceDirs() {
|
|
|
131
131
|
// 3. CLAUDE.md files are NOT indexed — Claude loads them into context automatically.
|
|
132
132
|
// Indexing them wastes vectors and creates duplicate keys across subprojects.
|
|
133
133
|
|
|
134
|
+
// 4. Project skills — index .claude/skills/<name>/SKILL.md
|
|
135
|
+
const projectSkillsDir = resolve(projectRoot, '.claude/skills');
|
|
136
|
+
if (existsSync(projectSkillsDir)) {
|
|
137
|
+
dirs.push({ path: '.claude/skills', prefix: 'skill', fileFilter: ['SKILL.md'], kind: 'skill' });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 5. Bundled moflo skills — gated by isSelfRef to prevent double-indexing
|
|
141
|
+
const bundledSkillsDir = resolve(mofloRoot, '.claude/skills');
|
|
142
|
+
if (!isSelfRef && existsSync(bundledSkillsDir) && resolve(bundledSkillsDir) !== resolve(projectSkillsDir)) {
|
|
143
|
+
dirs.push({ path: bundledSkillsDir, prefix: 'skill-bundled', fileFilter: ['SKILL.md'], kind: 'skill', absolute: true });
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
return dirs;
|
|
135
147
|
}
|
|
136
148
|
|
|
@@ -513,10 +525,12 @@ function buildHierarchy(chunks, chunkPrefix) {
|
|
|
513
525
|
return hierarchy;
|
|
514
526
|
}
|
|
515
527
|
|
|
516
|
-
function indexFile(db, filePath, keyPrefix) {
|
|
517
|
-
const fileName = basename(filePath, extname(filePath));
|
|
528
|
+
function indexFile(db, filePath, keyPrefix, options = {}) {
|
|
529
|
+
const fileName = options.nameOverride || basename(filePath, extname(filePath));
|
|
518
530
|
const docKey = `doc-${keyPrefix}-${fileName}`;
|
|
519
531
|
const chunkPrefix = `chunk-${keyPrefix}-${fileName}`;
|
|
532
|
+
const extraMetadata = options.extraMetadata || {};
|
|
533
|
+
const extraTags = options.extraTags || [];
|
|
520
534
|
|
|
521
535
|
try {
|
|
522
536
|
const content = readFileSync(filePath, 'utf-8');
|
|
@@ -538,6 +552,7 @@ function indexFile(db, filePath, keyPrefix) {
|
|
|
538
552
|
|
|
539
553
|
// 1. Store full document
|
|
540
554
|
const docMetadata = {
|
|
555
|
+
...extraMetadata,
|
|
541
556
|
type: 'document',
|
|
542
557
|
filePath: relativePath,
|
|
543
558
|
fileSize: stats.size,
|
|
@@ -547,7 +562,7 @@ function indexFile(db, filePath, keyPrefix) {
|
|
|
547
562
|
ragVersion: '2.0', // Mark as full RAG indexed
|
|
548
563
|
};
|
|
549
564
|
|
|
550
|
-
storeEntry(db, docKey, content, docMetadata, [keyPrefix, 'document']);
|
|
565
|
+
storeEntry(db, docKey, content, docMetadata, [keyPrefix, 'document', ...extraTags]);
|
|
551
566
|
debug(`Stored document: ${docKey}`);
|
|
552
567
|
|
|
553
568
|
// 2. Chunk and store semantic pieces with full RAG linking
|
|
@@ -567,7 +582,7 @@ function indexFile(db, filePath, keyPrefix) {
|
|
|
567
582
|
children: siblings,
|
|
568
583
|
chunkCount: chunks.length,
|
|
569
584
|
};
|
|
570
|
-
storeEntry(db, docKey, content, docChildrenMeta, [keyPrefix, 'document']);
|
|
585
|
+
storeEntry(db, docKey, content, docChildrenMeta, [keyPrefix, 'document', ...extraTags]);
|
|
571
586
|
|
|
572
587
|
for (let i = 0; i < chunks.length; i++) {
|
|
573
588
|
const chunk = chunks[i];
|
|
@@ -589,6 +604,7 @@ function indexFile(db, filePath, keyPrefix) {
|
|
|
589
604
|
const hierInfo = hierarchy[chunkKey];
|
|
590
605
|
|
|
591
606
|
const chunkMetadata = {
|
|
607
|
+
...extraMetadata,
|
|
592
608
|
type: 'chunk',
|
|
593
609
|
ragVersion: '2.0',
|
|
594
610
|
|
|
@@ -647,7 +663,7 @@ function indexFile(db, filePath, keyPrefix) {
|
|
|
647
663
|
chunkKey,
|
|
648
664
|
searchableContent,
|
|
649
665
|
chunkMetadata,
|
|
650
|
-
[keyPrefix, 'chunk', `level-${chunk.level}`, chunk.title.toLowerCase().replace(/[^a-z0-9]+/g, '-')]
|
|
666
|
+
[keyPrefix, 'chunk', `level-${chunk.level}`, chunk.title.toLowerCase().replace(/[^a-z0-9]+/g, '-'), ...extraTags]
|
|
651
667
|
);
|
|
652
668
|
|
|
653
669
|
debug(` Stored chunk ${i}: ${chunk.title} (${chunk.content.length} chars, prev=${!!prevChunk}, next=${!!nextChunk})`);
|
|
@@ -699,7 +715,17 @@ function indexDirectory(db, dirConfig) {
|
|
|
699
715
|
: allMdFiles;
|
|
700
716
|
|
|
701
717
|
for (const filePath of filtered) {
|
|
702
|
-
|
|
718
|
+
let options = {};
|
|
719
|
+
if (dirConfig.kind === 'skill') {
|
|
720
|
+
// kind: 'skill' — key by parent dir name (skill folder), not SKILL.md
|
|
721
|
+
const skillName = basename(dirname(filePath));
|
|
722
|
+
options = {
|
|
723
|
+
nameOverride: skillName,
|
|
724
|
+
extraMetadata: { kind: 'skill', skill_name: skillName },
|
|
725
|
+
extraTags: ['skill', `skill-${skillName}`],
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
const result = indexFile(db, filePath, dirConfig.prefix, options);
|
|
703
729
|
results.push(result);
|
|
704
730
|
}
|
|
705
731
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retired-shipped-files prune helper (#948).
|
|
3
|
+
*
|
|
4
|
+
* Closes the gap that retired agents (#932) and retired skills (#945) leave
|
|
5
|
+
* in consumer projects: the manifest cleanup at section 3 of the launcher
|
|
6
|
+
* only knows about files moflo previously synced, but `.claude/agents/` and
|
|
7
|
+
* `.claude/skills/` were not in the manifest before #948. So files retired
|
|
8
|
+
* before #948 lands stay on disk forever — Claude Code keeps loading them as
|
|
9
|
+
* subagents on every prompt, paying the per-prompt roster tokens we just
|
|
10
|
+
* spent #932 fixing.
|
|
11
|
+
*
|
|
12
|
+
* This module reads `retired-files.json` (shipped at the moflo package
|
|
13
|
+
* root) and, for each entry, only prunes the consumer-side path when the
|
|
14
|
+
* file's sha256 matches one of `knownContentHashes` — i.e. the file matches
|
|
15
|
+
* a version moflo actually shipped, so the consumer didn't customize it.
|
|
16
|
+
* Customized files are preserved and a one-line notice is emitted so the
|
|
17
|
+
* user can act.
|
|
18
|
+
*
|
|
19
|
+
* Manifest format:
|
|
20
|
+
*
|
|
21
|
+
* {
|
|
22
|
+
* "version": 1,
|
|
23
|
+
* "retired": [
|
|
24
|
+
* {
|
|
25
|
+
* "path": ".claude/agents/v3/performance-engineer.md",
|
|
26
|
+
* "retiredIn": "4.9.22",
|
|
27
|
+
* "retiredBy": "#932",
|
|
28
|
+
* "knownContentHashes": ["sha256:abc...", "sha256:def..."]
|
|
29
|
+
* }
|
|
30
|
+
* ]
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* @module bin/lib/retired-files
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { existsSync, readFileSync, unlinkSync } from 'fs';
|
|
37
|
+
import { resolve } from 'path';
|
|
38
|
+
import { createHash } from 'crypto';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compute sha256 of file content. Returns null on read errors so the caller
|
|
42
|
+
* can decide what to do — we never want a transient stat/read failure to
|
|
43
|
+
* trigger a prune of a file we couldn't actually verify.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} absPath
|
|
46
|
+
* @returns {string|null} `sha256:<hex>` or null
|
|
47
|
+
*/
|
|
48
|
+
export function fileSha256(absPath) {
|
|
49
|
+
try {
|
|
50
|
+
const buf = readFileSync(absPath);
|
|
51
|
+
return 'sha256:' + createHash('sha256').update(buf).digest('hex');
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Load + validate the retired-files manifest. Returns `{ entries: [] }` for
|
|
59
|
+
* any failure mode (missing file, invalid JSON, wrong shape) — the launcher
|
|
60
|
+
* must never block on a corrupt manifest.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} manifestPath - absolute path to retired-files.json
|
|
63
|
+
* @returns {{ entries: Array<{path:string, retiredIn?:string, retiredBy?:string, knownContentHashes:string[]}> }}
|
|
64
|
+
*/
|
|
65
|
+
export function loadRetiredManifest(manifestPath) {
|
|
66
|
+
if (!existsSync(manifestPath)) return { entries: [] };
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
70
|
+
} catch { return { entries: [] }; }
|
|
71
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.retired)) {
|
|
72
|
+
return { entries: [] };
|
|
73
|
+
}
|
|
74
|
+
// Filter out malformed entries — guards against a hand-edited file
|
|
75
|
+
// dropping the wrong shape into a launcher that runs on N consumer
|
|
76
|
+
// machines. A skipped entry never auto-prunes (safest default).
|
|
77
|
+
const entries = parsed.retired.filter((entry) =>
|
|
78
|
+
entry &&
|
|
79
|
+
typeof entry.path === 'string' &&
|
|
80
|
+
entry.path.length > 0 &&
|
|
81
|
+
Array.isArray(entry.knownContentHashes) &&
|
|
82
|
+
entry.knownContentHashes.length > 0 &&
|
|
83
|
+
entry.knownContentHashes.every((h) => typeof h === 'string' && h.startsWith('sha256:'))
|
|
84
|
+
);
|
|
85
|
+
return { entries };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Decide what to do with a consumer-side path against a retirement entry:
|
|
90
|
+
*
|
|
91
|
+
* - 'absent' — path doesn't exist on disk; nothing to do
|
|
92
|
+
* - 'prune' — file exists and content hash matches a known-shipped
|
|
93
|
+
* value; safe to delete (consumer didn't customize)
|
|
94
|
+
* - 'preserve' — file exists but content differs from every known-shipped
|
|
95
|
+
* hash; consumer customized, leave alone
|
|
96
|
+
* - 'unknown' — file exists but its hash couldn't be read (transient
|
|
97
|
+
* error); leave alone, retry next session
|
|
98
|
+
*
|
|
99
|
+
* @param {string} projectRoot
|
|
100
|
+
* @param {{path:string, knownContentHashes:string[]}} entry
|
|
101
|
+
* @returns {{ action: 'absent'|'prune'|'preserve'|'unknown', actualHash: string|null }}
|
|
102
|
+
*/
|
|
103
|
+
export function classifyRetiredFile(projectRoot, entry) {
|
|
104
|
+
const abs = resolve(projectRoot, entry.path);
|
|
105
|
+
if (!existsSync(abs)) return { action: 'absent', actualHash: null };
|
|
106
|
+
const actualHash = fileSha256(abs);
|
|
107
|
+
if (!actualHash) return { action: 'unknown', actualHash: null };
|
|
108
|
+
if (entry.knownContentHashes.includes(actualHash)) {
|
|
109
|
+
return { action: 'prune', actualHash };
|
|
110
|
+
}
|
|
111
|
+
return { action: 'preserve', actualHash };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Walk the manifest and apply prune decisions. Returns a small report so
|
|
116
|
+
* the caller can emit one summary line (the launcher pattern) instead of
|
|
117
|
+
* forcing it to track per-entry state.
|
|
118
|
+
*
|
|
119
|
+
* Failures are non-fatal — a single un-deletable file (Windows AV hold,
|
|
120
|
+
* EBUSY) must not stop pruning the rest. The caller surfaces the report.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} projectRoot
|
|
123
|
+
* @param {string} manifestPath
|
|
124
|
+
* @returns {{ pruned: string[], preserved: string[], unknown: string[], failed: Array<{path:string, message:string}> }}
|
|
125
|
+
*/
|
|
126
|
+
export function applyRetiredPrune(projectRoot, manifestPath) {
|
|
127
|
+
const { entries } = loadRetiredManifest(manifestPath);
|
|
128
|
+
const report = { pruned: [], preserved: [], unknown: [], failed: [] };
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
const { action } = classifyRetiredFile(projectRoot, entry);
|
|
131
|
+
if (action === 'absent') continue;
|
|
132
|
+
if (action === 'preserve') { report.preserved.push(entry.path); continue; }
|
|
133
|
+
if (action === 'unknown') { report.unknown.push(entry.path); continue; }
|
|
134
|
+
// action === 'prune'
|
|
135
|
+
try {
|
|
136
|
+
unlinkSync(resolve(projectRoot, entry.path));
|
|
137
|
+
report.pruned.push(entry.path);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
report.failed.push({
|
|
140
|
+
path: entry.path,
|
|
141
|
+
message: err && err.message ? err.message : String(err),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return report;
|
|
146
|
+
}
|
|
@@ -14,6 +14,7 @@ import { fileURLToPath, pathToFileURL } from 'url';
|
|
|
14
14
|
import { mofloDir } from './lib/moflo-paths.mjs';
|
|
15
15
|
import { repairMemoryDbIfCorrupt } from './lib/db-repair.mjs';
|
|
16
16
|
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
17
|
+
import { applyRetiredPrune } from './lib/retired-files.mjs';
|
|
17
18
|
|
|
18
19
|
// Headless skip (#860). The daemon's headless workers spawn `claude --print`
|
|
19
20
|
// with CLAUDE_CODE_HEADLESS=true (see src/cli/services/headless-worker-
|
|
@@ -681,6 +682,56 @@ try {
|
|
|
681
682
|
}
|
|
682
683
|
}
|
|
683
684
|
|
|
685
|
+
// ── Sync .claude/agents/ + .claude/skills/ recursively (#948) ──────
|
|
686
|
+
// Pre-#948, agents and skills weren't manifest-tracked at all, so any
|
|
687
|
+
// file moflo retired (e.g. the 49 ruflo-aspirational agents in #932 or
|
|
688
|
+
// skill-builder in #945) would linger forever in consumer projects —
|
|
689
|
+
// Claude Code kept loading them on every prompt, paying the per-prompt
|
|
690
|
+
// roster tokens we just spent #932 fixing. Walking these dirs through
|
|
691
|
+
// syncFile() puts every shipped file in `currentManifest`, which lets
|
|
692
|
+
// the cleanup loop below auto-prune retired files going forward.
|
|
693
|
+
//
|
|
694
|
+
// Limitation: this only catches retirements that happen AFTER #948.
|
|
695
|
+
// For files retired BEFORE #948 (#932 + #945), see the retired-files.json
|
|
696
|
+
// hash-gated prune block further down — it ships a static list with
|
|
697
|
+
// content hashes so we only delete files the consumer didn't customize.
|
|
698
|
+
//
|
|
699
|
+
// User-authored files at custom paths (.claude/agents/custom/foo.md,
|
|
700
|
+
// .claude/skills/<custom>/SKILL.md) are NEVER passed through syncFile()
|
|
701
|
+
// because they don't exist in node_modules/moflo/, so they never enter
|
|
702
|
+
// the manifest and never get pruned — same proven safety story as
|
|
703
|
+
// scripts/helpers above.
|
|
704
|
+
async function syncDirRecursive(srcDir, destPrefix) {
|
|
705
|
+
if (!existsSync(srcDir)) return;
|
|
706
|
+
let entries;
|
|
707
|
+
try {
|
|
708
|
+
entries = readdirSync(srcDir, { recursive: true, withFileTypes: true });
|
|
709
|
+
} catch (err) {
|
|
710
|
+
emitWarning(`${destPrefix} readdir failed (${errMessage(err)})`);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
for (const entry of entries) {
|
|
714
|
+
if (!entry.isFile()) continue;
|
|
715
|
+
if (!entry.name.toLowerCase().endsWith('.md')) continue;
|
|
716
|
+
const parent = entry.parentPath || entry.path || srcDir;
|
|
717
|
+
const absSrc = resolve(parent, entry.name);
|
|
718
|
+
const rel = absSrc.slice(srcDir.length + 1).split(/[\\/]/).join('/');
|
|
719
|
+
const absDest = resolve(projectRoot, destPrefix, rel);
|
|
720
|
+
try { mkdirSync(dirname(absDest), { recursive: true }); } catch (err) {
|
|
721
|
+
emitWarning(`${destPrefix} subdir mkdir failed for ${rel} (${errMessage(err)})`);
|
|
722
|
+
}
|
|
723
|
+
await syncFile(absSrc, absDest, `${destPrefix}/${rel}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
await syncDirRecursive(
|
|
727
|
+
resolve(projectRoot, 'node_modules/moflo/.claude/agents'),
|
|
728
|
+
'.claude/agents',
|
|
729
|
+
);
|
|
730
|
+
await syncDirRecursive(
|
|
731
|
+
resolve(projectRoot, 'node_modules/moflo/.claude/skills'),
|
|
732
|
+
'.claude/skills',
|
|
733
|
+
);
|
|
734
|
+
|
|
684
735
|
// Sync all shipped guidance files from node_modules/moflo/.claude/guidance/shipped/
|
|
685
736
|
const guidanceDir = resolve(projectRoot, '.claude/guidance');
|
|
686
737
|
const shippedDir = resolve(projectRoot, 'node_modules/moflo/.claude/guidance/shipped');
|
|
@@ -725,6 +776,56 @@ try {
|
|
|
725
776
|
emitMutation('cleaned up retired files', `${removedFiles} removed`);
|
|
726
777
|
}
|
|
727
778
|
|
|
779
|
+
// ── Hash-gated prune of pre-#948 retirements (Mechanism B) ─────────
|
|
780
|
+
// The manifest cleanup above only knows about files moflo previously
|
|
781
|
+
// synced. Agents and skills retired BEFORE #948 (#932's 49 agents and
|
|
782
|
+
// #945's skill-builder, plus older retirements) were never in any
|
|
783
|
+
// manifest — they exist on disk in consumer projects only because
|
|
784
|
+
// earlier moflo versions shipped them via the npm tarball + flo init.
|
|
785
|
+
// `retired-files.json` is the explicit, reviewable static list that
|
|
786
|
+
// closes that gap. Each entry pairs a path with the sha256 hashes of
|
|
787
|
+
// every content version moflo previously shipped, so we only auto-
|
|
788
|
+
// prune when the consumer's file matches a known-shipped hash —
|
|
789
|
+
// customized files (different hash) get preserved with a one-line
|
|
790
|
+
// notice the user can act on.
|
|
791
|
+
try {
|
|
792
|
+
const retiredManifestPath = resolve(
|
|
793
|
+
projectRoot,
|
|
794
|
+
'node_modules/moflo/retired-files.json',
|
|
795
|
+
);
|
|
796
|
+
const report = applyRetiredPrune(projectRoot, retiredManifestPath);
|
|
797
|
+
if (report.pruned.length > 0) {
|
|
798
|
+
emitMutation(
|
|
799
|
+
'pruned retired shipped files',
|
|
800
|
+
`${plural(report.pruned.length, 'file')} matching known-shipped content removed`,
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
if (report.preserved.length > 0) {
|
|
804
|
+
// stdout (not stderr) so Claude sees this in `additionalContext`
|
|
805
|
+
// and can surface to the user — these aren't failures, just
|
|
806
|
+
// consumer-customized files we deliberately left alone.
|
|
807
|
+
const sample = report.preserved.slice(0, 5).map((p) => ` - ${p}`).join('\n');
|
|
808
|
+
const more = report.preserved.length > 5
|
|
809
|
+
? `\n …and ${report.preserved.length - 5} more`
|
|
810
|
+
: '';
|
|
811
|
+
try {
|
|
812
|
+
process.stdout.write(
|
|
813
|
+
`moflo: retained ${plural(report.preserved.length, 'customized retired file')} (delete manually if unwanted):\n${sample}${more}\n`,
|
|
814
|
+
);
|
|
815
|
+
} catch { /* non-fatal */ }
|
|
816
|
+
}
|
|
817
|
+
if (report.failed.length > 0) {
|
|
818
|
+
const sample = report.failed.slice(0, 3)
|
|
819
|
+
.map((f) => ` - ${f.path}: ${f.message}`).join('\n');
|
|
820
|
+
emitWarning(`${plural(report.failed.length, 'retired file')} failed to prune:\n${sample}`);
|
|
821
|
+
}
|
|
822
|
+
} catch (err) {
|
|
823
|
+
// Non-fatal — the consumer just doesn't get the prune this session.
|
|
824
|
+
// Next session retries; manifest-cleanup above still works for
|
|
825
|
+
// future retirements regardless.
|
|
826
|
+
emitWarning(`retired-files prune skipped (${errMessage(err)})`);
|
|
827
|
+
}
|
|
828
|
+
|
|
728
829
|
// The daemon was already stopped above so the lock file is gone and
|
|
729
830
|
// there's no live PID to recycle here. Section 4's `hooks.mjs
|
|
730
831
|
// session-start` will spawn a fresh daemon under the current moflo
|
|
@@ -1334,14 +1435,15 @@ try {
|
|
|
1334
1435
|
} catch { /* writing the failure itself must not throw */ }
|
|
1335
1436
|
}
|
|
1336
1437
|
|
|
1337
|
-
// ── 3e-729. Purge ephemeral-namespace rows (#729)
|
|
1338
|
-
//
|
|
1339
|
-
//
|
|
1340
|
-
//
|
|
1341
|
-
//
|
|
1342
|
-
//
|
|
1343
|
-
// `purged: 0`
|
|
1344
|
-
// so the foreground sql.js write isn't
|
|
1438
|
+
// ── 3e-729. Purge ephemeral-namespace rows + trim tasklist (#729, #968) ─────
|
|
1439
|
+
// Three namespaces (hive-mind, epic-state, test-bridge-fix) store internal
|
|
1440
|
+
// run-tracking and get hard-deleted on every session start. The fourth
|
|
1441
|
+
// embedding-skipped namespace, `tasklist`, backs the dashboard's Flo Runs
|
|
1442
|
+
// tab — it's *trimmed* to a retention cap instead of purged so prior runs
|
|
1443
|
+
// survive a session restart (#968). Idempotent: returns
|
|
1444
|
+
// `{ purged: 0, trimmed: 0 }` when nothing needs cleaning. Runs BEFORE the
|
|
1445
|
+
// background MCP/daemon spawn so the foreground sql.js write isn't
|
|
1446
|
+
// overwritten by a concurrent flush.
|
|
1345
1447
|
try {
|
|
1346
1448
|
const purgePaths = [
|
|
1347
1449
|
resolve(projectRoot, 'node_modules/moflo/dist/src/cli/services/ephemeral-namespace-purge.js'),
|
|
@@ -1357,6 +1459,12 @@ try {
|
|
|
1357
1459
|
`${plural(result.purged, 'row')} from internal run-tracking`,
|
|
1358
1460
|
);
|
|
1359
1461
|
}
|
|
1462
|
+
if (result?.trimmed > 0) {
|
|
1463
|
+
emitMutation(
|
|
1464
|
+
'trimmed flo run history',
|
|
1465
|
+
`${plural(result.trimmed, 'old row')} beyond retention cap`,
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1360
1468
|
}
|
|
1361
1469
|
} catch (err) {
|
|
1362
1470
|
// Non-fatal — leftover rows just sit until the next session retries.
|
|
@@ -20,7 +20,7 @@ const AES_TAG_LEN = 16;
|
|
|
20
20
|
const AES_ALG = 'aes-256-gcm';
|
|
21
21
|
// ── Catalog ──────────────────────────────────────────────────
|
|
22
22
|
const RUFLO_COMMANDS = 'init agent swarm memory mcp task session config status start workflow hooks hive-mind daemon neural security performance providers plugins deployment embeddings claims migrate process doctor completions'.split(' ');
|
|
23
|
-
const AGENT_TYPES = 'coder reviewer tester planner researcher security-
|
|
23
|
+
const AGENT_TYPES = 'coder reviewer tester planner researcher security-auditor backend-dev frontend-dev database-dev cicd-engineer api-docs system-architect code-analyzer analyst base-template-generator test-long-runner'.split(' ');
|
|
24
24
|
const HOOK_TYPES = 'pre-edit post-edit pre-command post-command pre-task post-task session-start session-end session-restore notify route explain pretrain build-agents transfer teammate-idle task-completed'.split(' ');
|
|
25
25
|
const WORKER_TYPES = 'ultralearn optimize consolidate predict audit map preload deepdive document refactor benchmark testgaps'.split(' ');
|
|
26
26
|
const OFFLINE_MODELS = [
|
|
@@ -68,13 +68,8 @@ const AGENT_TYPES = [
|
|
|
68
68
|
{ value: 'coordinator', label: 'Coordinator', hint: 'Multi-agent orchestration and spell management' },
|
|
69
69
|
{ value: 'analyst', label: 'Analyst', hint: 'Performance analysis and optimization' },
|
|
70
70
|
{ value: 'optimizer', label: 'Optimizer', hint: 'Performance optimization and bottleneck analysis' },
|
|
71
|
-
{ value: 'security-architect', label: 'Security Architect', hint: 'Security architecture and threat modeling' },
|
|
72
71
|
{ value: 'security-auditor', label: 'Security Auditor', hint: 'CVE remediation and security testing' },
|
|
73
|
-
{ value: '
|
|
74
|
-
{ value: 'swarm-specialist', label: 'Swarm Specialist', hint: 'Unified coordination engine' },
|
|
75
|
-
{ value: 'performance-engineer', label: 'Performance Engineer', hint: '2.49x-7.47x optimization targets' },
|
|
76
|
-
{ value: 'core-architect', label: 'Core Architect', hint: 'Domain-driven design restructure' },
|
|
77
|
-
{ value: 'test-architect', label: 'Test Architect', hint: 'TDD London School methodology' }
|
|
72
|
+
{ value: 'planner', label: 'Planner', hint: 'Task planning and breakdown' },
|
|
78
73
|
];
|
|
79
74
|
// Agent spawn subcommand
|
|
80
75
|
const spawnCommand = {
|
|
@@ -844,9 +839,8 @@ function getAgentCapabilities(type) {
|
|
|
844
839
|
reviewer: ['code-review', 'security-audit', 'quality-check', 'documentation'],
|
|
845
840
|
architect: ['system-design', 'pattern-analysis', 'scalability', 'documentation'],
|
|
846
841
|
coordinator: ['task-orchestration', 'agent-management', 'spell-control'],
|
|
847
|
-
'security-
|
|
848
|
-
|
|
849
|
-
'performance-engineer': ['benchmarking', 'profiling', 'optimization', 'monitoring']
|
|
842
|
+
'security-auditor': ['threat-modeling', 'security-patterns', 'compliance', 'audit', 'vulnerability-scan'],
|
|
843
|
+
planner: ['task-breakdown', 'sequencing', 'estimation'],
|
|
850
844
|
};
|
|
851
845
|
return capabilities[type] || ['general'];
|
|
852
846
|
}
|