opencode-swarm 7.28.2 → 7.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -442
- package/dist/cli/index.js +55 -13
- package/dist/hooks/knowledge-application.d.ts +12 -0
- package/dist/hooks/skill-propagation-gate.d.ts +16 -4
- package/dist/index.js +351 -117
- package/dist/tools/test-runner.d.ts +0 -1
- package/dist/utils/bun-compat.d.ts +10 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var package_default;
|
|
|
48
48
|
var init_package = __esm(() => {
|
|
49
49
|
package_default = {
|
|
50
50
|
name: "opencode-swarm",
|
|
51
|
-
version: "7.
|
|
51
|
+
version: "7.29.0",
|
|
52
52
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
53
53
|
main: "dist/index.js",
|
|
54
54
|
types: "dist/index.d.ts",
|
|
@@ -16518,6 +16518,27 @@ function bunHash(input) {
|
|
|
16518
16518
|
}
|
|
16519
16519
|
return hash2;
|
|
16520
16520
|
}
|
|
16521
|
+
function killProcessTreeImpl(pid, signal, directKill, wasDetached) {
|
|
16522
|
+
if (typeof pid !== "number" || pid <= 0) {
|
|
16523
|
+
directKill();
|
|
16524
|
+
return;
|
|
16525
|
+
}
|
|
16526
|
+
if (process.platform === "win32") {
|
|
16527
|
+
try {
|
|
16528
|
+
nodeSpawnSync("taskkill", ["/PID", String(pid), "/T", "/F"]);
|
|
16529
|
+
} catch {
|
|
16530
|
+
directKill();
|
|
16531
|
+
}
|
|
16532
|
+
return;
|
|
16533
|
+
}
|
|
16534
|
+
if (wasDetached) {
|
|
16535
|
+
try {
|
|
16536
|
+
process.kill(-pid, signal ?? "SIGKILL");
|
|
16537
|
+
return;
|
|
16538
|
+
} catch {}
|
|
16539
|
+
}
|
|
16540
|
+
directKill();
|
|
16541
|
+
}
|
|
16521
16542
|
function streamFromNode(pipe2) {
|
|
16522
16543
|
const collected = new Promise((resolve) => {
|
|
16523
16544
|
if (!pipe2) {
|
|
@@ -16640,20 +16661,34 @@ function bunSpawn(cmd, options) {
|
|
|
16640
16661
|
return proc2.exitCode;
|
|
16641
16662
|
},
|
|
16642
16663
|
kill(sig) {
|
|
16643
|
-
|
|
16664
|
+
if (options?.killProcessTree) {
|
|
16665
|
+
killProcessTreeImpl(proc2.pid, sig, () => proc2.kill(sig), false);
|
|
16666
|
+
} else {
|
|
16667
|
+
proc2.kill(sig);
|
|
16668
|
+
}
|
|
16644
16669
|
}
|
|
16645
16670
|
};
|
|
16646
16671
|
}
|
|
16647
16672
|
const [file2, ...args2] = cmd;
|
|
16673
|
+
const detached = options?.killProcessTree === true;
|
|
16648
16674
|
const proc = nodeSpawn(file2, args2, {
|
|
16649
16675
|
cwd: options?.cwd,
|
|
16650
16676
|
env: options?.env,
|
|
16677
|
+
detached,
|
|
16678
|
+
windowsHide: true,
|
|
16651
16679
|
stdio: [
|
|
16652
16680
|
mapStdio(options?.stdin),
|
|
16653
16681
|
mapStdio(options?.stdout),
|
|
16654
16682
|
mapStdio(options?.stderr)
|
|
16655
16683
|
]
|
|
16656
16684
|
});
|
|
16685
|
+
const killChild = (signal) => {
|
|
16686
|
+
if (detached) {
|
|
16687
|
+
killProcessTreeImpl(proc.pid, signal, () => proc.kill(signal), true);
|
|
16688
|
+
} else {
|
|
16689
|
+
proc.kill(signal);
|
|
16690
|
+
}
|
|
16691
|
+
};
|
|
16657
16692
|
let timeoutHandle;
|
|
16658
16693
|
const exited = new Promise((resolve) => {
|
|
16659
16694
|
proc.on("exit", (code) => resolve(code ?? 0));
|
|
@@ -16661,7 +16696,7 @@ function bunSpawn(cmd, options) {
|
|
|
16661
16696
|
if (options?.timeout && options.timeout > 0) {
|
|
16662
16697
|
timeoutHandle = setTimeout(() => {
|
|
16663
16698
|
try {
|
|
16664
|
-
|
|
16699
|
+
killChild("SIGKILL");
|
|
16665
16700
|
} catch {}
|
|
16666
16701
|
}, options.timeout);
|
|
16667
16702
|
if (typeof timeoutHandle.unref === "function") {
|
|
@@ -16681,7 +16716,7 @@ function bunSpawn(cmd, options) {
|
|
|
16681
16716
|
},
|
|
16682
16717
|
kill(signal) {
|
|
16683
16718
|
try {
|
|
16684
|
-
|
|
16719
|
+
killChild(signal);
|
|
16685
16720
|
} catch {}
|
|
16686
16721
|
}
|
|
16687
16722
|
};
|
|
@@ -67348,7 +67383,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
67348
67383
|
const coverage = opts.coverage ?? false;
|
|
67349
67384
|
switch (framework) {
|
|
67350
67385
|
case "bun": {
|
|
67351
|
-
const args2 = ["bun", "test"];
|
|
67386
|
+
const args2 = ["bun", "--smol", "test"];
|
|
67352
67387
|
if (coverage)
|
|
67353
67388
|
args2.push("--coverage");
|
|
67354
67389
|
if (scope !== "all" && files.length > 0)
|
|
@@ -69781,7 +69816,7 @@ function getTargetedExecutionUnsupportedReason(framework) {
|
|
|
69781
69816
|
function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
69782
69817
|
switch (framework) {
|
|
69783
69818
|
case "bun": {
|
|
69784
|
-
const args2 = ["bun", "test"];
|
|
69819
|
+
const args2 = ["bun", "--smol", "test"];
|
|
69785
69820
|
if (coverage)
|
|
69786
69821
|
args2.push("--coverage");
|
|
69787
69822
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -70318,17 +70353,24 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
70318
70353
|
const proc = bunSpawn(command, {
|
|
70319
70354
|
stdout: "pipe",
|
|
70320
70355
|
stderr: "pipe",
|
|
70321
|
-
|
|
70356
|
+
stdin: "ignore",
|
|
70357
|
+
cwd,
|
|
70358
|
+
killProcessTree: true
|
|
70359
|
+
});
|
|
70360
|
+
let timeoutHandle;
|
|
70361
|
+
const timeoutPromise = new Promise((resolve16) => {
|
|
70362
|
+
timeoutHandle = setTimeout(() => {
|
|
70363
|
+
proc.kill();
|
|
70364
|
+
resolve16(-1);
|
|
70365
|
+
}, timeout_ms);
|
|
70322
70366
|
});
|
|
70323
|
-
const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
|
|
70324
|
-
proc.kill();
|
|
70325
|
-
resolve16(-1);
|
|
70326
|
-
}, timeout_ms));
|
|
70327
70367
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
70328
70368
|
Promise.race([proc.exited, timeoutPromise]),
|
|
70329
70369
|
readBoundedStream(proc.stdout, MAX_OUTPUT_BYTES3),
|
|
70330
70370
|
readBoundedStream(proc.stderr, MAX_OUTPUT_BYTES3)
|
|
70331
70371
|
]);
|
|
70372
|
+
if (timeoutHandle !== undefined)
|
|
70373
|
+
clearTimeout(timeoutHandle);
|
|
70332
70374
|
const duration_ms = Date.now() - startTime;
|
|
70333
70375
|
let output = stdoutResult.text;
|
|
70334
70376
|
if (stderrResult.text) {
|
|
@@ -70631,7 +70673,6 @@ var init_test_runner = __esm(() => {
|
|
|
70631
70673
|
files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
|
|
70632
70674
|
coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
70633
70675
|
timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
70634
|
-
allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
70635
70676
|
working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
|
|
70636
70677
|
},
|
|
70637
70678
|
async execute(args2, directory) {
|
|
@@ -70705,7 +70746,8 @@ var init_test_runner = __esm(() => {
|
|
|
70705
70746
|
}
|
|
70706
70747
|
const scope = args2.scope || "all";
|
|
70707
70748
|
if (scope === "all") {
|
|
70708
|
-
|
|
70749
|
+
const fullSuiteAllowed = process.env.SWARM_ALLOW_FULL_SUITE === "1" || process.env.SWARM_ALLOW_FULL_SUITE === "true";
|
|
70750
|
+
if (!fullSuiteAllowed) {
|
|
70709
70751
|
const errorResult = {
|
|
70710
70752
|
success: false,
|
|
70711
70753
|
framework: "none",
|
|
@@ -75902,58 +75944,31 @@ SECURITY_KEYWORDS: password, secret, token, credential, auth, login, encryption,
|
|
|
75902
75944
|
|
|
75903
75945
|
## SKILLS PROPAGATION
|
|
75904
75946
|
|
|
75905
|
-
Subagents run in isolated contexts.
|
|
75906
|
-
|
|
75907
|
-
### Step 1 — Discover available skills (once per session)
|
|
75908
|
-
|
|
75909
|
-
At session start, before your first delegation:
|
|
75910
|
-
1. Read contract files first: \`AGENTS.md\`, \`CLAUDE.md\`, \`CONTRIBUTING.md\`, \`TESTING.md\` when present. Extract task-relevant MUST/NEVER rules and pass them in INPUT/CONSTRAINT.
|
|
75911
|
-
2. Reuse \`<skill-context>\` skills when present.
|
|
75912
|
-
3. Otherwise use \`search\` with \`include\` patterns like \`.opencode/skills/*/SKILL.md,.claude/skills/*/SKILL.md\` and frontmatter queries \`^name:\` / \`^description:\`.
|
|
75913
|
-
4. Cache a short \`.swarm/context.md\` \`## Available Skills\` index. Include the repo-relative \`file:\` path and description so subagents can load on demand:
|
|
75914
|
-
- writing-tests: Guidelines for writing tests (used: 12, compliance: 95%) → test_engineer, coder
|
|
75915
|
-
- engineering-conventions: Engineering invariants (used: 8, compliance: 100%) → coder, reviewer, test_engineer
|
|
75916
|
-
- [name]: [description] (used: N, compliance: N%) → [applicable agents]
|
|
75917
|
-
|
|
75918
|
-
Use \`.swarm/skill-usage.jsonl\` when present; otherwise weight skills equally.
|
|
75947
|
+
Subagents run in isolated contexts. Any project-specific skill constraints loaded into your session (e.g. \`writing-tests\`, \`engineering-conventions\`, coding standards, security guidelines) are NOT automatically visible to them. The hook system auto-injects relevant skills into delegation prompts.
|
|
75919
75948
|
|
|
75920
|
-
|
|
75949
|
+
### Step 1 — Skills are auto-discovered and scored
|
|
75921
75950
|
|
|
75951
|
+
The hook system discovers available skills and scores them by relevance to the task. The hook auto-injects them into the delegation prompt.
|
|
75922
75952
|
|
|
75923
|
-
### Step 2 —
|
|
75953
|
+
### Step 2 — SKILLS: field is auto-populated
|
|
75924
75954
|
|
|
75925
|
-
|
|
75955
|
+
The hook auto-populates the \`SKILLS:\` field with top recommended skills (max 5, threshold 0.5). Explicit \`SKILLS: none\` is preserved.
|
|
75926
75956
|
|
|
75927
|
-
|
|
75928
|
-
Route tests to test_engineer/coder; conventions to coder/reviewer/test_engineer; implementation to coder/reviewer; review/security to reviewer; docs to docs; architecture/ui/domain topics to designer or sme.
|
|
75957
|
+
### Step 3 — Skill references with context descriptions
|
|
75929
75958
|
|
|
75930
|
-
When
|
|
75959
|
+
When passing skill references, you may add brief context descriptions. The hook injects \`file:path (-- description)\` format.
|
|
75931
75960
|
|
|
75932
|
-
### Step
|
|
75961
|
+
### Step 4 — Forward SKILLS_USED_BY_CODER to reviewer
|
|
75933
75962
|
|
|
75934
|
-
|
|
75963
|
+
When delegating to the reviewer after a coder task, include a \`SKILLS_USED_BY_CODER: [comma-separated list of skill paths from the coder delegation]\` field. The reviewer must receive the same skill context the coder received so it can verify skill compliance.
|
|
75935
75964
|
|
|
75936
|
-
|
|
75937
|
-
- \`SKILLS: file:.claude/skills/writing-tests/SKILL.md\` — preferred for skills that exist on disk; use repo-relative \`file:\` references, comma-separated when multiple skills apply
|
|
75938
|
-
- Descriptive multi-line catalog:
|
|
75939
|
-
SKILLS:
|
|
75940
|
-
- file:.claude/skills/writing-tests/SKILL.md - Guidelines for writing tests
|
|
75941
|
-
- file:.claude/skills/engineering-conventions/SKILL.md - Engineering invariants for safe changes
|
|
75942
|
-
- Inline fallback:
|
|
75943
|
-
SKILLS:
|
|
75944
|
-
--- [skill-name] ---
|
|
75945
|
-
[full SKILL.md body content pasted here]
|
|
75965
|
+
Example: If the coder received \`SKILLS: file:.claude/skills/writing-tests/SKILL.md\`, the reviewer delegation must include \`SKILLS_USED_BY_CODER: file:.claude/skills/writing-tests/SKILL.md\` in addition to the reviewer's own \`SKILLS:\` field.
|
|
75946
75966
|
|
|
75947
|
-
|
|
75967
|
+
**Skill-to-agent routing:** Managed via \`.opencode/skill-routing.yaml\`. The hook reads this file at delegation time.
|
|
75948
75968
|
|
|
75949
|
-
**SKILL_LOAD_FAILED recovery:** do NOT retry with the same reference.
|
|
75950
|
-
|
|
75951
|
-
**Mandatory for coding tasks:** provide \`writing-tests\` to test_engineer and \`engineering-conventions\` to coder + reviewer when present.
|
|
75952
|
-
|
|
75953
|
-
### Step 4 — Forward skills to reviewer
|
|
75954
|
-
|
|
75955
|
-
When delegating to reviewer after coder, include \`SKILLS_USED_BY_CODER: [comma-separated skill paths from coder]\` plus reviewer \`SKILLS:\` so compliance can be checked.
|
|
75969
|
+
**SKILL_LOAD_FAILED recovery:** If a subagent reports SKILL_LOAD_FAILED for a \`file:\` reference, do NOT retry with the same reference. Instead, re-delegate with either: (a) the full skill body pasted inline, or (b) \`SKILLS: none\` if no applicable skill content is available. Never re-use a file: reference that has already failed.
|
|
75956
75970
|
|
|
75971
|
+
**Mandatory for coding tasks:** Always provide \`writing-tests\` to test_engineer and \`engineering-conventions\` to coder + reviewer when those skills are present in the project. Prefer \`file:\` references when the files exist.
|
|
75957
75972
|
|
|
75958
75973
|
## SWARM KNOWLEDGE DIRECTIVES (v2 acknowledgment contract)
|
|
75959
75974
|
|
|
@@ -75976,14 +75991,28 @@ You may also call the \`knowledge_ack\` tool to record an outcome explicitly whe
|
|
|
75976
75991
|
|
|
75977
75992
|
## SKILL IMPROVER (low-frequency, expensive-model adviser)
|
|
75978
75993
|
|
|
75979
|
-
|
|
75994
|
+
The \`skill_improver\` agent and the \`skill_improve\` tool exist for rare, deep
|
|
75995
|
+
review of accumulated knowledge / skills / spec / architect prompt. They are
|
|
75996
|
+
quota-bounded (default 10 calls/day) and disabled by default. Suggest running
|
|
75997
|
+
\`skill_improve\` only after one of:
|
|
75998
|
+
- repeated reviewer rejections in a row,
|
|
75999
|
+
- many \`KNOWLEDGE_IGNORED\` outcomes for the same cluster,
|
|
76000
|
+
- stale skills (no updates while their target area changed),
|
|
76001
|
+
- a fresh spec mismatch with shipped behaviour.
|
|
75980
76002
|
|
|
75981
76003
|
When \`skill_improver.require_user_approval\` is true (default), ASK the user
|
|
75982
76004
|
before running. Default outputs are proposals only — they never modify source.
|
|
75983
76005
|
|
|
75984
76006
|
## SPEC WRITER
|
|
75985
76007
|
|
|
75986
|
-
For substantial spec authoring
|
|
76008
|
+
For substantial spec authoring or revision, prefer delegating to the
|
|
76009
|
+
\`spec_writer\` agent (independent model from architect). It writes only via
|
|
76010
|
+
the safe \`spec_write\` tool. Use it when:
|
|
76011
|
+
- the user requests a new spec or major spec revision,
|
|
76012
|
+
- requirements decomposition is non-trivial,
|
|
76013
|
+
- you would otherwise inline-author \`.swarm/spec.md\` yourself.
|
|
76014
|
+
|
|
76015
|
+
Continue handling small touch-ups (typos, cross-references) inline.
|
|
75987
76016
|
|
|
75988
76017
|
### ANTI-RATIONALIZATION
|
|
75989
76018
|
- ✗ "The coder already knows these conventions" → Skills contain project-specific rules the model cannot know from training. Always pass.
|
|
@@ -75996,7 +76025,7 @@ For substantial spec authoring/revision, prefer \`spec_writer\`; it writes only
|
|
|
75996
76025
|
## SLASH COMMANDS
|
|
75997
76026
|
{{SLASH_COMMANDS}}
|
|
75998
76027
|
Commands above are documented with args and behavioral details. Run commands via /swarm <command> [args].
|
|
75999
|
-
Outside OpenCode,
|
|
76028
|
+
Outside OpenCode, invoke any plugin command via: \`bunx opencode-swarm run <command> [args]\` (e.g. \`bunx opencode-swarm run knowledge migrate\`). Do not use \`bun -e\` or look for \`src/commands/\` — those paths are internal to the plugin source and do not exist in user project directories. EXCEPTION — human-only commands (including but not limited to \`acknowledge-spec-drift\`, \`reset\`, \`reset-session\`, \`rollback\`, \`checkpoint\`, and any command that releases a runtime safety gate or destroys plan state): you MUST present these to the user and ask them to run the command themselves. Never invoke a human-only command via Bash, swarm_command, or chat fallback. The runtime guardrail will block such attempts; if a Bash call returns \`BLOCKED\` with a "human-only" message, do not retry under a different shell form — present the situation to the user instead.
|
|
76000
76029
|
|
|
76001
76030
|
SMEs advise only. Reviewer and critic review only. None of them write code.
|
|
76002
76031
|
|
|
@@ -76004,15 +76033,15 @@ Available Tools: {{AVAILABLE_TOOLS}}
|
|
|
76004
76033
|
|
|
76005
76034
|
## DELEGATION FORMAT
|
|
76006
76035
|
|
|
76007
|
-
Delegations
|
|
76036
|
+
Delegations are performed ONLY by calling the **Task** tool. Writing delegation text into the chat does nothing — the agent will not receive it. Every delegation below is the content you pass to the Task tool, not text you output to the conversation.
|
|
76008
76037
|
|
|
76009
|
-
follow the receiving agent's INPUT FORMAT exactly
|
|
76038
|
+
All delegations MUST follow the receiving agent's INPUT FORMAT exactly. Do NOT invent fields, omit required fields, or force one agent's schema onto another. Every delegation MUST begin with the agent name, include \`TASK:\`, and include \`SKILLS:\` when that agent prompt supports skills.
|
|
76010
76039
|
Do NOT add conversational preamble before the agent prefix. Begin directly with the agent name.
|
|
76011
76040
|
|
|
76012
76041
|
{{AGENT_PREFIX}}[agent]
|
|
76013
76042
|
TASK: [single objective]
|
|
76014
76043
|
[agent-specific fields required by that agent's INPUT FORMAT]
|
|
76015
|
-
SKILLS: [either "none", repo-relative file references
|
|
76044
|
+
SKILLS: [either "none", repo-relative file: references, or inline skill bodies — see SKILLS PROPAGATION; use "none" only when no project-specific skill applies]
|
|
76016
76045
|
|
|
76017
76046
|
Examples:
|
|
76018
76047
|
|
|
@@ -76045,22 +76074,22 @@ FILE: src/auth/login.ts
|
|
|
76045
76074
|
INPUT: Validate email format, password >= 8 chars
|
|
76046
76075
|
OUTPUT: Modified file
|
|
76047
76076
|
CONSTRAINT: Do not modify other functions
|
|
76048
|
-
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76077
|
+
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76049
76078
|
|
|
76050
76079
|
{{AGENT_PREFIX}}reviewer
|
|
76051
76080
|
TASK: Review login validation
|
|
76052
76081
|
FILE: src/auth/login.ts
|
|
76053
76082
|
CHECK: [security, correctness, edge-cases]
|
|
76054
76083
|
GATES: lint=PASS, sast_scan=PASS, secretscan=PASS
|
|
76055
|
-
SKILLS_USED_BY_CODER: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76084
|
+
SKILLS_USED_BY_CODER: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76056
76085
|
OUTPUT: VERDICT + RISK + ISSUES
|
|
76057
|
-
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76086
|
+
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76058
76087
|
|
|
76059
76088
|
{{AGENT_PREFIX}}test_engineer
|
|
76060
76089
|
TASK: Generate and run login validation tests
|
|
76061
76090
|
FILE: src/auth/login.ts
|
|
76062
76091
|
OUTPUT: Test file at src/auth/login.test.ts + VERDICT: PASS/FAIL with failure details
|
|
76063
|
-
SKILLS: file:.claude/skills/writing-tests/SKILL.md
|
|
76092
|
+
SKILLS: file:.claude/skills/writing-tests/SKILL.md
|
|
76064
76093
|
|
|
76065
76094
|
{{AGENT_PREFIX}}critic
|
|
76066
76095
|
TASK: Review plan for user authentication feature
|
|
@@ -76075,14 +76104,14 @@ FILE: src/auth/login.ts
|
|
|
76075
76104
|
CHECK: [security-only] — evaluate against OWASP Top 10, scan for hardcoded secrets, injection vectors, insecure crypto, missing input validation
|
|
76076
76105
|
GATES: lint=PASS, sast_scan=PASS, secretscan=PASS
|
|
76077
76106
|
OUTPUT: VERDICT + RISK + SECURITY ISSUES ONLY
|
|
76078
|
-
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76107
|
+
SKILLS: file:.claude/skills/engineering-conventions/SKILL.md
|
|
76079
76108
|
|
|
76080
76109
|
{{AGENT_PREFIX}}test_engineer
|
|
76081
76110
|
TASK: Adversarial security testing
|
|
76082
76111
|
FILE: src/auth/login.ts
|
|
76083
76112
|
CONSTRAINT: ONLY attack vectors — malformed inputs, oversized payloads, injection attempts, auth bypass, boundary violations
|
|
76084
76113
|
OUTPUT: Test file + VERDICT: PASS/FAIL
|
|
76085
|
-
SKILLS: file:.claude/skills/writing-tests/SKILL.md
|
|
76114
|
+
SKILLS: file:.claude/skills/writing-tests/SKILL.md
|
|
76086
76115
|
|
|
76087
76116
|
{{AGENT_PREFIX}}explorer
|
|
76088
76117
|
TASK: Integration impact analysis
|
|
@@ -95075,6 +95104,9 @@ function utcDayKey(d = new Date) {
|
|
|
95075
95104
|
function buildAckDedupKey(sessionId, id, result, now = new Date) {
|
|
95076
95105
|
return `${sessionId}|${id}|${result}|${utcDayKey(now)}`;
|
|
95077
95106
|
}
|
|
95107
|
+
function filterHighConfidenceKnowledge(entries, threshold = 0.8) {
|
|
95108
|
+
return entries.filter((entry) => entry.confidence >= threshold);
|
|
95109
|
+
}
|
|
95078
95110
|
|
|
95079
95111
|
// src/hooks/knowledge-application-gate.ts
|
|
95080
95112
|
var HIGH_RISK_TOOLS = new Set([
|
|
@@ -95527,7 +95559,8 @@ function createKnowledgeInjectorHook(directory, config3) {
|
|
|
95527
95559
|
currentPhase: phaseDescription
|
|
95528
95560
|
};
|
|
95529
95561
|
const entries = await readContextualKnowledge(directory, config3, retrievalCtx);
|
|
95530
|
-
|
|
95562
|
+
const filteredEntries = filterHighConfidenceKnowledge(entries);
|
|
95563
|
+
cachedShownIds = filteredEntries.map((e) => e.id);
|
|
95531
95564
|
let freshPreamble = null;
|
|
95532
95565
|
try {
|
|
95533
95566
|
const driftReports = await readPriorDriftReports(directory);
|
|
@@ -95548,7 +95581,7 @@ function createKnowledgeInjectorHook(directory, config3) {
|
|
|
95548
95581
|
${freshPreamble}` : `<curator_briefing>${truncatedBriefing}</curator_briefing>`;
|
|
95549
95582
|
}
|
|
95550
95583
|
} catch {}
|
|
95551
|
-
if (
|
|
95584
|
+
if (filteredEntries.length === 0) {
|
|
95552
95585
|
if (freshPreamble === null)
|
|
95553
95586
|
return;
|
|
95554
95587
|
cachedInjectionText = freshPreamble;
|
|
@@ -95559,9 +95592,9 @@ ${freshPreamble}` : `<curator_briefing>${truncatedBriefing}</curator_briefing>`;
|
|
|
95559
95592
|
const isFullBudget = effectiveBudget === maxInjectChars;
|
|
95560
95593
|
const directiveBudget = Math.floor(effectiveBudget * 0.45);
|
|
95561
95594
|
const lessonBudget = Math.floor(effectiveBudget * 0.3);
|
|
95562
|
-
const directiveEntries =
|
|
95595
|
+
const directiveEntries = filteredEntries.filter((e) => e.triggers && e.triggers.length > 0 || e.required_actions && e.required_actions.length > 0 || e.forbidden_actions && e.forbidden_actions.length > 0 || e.directive_priority === "critical" || e.directive_priority === "high" || e.generated_skill_path);
|
|
95563
95596
|
const directiveBlock = buildDirectiveBlock(directiveEntries, directiveBudget, config3);
|
|
95564
|
-
const lessonBlock = buildKnowledgeBlock(
|
|
95597
|
+
const lessonBlock = buildKnowledgeBlock(filteredEntries, lessonBudget, config3, projectName);
|
|
95565
95598
|
const parts2 = [];
|
|
95566
95599
|
let remaining = effectiveBudget;
|
|
95567
95600
|
if (directiveBlock) {
|
|
@@ -95603,7 +95636,7 @@ ${freshPreamble}` : `<curator_briefing>${truncatedBriefing}</curator_briefing>`;
|
|
|
95603
95636
|
injectKnowledgeMessage(output, cachedInjectionText);
|
|
95604
95637
|
const sessionID = systemMsg?.info?.sessionID;
|
|
95605
95638
|
if (sessionID) {
|
|
95606
|
-
const criticalIds =
|
|
95639
|
+
const criticalIds = filteredEntries.filter((e) => e.directive_priority === "critical" && e.status !== "archived").map((e) => e.id);
|
|
95607
95640
|
if (criticalIds.length > 0) {
|
|
95608
95641
|
setCriticalShownIds(sessionID, {
|
|
95609
95642
|
ids: criticalIds,
|
|
@@ -96137,8 +96170,12 @@ function getSkillStats(skillPath, directory) {
|
|
|
96137
96170
|
};
|
|
96138
96171
|
}
|
|
96139
96172
|
function formatSkillIndexWithContext(skills, directory) {
|
|
96140
|
-
const
|
|
96141
|
-
|
|
96173
|
+
const usageLogPath = path89.join(directory, ".swarm", "skill-usage.jsonl");
|
|
96174
|
+
let hasHistory = false;
|
|
96175
|
+
try {
|
|
96176
|
+
const stat7 = fs62.statSync(usageLogPath);
|
|
96177
|
+
hasHistory = stat7.size > 0;
|
|
96178
|
+
} catch {}
|
|
96142
96179
|
if (!hasHistory) {
|
|
96143
96180
|
return skills.map((sp) => {
|
|
96144
96181
|
const meta3 = _internals42.readSkillMetadata(sp, directory);
|
|
@@ -96168,6 +96205,121 @@ _internals42.computeRecencyScore = computeRecencyScore;
|
|
|
96168
96205
|
_internals42.computeContextMatchScore = computeContextMatchScore;
|
|
96169
96206
|
|
|
96170
96207
|
// src/hooks/skill-propagation-gate.ts
|
|
96208
|
+
function parseSimpleYaml(content) {
|
|
96209
|
+
const lines = content.split(`
|
|
96210
|
+
`);
|
|
96211
|
+
const result = {};
|
|
96212
|
+
let _currentSection = null;
|
|
96213
|
+
let currentSubSection = null;
|
|
96214
|
+
let currentList = [];
|
|
96215
|
+
let currentListItem = null;
|
|
96216
|
+
for (const line of lines) {
|
|
96217
|
+
if (!line.trim() || line.trim().startsWith("#"))
|
|
96218
|
+
continue;
|
|
96219
|
+
const indent = line.search(/\S/);
|
|
96220
|
+
const trimmed = line.trim();
|
|
96221
|
+
if (indent === 0 && trimmed.endsWith(":")) {
|
|
96222
|
+
if (_currentSection && currentSubSection && currentList.length > 0) {
|
|
96223
|
+
if (!result[_currentSection]) {
|
|
96224
|
+
result[_currentSection] = {};
|
|
96225
|
+
}
|
|
96226
|
+
result[_currentSection][currentSubSection] = currentList;
|
|
96227
|
+
} else if (currentSubSection && currentList.length > 0) {
|
|
96228
|
+
result[currentSubSection] = currentList;
|
|
96229
|
+
}
|
|
96230
|
+
_currentSection = trimmed.slice(0, -1);
|
|
96231
|
+
currentSubSection = null;
|
|
96232
|
+
currentList = [];
|
|
96233
|
+
currentListItem = null;
|
|
96234
|
+
continue;
|
|
96235
|
+
}
|
|
96236
|
+
if (indent === 2 && trimmed.endsWith(":")) {
|
|
96237
|
+
if (_currentSection && currentSubSection && currentList.length > 0) {
|
|
96238
|
+
if (!result[_currentSection]) {
|
|
96239
|
+
result[_currentSection] = {};
|
|
96240
|
+
}
|
|
96241
|
+
result[_currentSection][currentSubSection] = currentList;
|
|
96242
|
+
} else if (currentSubSection && currentList.length > 0) {
|
|
96243
|
+
result[currentSubSection] = currentList;
|
|
96244
|
+
}
|
|
96245
|
+
currentSubSection = trimmed.slice(0, -1);
|
|
96246
|
+
currentList = [];
|
|
96247
|
+
currentListItem = null;
|
|
96248
|
+
continue;
|
|
96249
|
+
}
|
|
96250
|
+
if (trimmed.startsWith("- ")) {
|
|
96251
|
+
currentListItem = null;
|
|
96252
|
+
const rest = trimmed.slice(2);
|
|
96253
|
+
if (rest.includes(":")) {
|
|
96254
|
+
const colonIndex = rest.indexOf(":");
|
|
96255
|
+
const key = rest.slice(0, colonIndex).trim();
|
|
96256
|
+
const value = rest.slice(colonIndex + 1).trim();
|
|
96257
|
+
currentListItem = { [key]: parseYamlValue(value) };
|
|
96258
|
+
} else {
|
|
96259
|
+
currentListItem = { path: trimmed.slice(2) };
|
|
96260
|
+
}
|
|
96261
|
+
currentList.push(currentListItem);
|
|
96262
|
+
continue;
|
|
96263
|
+
}
|
|
96264
|
+
if (indent >= 4 && currentListItem) {
|
|
96265
|
+
if (trimmed.includes(":")) {
|
|
96266
|
+
const colonIndex = trimmed.indexOf(":");
|
|
96267
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
96268
|
+
const value = trimmed.slice(colonIndex + 1).trim();
|
|
96269
|
+
currentListItem[key] = parseYamlValue(value);
|
|
96270
|
+
}
|
|
96271
|
+
}
|
|
96272
|
+
}
|
|
96273
|
+
if (currentSubSection && currentList.length > 0) {
|
|
96274
|
+
if (_currentSection) {
|
|
96275
|
+
if (!result[_currentSection]) {
|
|
96276
|
+
result[_currentSection] = {};
|
|
96277
|
+
}
|
|
96278
|
+
result[_currentSection][currentSubSection] = currentList;
|
|
96279
|
+
} else {
|
|
96280
|
+
result[currentSubSection] = currentList;
|
|
96281
|
+
}
|
|
96282
|
+
}
|
|
96283
|
+
return result;
|
|
96284
|
+
}
|
|
96285
|
+
function parseYamlValue(value) {
|
|
96286
|
+
const trimmed = value.trim();
|
|
96287
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
96288
|
+
return trimmed.slice(1, -1);
|
|
96289
|
+
}
|
|
96290
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
96291
|
+
const inner = trimmed.slice(1, -1);
|
|
96292
|
+
if (!inner.trim())
|
|
96293
|
+
return [];
|
|
96294
|
+
return inner.split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
96295
|
+
}
|
|
96296
|
+
if (trimmed.toLowerCase() === "true")
|
|
96297
|
+
return true;
|
|
96298
|
+
if (trimmed.toLowerCase() === "false")
|
|
96299
|
+
return false;
|
|
96300
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
96301
|
+
return trimmed.includes(".") ? parseFloat(trimmed) : parseInt(trimmed, 10);
|
|
96302
|
+
}
|
|
96303
|
+
return trimmed;
|
|
96304
|
+
}
|
|
96305
|
+
function loadRoutingSkills(directory, targetAgent) {
|
|
96306
|
+
const routingPath = path90.join(directory, ".opencode", "skill-routing.yaml");
|
|
96307
|
+
if (!_internals43.existsSync(routingPath))
|
|
96308
|
+
return [];
|
|
96309
|
+
try {
|
|
96310
|
+
const content = _internals43.readFileSync(routingPath, "utf-8");
|
|
96311
|
+
const config3 = parseSimpleYaml(content);
|
|
96312
|
+
if (!config3?.routing)
|
|
96313
|
+
return [];
|
|
96314
|
+
const routing = config3.routing;
|
|
96315
|
+
const routingEntries = routing[targetAgent];
|
|
96316
|
+
if (!routingEntries || routingEntries.length === 0)
|
|
96317
|
+
return [];
|
|
96318
|
+
return routingEntries.map((entry) => entry.path);
|
|
96319
|
+
} catch {
|
|
96320
|
+
return [];
|
|
96321
|
+
}
|
|
96322
|
+
}
|
|
96171
96323
|
var SKILL_CAPABLE_AGENTS = new Set([
|
|
96172
96324
|
"coder",
|
|
96173
96325
|
"reviewer",
|
|
@@ -96200,11 +96352,12 @@ var _internals43 = {
|
|
|
96200
96352
|
appendSkillUsageEntry,
|
|
96201
96353
|
readSkillUsageEntries,
|
|
96202
96354
|
readSkillUsageEntriesTail,
|
|
96203
|
-
extractSkillsFieldFromPrompt: null,
|
|
96204
96355
|
parseSkillPaths: null,
|
|
96205
96356
|
extractTaskIdFromPrompt: null,
|
|
96357
|
+
extractSkillsFieldFromPrompt: null,
|
|
96206
96358
|
computeSkillRelevanceScore,
|
|
96207
|
-
formatSkillIndexWithContext
|
|
96359
|
+
formatSkillIndexWithContext,
|
|
96360
|
+
loadRoutingSkills: null
|
|
96208
96361
|
};
|
|
96209
96362
|
function discoverAvailableSkills(directory) {
|
|
96210
96363
|
const results = [];
|
|
@@ -96312,22 +96465,21 @@ function parseSkillPaths(fieldValue) {
|
|
|
96312
96465
|
const trimmed = fieldValue.trim();
|
|
96313
96466
|
if (trimmed.toLowerCase() === "none" || trimmed === "")
|
|
96314
96467
|
return [];
|
|
96315
|
-
const
|
|
96316
|
-
|
|
96317
|
-
|
|
96318
|
-
|
|
96319
|
-
|
|
96320
|
-
|
|
96321
|
-
|
|
96322
|
-
|
|
96323
|
-
|
|
96324
|
-
|
|
96325
|
-
|
|
96326
|
-
|
|
96327
|
-
|
|
96328
|
-
|
|
96329
|
-
}
|
|
96330
|
-
return [...new Set(paths)];
|
|
96468
|
+
const commaParts = trimmed.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
96469
|
+
if (commaParts.length === 1 && commaParts[0].startsWith("- ")) {
|
|
96470
|
+
const newlineParts = trimmed.split(`
|
|
96471
|
+
`).map((s) => s.trim()).filter((s) => s.startsWith("- ")).map((s) => s.slice(2).trim());
|
|
96472
|
+
return newlineParts.map((s) => {
|
|
96473
|
+
const parenIndex = s.indexOf("(--");
|
|
96474
|
+
const pathOnly = parenIndex !== -1 ? s.slice(0, parenIndex).trim() : s;
|
|
96475
|
+
const dashIndex = pathOnly.search(/\s+[-–—]\s+/);
|
|
96476
|
+
return dashIndex !== -1 ? pathOnly.slice(0, dashIndex).trim() : pathOnly;
|
|
96477
|
+
}).filter((s) => s.length > 0);
|
|
96478
|
+
}
|
|
96479
|
+
return commaParts.map((s) => {
|
|
96480
|
+
const parenIndex = s.indexOf("(--");
|
|
96481
|
+
return parenIndex !== -1 ? s.slice(0, parenIndex).trim() : s;
|
|
96482
|
+
}).filter((s) => s.length > 0);
|
|
96331
96483
|
}
|
|
96332
96484
|
function extractTaskIdFromPrompt(prompt) {
|
|
96333
96485
|
if (!prompt || typeof prompt !== "string")
|
|
@@ -96342,22 +96494,22 @@ function extractTaskIdFromPrompt(prompt) {
|
|
|
96342
96494
|
}
|
|
96343
96495
|
async function skillPropagationGateBefore(directory, input, config3) {
|
|
96344
96496
|
if (!config3.enabled)
|
|
96345
|
-
return { blocked: false, reason: null };
|
|
96497
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96346
96498
|
const toolName = typeof input.tool === "string" ? input.tool : "";
|
|
96347
96499
|
if (toolName !== "task" && toolName !== "Task")
|
|
96348
|
-
return { blocked: false, reason: null };
|
|
96500
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96349
96501
|
const agentRaw = typeof input.agent === "string" ? input.agent : "";
|
|
96350
96502
|
if (!agentRaw)
|
|
96351
|
-
return { blocked: false, reason: null };
|
|
96503
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96352
96504
|
const baseAgent = stripKnownSwarmPrefix(agentRaw);
|
|
96353
96505
|
if (baseAgent !== "architect")
|
|
96354
|
-
return { blocked: false, reason: null };
|
|
96506
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96355
96507
|
const parsed = _internals43.parseDelegationArgs(input.args);
|
|
96356
96508
|
if (!parsed)
|
|
96357
|
-
return { blocked: false, reason: null };
|
|
96509
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96358
96510
|
const targetBase = stripKnownSwarmPrefix(parsed.targetAgent);
|
|
96359
96511
|
if (!_internals43.SKILL_CAPABLE_AGENTS.has(targetBase))
|
|
96360
|
-
return { blocked: false, reason: null };
|
|
96512
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96361
96513
|
const sessionID = typeof input.sessionID === "string" ? input.sessionID : "unknown";
|
|
96362
96514
|
const availableSkills = _internals43.discoverAvailableSkills(directory);
|
|
96363
96515
|
const skillsValue = parsed.skillsField.trim();
|
|
@@ -96420,6 +96572,23 @@ async function skillPropagationGateBefore(directory, input, config3) {
|
|
|
96420
96572
|
warn(`[skill-propagation-gate] skill scoring failed (non-blocking): ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
96421
96573
|
}
|
|
96422
96574
|
}
|
|
96575
|
+
try {
|
|
96576
|
+
const routingPaths = _internals43.loadRoutingSkills(directory, targetBase);
|
|
96577
|
+
if (routingPaths.length > 0) {
|
|
96578
|
+
const existingPaths = new Set(scored.map((s) => s.skillPath));
|
|
96579
|
+
for (const routingPath of routingPaths) {
|
|
96580
|
+
if (!existingPaths.has(routingPath)) {
|
|
96581
|
+
scored.push({
|
|
96582
|
+
skillPath: routingPath,
|
|
96583
|
+
score: 0.9,
|
|
96584
|
+
usageCount: 0
|
|
96585
|
+
});
|
|
96586
|
+
existingPaths.add(routingPath);
|
|
96587
|
+
}
|
|
96588
|
+
}
|
|
96589
|
+
scored.sort((a, b) => b.score - a.score || b.usageCount - a.usageCount);
|
|
96590
|
+
}
|
|
96591
|
+
} catch {}
|
|
96423
96592
|
if (availableSkills.length > 0) {
|
|
96424
96593
|
try {
|
|
96425
96594
|
let skillsForIndex = availableSkills;
|
|
@@ -96478,14 +96647,14 @@ ${newSection}`;
|
|
|
96478
96647
|
const coderHadSkills = skillsValue.length > 0 && skillsValue.toLowerCase() !== "none";
|
|
96479
96648
|
if (!hasSkillsUsedByCoder && coderHadSkills) {
|
|
96480
96649
|
const message = `SKILLS_USED_BY_CODER warning: Delegating to reviewer without SKILLS_USED_BY_CODER field. ` + `Add SKILLS_USED_BY_CODER with the skills the coder received for this task.`;
|
|
96481
|
-
return { blocked: false, reason: message };
|
|
96650
|
+
return { blocked: false, reason: message, recommendedSkills: undefined };
|
|
96482
96651
|
}
|
|
96483
96652
|
}
|
|
96484
96653
|
if (availableSkills.length === 0)
|
|
96485
|
-
return { blocked: false, reason: null };
|
|
96654
|
+
return { blocked: false, reason: null, recommendedSkills: undefined };
|
|
96486
96655
|
const skillsLower = skillsValue.toLowerCase();
|
|
96487
96656
|
if (skillsValue && skillsLower !== "none")
|
|
96488
|
-
return { blocked: false, reason: null };
|
|
96657
|
+
return { blocked: false, reason: null, recommendedSkills: scored };
|
|
96489
96658
|
const skillNames = availableSkills.map((p) => {
|
|
96490
96659
|
const parts2 = p.split("/");
|
|
96491
96660
|
return parts2[parts2.length - 2] ?? p;
|
|
@@ -96505,9 +96674,9 @@ ${newSection}`;
|
|
|
96505
96674
|
} catch {}
|
|
96506
96675
|
if (config3.enforce) {
|
|
96507
96676
|
const blockedMsg = `Blocked by skill propagation gate: Delegating to ${targetBase} without SKILLS field. ` + `Available skills: ${skillNames.join(", ")}. ` + `Add a SKILLS: field or set enforce: false in config.`;
|
|
96508
|
-
return { blocked: true, reason: blockedMsg };
|
|
96677
|
+
return { blocked: true, reason: blockedMsg, recommendedSkills: undefined };
|
|
96509
96678
|
}
|
|
96510
|
-
return { blocked: false, reason: warningMsg };
|
|
96679
|
+
return { blocked: false, reason: warningMsg, recommendedSkills: undefined };
|
|
96511
96680
|
}
|
|
96512
96681
|
var COMPLIANCE_PATTERN = /SKILL_COMPLIANCE\s*:\s*(COMPLIANT|PARTIAL|VIOLATED)(?:\s*(?:—|-)\s*(.*))?\s*$/i;
|
|
96513
96682
|
var CODER_SKILLS_PATTERN = /SKILLS_USED_BY_CODER\s*:\s*(.+)/i;
|
|
@@ -96616,10 +96785,8 @@ async function skillPropagationTransformScan(directory, output, sessionID) {
|
|
|
96616
96785
|
continue;
|
|
96617
96786
|
let currentTargetAgent = "";
|
|
96618
96787
|
let skillsField = "";
|
|
96619
|
-
const
|
|
96620
|
-
`)
|
|
96621
|
-
for (let lineIndex = 0;lineIndex < textLines.length; lineIndex++) {
|
|
96622
|
-
const line = textLines[lineIndex] ?? "";
|
|
96788
|
+
for (const line of text.split(`
|
|
96789
|
+
`)) {
|
|
96623
96790
|
const trimmed = line.trim();
|
|
96624
96791
|
if (trimmed.match(/TO\s+(coder|reviewer|test_engineer|sme|docs|designer)/i)) {
|
|
96625
96792
|
const agentMatch = trimmed.match(/TO\s+(coder|reviewer|test_engineer|sme|docs|designer)/i);
|
|
@@ -96627,8 +96794,7 @@ async function skillPropagationTransformScan(directory, output, sessionID) {
|
|
|
96627
96794
|
currentTargetAgent = agentMatch[1].toLowerCase();
|
|
96628
96795
|
}
|
|
96629
96796
|
if (trimmed.startsWith("SKILLS:")) {
|
|
96630
|
-
skillsField =
|
|
96631
|
-
`));
|
|
96797
|
+
skillsField = trimmed.slice("SKILLS:".length).trim();
|
|
96632
96798
|
}
|
|
96633
96799
|
if (currentTargetAgent && skillsField && skillsField.toLowerCase() !== "none") {
|
|
96634
96800
|
const skillPaths = _internals43.parseSkillPaths(skillsField);
|
|
@@ -96664,10 +96830,11 @@ _internals43.skillPropagationTransformScan = skillPropagationTransformScan;
|
|
|
96664
96830
|
_internals43.writeWarnEvent = writeWarnEvent2;
|
|
96665
96831
|
_internals43.discoverAvailableSkills = discoverAvailableSkills;
|
|
96666
96832
|
_internals43.parseDelegationArgs = parseDelegationArgs;
|
|
96667
|
-
_internals43.extractSkillsFieldFromPrompt = extractSkillsFieldFromPrompt;
|
|
96668
96833
|
_internals43.parseSkillPaths = parseSkillPaths;
|
|
96669
96834
|
_internals43.extractTaskIdFromPrompt = extractTaskIdFromPrompt;
|
|
96835
|
+
_internals43.extractSkillsFieldFromPrompt = extractSkillsFieldFromPrompt;
|
|
96670
96836
|
_internals43.formatSkillIndexWithContext = formatSkillIndexWithContext;
|
|
96837
|
+
_internals43.loadRoutingSkills = loadRoutingSkills;
|
|
96671
96838
|
|
|
96672
96839
|
// src/hooks/slop-detector.ts
|
|
96673
96840
|
import * as fs64 from "node:fs";
|
|
@@ -99949,7 +100116,7 @@ import {
|
|
|
99949
100116
|
readFileSync as readFileSync44,
|
|
99950
100117
|
writeFileSync as writeFileSync17
|
|
99951
100118
|
} from "node:fs";
|
|
99952
|
-
import { join as
|
|
100119
|
+
import { join as join84 } from "node:path";
|
|
99953
100120
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
99954
100121
|
var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
|
|
99955
100122
|
var COUNCIL_GATE_NAME = "council";
|
|
@@ -99983,9 +100150,9 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
99983
100150
|
if (!VALID_TASK_ID.test(synthesis.taskId)) {
|
|
99984
100151
|
throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
|
|
99985
100152
|
}
|
|
99986
|
-
const dir =
|
|
100153
|
+
const dir = join84(workingDir, EVIDENCE_DIR2);
|
|
99987
100154
|
mkdirSync25(dir, { recursive: true });
|
|
99988
|
-
const filePath =
|
|
100155
|
+
const filePath = join84(dir, `${synthesis.taskId}.json`);
|
|
99989
100156
|
const existingRoot = Object.create(null);
|
|
99990
100157
|
if (existsSync53(filePath)) {
|
|
99991
100158
|
try {
|
|
@@ -100019,7 +100186,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
100019
100186
|
updated.required_gates = [];
|
|
100020
100187
|
writeFileSync17(filePath, JSON.stringify(updated, null, 2));
|
|
100021
100188
|
try {
|
|
100022
|
-
const councilDir =
|
|
100189
|
+
const councilDir = join84(workingDir, ".swarm", "council");
|
|
100023
100190
|
mkdirSync25(councilDir, { recursive: true });
|
|
100024
100191
|
const auditLine = JSON.stringify({
|
|
100025
100192
|
round: synthesis.roundNumber,
|
|
@@ -100027,7 +100194,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
100027
100194
|
timestamp: synthesis.timestamp,
|
|
100028
100195
|
vetoedBy: synthesis.vetoedBy
|
|
100029
100196
|
});
|
|
100030
|
-
appendFileSync12(
|
|
100197
|
+
appendFileSync12(join84(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
|
|
100031
100198
|
`);
|
|
100032
100199
|
} catch (auditError) {
|
|
100033
100200
|
console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
|
|
@@ -100350,20 +100517,20 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
|
|
|
100350
100517
|
|
|
100351
100518
|
// src/council/criteria-store.ts
|
|
100352
100519
|
import { existsSync as existsSync54, mkdirSync as mkdirSync26, readFileSync as readFileSync45, writeFileSync as writeFileSync18 } from "node:fs";
|
|
100353
|
-
import { join as
|
|
100520
|
+
import { join as join85 } from "node:path";
|
|
100354
100521
|
var COUNCIL_DIR = ".swarm/council";
|
|
100355
100522
|
function writeCriteria(workingDir, taskId, criteria) {
|
|
100356
|
-
const dir =
|
|
100523
|
+
const dir = join85(workingDir, COUNCIL_DIR);
|
|
100357
100524
|
mkdirSync26(dir, { recursive: true });
|
|
100358
100525
|
const payload = {
|
|
100359
100526
|
taskId,
|
|
100360
100527
|
criteria,
|
|
100361
100528
|
declaredAt: new Date().toISOString()
|
|
100362
100529
|
};
|
|
100363
|
-
writeFileSync18(
|
|
100530
|
+
writeFileSync18(join85(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
|
|
100364
100531
|
}
|
|
100365
100532
|
function readCriteria(workingDir, taskId) {
|
|
100366
|
-
const filePath =
|
|
100533
|
+
const filePath = join85(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
|
|
100367
100534
|
if (!existsSync54(filePath))
|
|
100368
100535
|
return null;
|
|
100369
100536
|
try {
|
|
@@ -118885,6 +119052,73 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
118885
119052
|
skillSession.pendingAdvisoryMessages ??= [];
|
|
118886
119053
|
skillSession.pendingAdvisoryMessages.push(skillResult.reason);
|
|
118887
119054
|
}
|
|
119055
|
+
if (skillResult.recommendedSkills && skillResult.recommendedSkills.length > 0) {
|
|
119056
|
+
const argsRecord = input.args;
|
|
119057
|
+
const promptRaw = argsRecord.prompt;
|
|
119058
|
+
if (typeof promptRaw === "string") {
|
|
119059
|
+
const parsedDelegation = parseDelegationArgs(input.args);
|
|
119060
|
+
if (parsedDelegation) {
|
|
119061
|
+
const existingSkills = parsedDelegation.skillsField.trim();
|
|
119062
|
+
if (!existingSkills) {
|
|
119063
|
+
const qualified = skillResult.recommendedSkills.filter((s) => s.score >= 0.5);
|
|
119064
|
+
if (qualified.length === 0) {
|
|
119065
|
+
argsRecord.prompt = `SKILLS: none
|
|
119066
|
+
|
|
119067
|
+
${promptRaw}`;
|
|
119068
|
+
console.warn("[skill-propagation-gate] No skills above threshold 0.5 — injected SKILLS: none");
|
|
119069
|
+
} else {
|
|
119070
|
+
const topSkills = qualified.slice(0, 5);
|
|
119071
|
+
const SKILL_DESCRIPTIONS = {
|
|
119072
|
+
"writing-tests": "Guidelines for writing tests",
|
|
119073
|
+
"engineering-conventions": "Engineering invariants and conventions",
|
|
119074
|
+
"running-tests": "Safe test execution patterns",
|
|
119075
|
+
"commit-pr": "Commit and PR workflow",
|
|
119076
|
+
"swarm-implement": "Swarm implementation workflow",
|
|
119077
|
+
"issue-tracer": "Issue investigation workflow",
|
|
119078
|
+
"qa-sweep": "QA sweep workflow",
|
|
119079
|
+
"research-first": "Research-driven approach",
|
|
119080
|
+
"swarm-pr-review": "PR review workflow",
|
|
119081
|
+
"tech-debt-ci-review": "Tech debt and CI review",
|
|
119082
|
+
browse: "Fast web browsing",
|
|
119083
|
+
code: "Expert coding workflow",
|
|
119084
|
+
review: "Pre-landing PR review",
|
|
119085
|
+
"ci-failure-resolver": "CI/CD failure resolution"
|
|
119086
|
+
};
|
|
119087
|
+
const skillPaths = topSkills.map((s) => {
|
|
119088
|
+
const dirName = path144.basename(path144.dirname(s.skillPath));
|
|
119089
|
+
const desc = SKILL_DESCRIPTIONS[dirName] ?? dirName;
|
|
119090
|
+
return `file:${s.skillPath} (-- ${desc})`;
|
|
119091
|
+
}).join(", ");
|
|
119092
|
+
const skillsLine = `SKILLS: ${skillPaths}`;
|
|
119093
|
+
const newPrompt = `${skillsLine}
|
|
119094
|
+
|
|
119095
|
+
${promptRaw}`;
|
|
119096
|
+
argsRecord.prompt = newPrompt;
|
|
119097
|
+
const skillNames = topSkills.map((s) => `${path144.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
|
|
119098
|
+
console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
|
|
119099
|
+
for (const skill of topSkills) {
|
|
119100
|
+
try {
|
|
119101
|
+
appendSkillUsageEntry(ctx.directory, {
|
|
119102
|
+
skillPath: skill.skillPath,
|
|
119103
|
+
agentName: String(input.agent),
|
|
119104
|
+
taskID: "injection",
|
|
119105
|
+
timestamp: new Date().toISOString(),
|
|
119106
|
+
complianceVerdict: "not_checked",
|
|
119107
|
+
sessionID: input.sessionID
|
|
119108
|
+
});
|
|
119109
|
+
} catch {}
|
|
119110
|
+
}
|
|
119111
|
+
const targetAgent = parsedDelegation.targetAgent.toLowerCase();
|
|
119112
|
+
if (targetAgent.includes("reviewer")) {
|
|
119113
|
+
const usedByCoderLine = `SKILLS_USED_BY_CODER: ${topSkills.map((s) => `file:${s.skillPath}`).join(", ")}`;
|
|
119114
|
+
argsRecord.prompt = `${newPrompt}
|
|
119115
|
+
${usedByCoderLine}`;
|
|
119116
|
+
}
|
|
119117
|
+
}
|
|
119118
|
+
}
|
|
119119
|
+
}
|
|
119120
|
+
}
|
|
119121
|
+
}
|
|
118888
119122
|
if (swarmState.lastBudgetPct >= 50) {
|
|
118889
119123
|
const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
|
|
118890
119124
|
if (!pressureSession.contextPressureWarningSent) {
|