opencode-swarm 7.99.4 → 7.99.6
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/.opencode/skills/brainstorm/SKILL.md +1 -0
- package/.opencode/skills/codebase-review-swarm/SKILL.md +1 -1
- package/.opencode/skills/council/SKILL.md +5 -3
- package/.opencode/skills/deep-dive/SKILL.md +2 -2
- package/.opencode/skills/deep-research/SKILL.md +7 -4
- package/.opencode/skills/plan/SKILL.md +1 -0
- package/.opencode/skills/specify/SKILL.md +1 -0
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +3 -1
- package/.opencode/skills/swarm-pr-review/SKILL.md +9 -9
- package/dist/cli/{config-doctor-9fhfy6p7.js → config-doctor-1j77p1jy.js} +1 -1
- package/dist/cli/{guardrail-explain-3dxh8t8v.js → guardrail-explain-s4c2h8mv.js} +3 -3
- package/dist/cli/{index-svf2zjxs.js → index-0d3pmjf9.js} +28 -0
- package/dist/cli/{index-0avc4356.js → index-1kcp4q4y.js} +21 -18
- package/dist/cli/{index-r431nee9.js → index-etssqrff.js} +3 -3
- package/dist/cli/{index-rq7arj2r.js → index-ksc4ftms.js} +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/commands/registry.d.ts +3 -1
- package/dist/index.js +460 -90
- package/dist/tools/repo-graph/builder.d.ts +7 -5
- package/dist/tools/repo-graph/query.d.ts +2 -1
- package/dist/tools/repo-graph/types.d.ts +37 -0
- package/dist/tools/repo-graph.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.99.
|
|
72
|
+
version: "7.99.6",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -17516,21 +17516,6 @@ var init_bundled_skills = __esm(() => {
|
|
|
17516
17516
|
syncedProjectSkillTargets = new Set;
|
|
17517
17517
|
});
|
|
17518
17518
|
|
|
17519
|
-
// src/utils/errors.ts
|
|
17520
|
-
var SwarmError;
|
|
17521
|
-
var init_errors3 = __esm(() => {
|
|
17522
|
-
SwarmError = class SwarmError extends Error {
|
|
17523
|
-
code;
|
|
17524
|
-
guidance;
|
|
17525
|
-
constructor(message, code, guidance) {
|
|
17526
|
-
super(message);
|
|
17527
|
-
this.name = "SwarmError";
|
|
17528
|
-
this.code = code;
|
|
17529
|
-
this.guidance = guidance;
|
|
17530
|
-
}
|
|
17531
|
-
};
|
|
17532
|
-
});
|
|
17533
|
-
|
|
17534
17519
|
// src/utils/logger.ts
|
|
17535
17520
|
function isDebug() {
|
|
17536
17521
|
return process.env.OPENCODE_SWARM_DEBUG === "1";
|
|
@@ -17573,6 +17558,21 @@ function error48(message, data) {
|
|
|
17573
17558
|
}
|
|
17574
17559
|
var init_logger = () => {};
|
|
17575
17560
|
|
|
17561
|
+
// src/utils/errors.ts
|
|
17562
|
+
var SwarmError;
|
|
17563
|
+
var init_errors3 = __esm(() => {
|
|
17564
|
+
SwarmError = class SwarmError extends Error {
|
|
17565
|
+
code;
|
|
17566
|
+
guidance;
|
|
17567
|
+
constructor(message, code, guidance) {
|
|
17568
|
+
super(message);
|
|
17569
|
+
this.name = "SwarmError";
|
|
17570
|
+
this.code = code;
|
|
17571
|
+
this.guidance = guidance;
|
|
17572
|
+
}
|
|
17573
|
+
};
|
|
17574
|
+
});
|
|
17575
|
+
|
|
17576
17576
|
// src/utils/regex.ts
|
|
17577
17577
|
function escapeRegex2(s) {
|
|
17578
17578
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -77880,6 +77880,21 @@ function emitObjectTypeMismatch(key, value, findings) {
|
|
|
77880
77880
|
});
|
|
77881
77881
|
}
|
|
77882
77882
|
}
|
|
77883
|
+
function emitWorktreeIsolationLayeringAdvisory(config3, findings) {
|
|
77884
|
+
const parallelization = config3.parallelization;
|
|
77885
|
+
const worktreePolicy = config3.worktree?.policy ?? "auto";
|
|
77886
|
+
if (parallelization?.enabled === true && (parallelization.maxConcurrentTasks ?? 1) > 1 && worktreePolicy !== "disabled") {
|
|
77887
|
+
findings.push({
|
|
77888
|
+
id: "worktree-isolation-baseline-active",
|
|
77889
|
+
title: "Worktree isolation is already active for standard parallel coders",
|
|
77890
|
+
description: "Standard parallel coders already use baseline worktree isolation through the parallel execution profile plus top-level worktree.policy. Lean Turbo and Epic are additive strategies, not requirements for obtaining worktree isolation.",
|
|
77891
|
+
severity: "warn",
|
|
77892
|
+
path: "worktree.policy",
|
|
77893
|
+
currentValue: worktreePolicy,
|
|
77894
|
+
autoFixable: false
|
|
77895
|
+
});
|
|
77896
|
+
}
|
|
77897
|
+
}
|
|
77883
77898
|
function getUserConfigDir3() {
|
|
77884
77899
|
return process.env.XDG_CONFIG_HOME || path69.join(os15.homedir(), ".config");
|
|
77885
77900
|
}
|
|
@@ -78440,6 +78455,14 @@ function validateConfigKey(path70, value) {
|
|
|
78440
78455
|
emitObjectTypeMismatch("summaries", value, findings);
|
|
78441
78456
|
break;
|
|
78442
78457
|
}
|
|
78458
|
+
case "auto_review": {
|
|
78459
|
+
emitObjectTypeMismatch("auto_review", value, findings);
|
|
78460
|
+
break;
|
|
78461
|
+
}
|
|
78462
|
+
case "repo_graph": {
|
|
78463
|
+
emitObjectTypeMismatch("repo_graph", value, findings);
|
|
78464
|
+
break;
|
|
78465
|
+
}
|
|
78443
78466
|
case "review_passes": {
|
|
78444
78467
|
emitObjectTypeMismatch("review_passes", value, findings);
|
|
78445
78468
|
break;
|
|
@@ -78516,6 +78539,10 @@ function validateConfigKey(path70, value) {
|
|
|
78516
78539
|
emitObjectTypeMismatch("skill_improver", value, findings);
|
|
78517
78540
|
break;
|
|
78518
78541
|
}
|
|
78542
|
+
case "skills": {
|
|
78543
|
+
emitObjectTypeMismatch("skills", value, findings);
|
|
78544
|
+
break;
|
|
78545
|
+
}
|
|
78519
78546
|
case "spec_writer": {
|
|
78520
78547
|
emitObjectTypeMismatch("spec_writer", value, findings);
|
|
78521
78548
|
break;
|
|
@@ -78701,6 +78728,7 @@ function walkConfigAndValidate(obj, path70, findings, visited = new WeakSet) {
|
|
|
78701
78728
|
function runConfigDoctor(config3, directory) {
|
|
78702
78729
|
const findings = [];
|
|
78703
78730
|
walkConfigAndValidate(config3, "", findings);
|
|
78731
|
+
emitWorktreeIsolationLayeringAdvisory(config3, findings);
|
|
78704
78732
|
const summary = {
|
|
78705
78733
|
info: findings.filter((f) => f.severity === "info").length,
|
|
78706
78734
|
warn: findings.filter((f) => f.severity === "warn").length,
|
|
@@ -101057,6 +101085,13 @@ function findSimilarCommands(query) {
|
|
|
101057
101085
|
scored.sort((a, b) => a.score - b.score);
|
|
101058
101086
|
return scored.slice(0, 3).map((s) => s.cmd);
|
|
101059
101087
|
}
|
|
101088
|
+
function emitValidationWarnings(prefix, warnings) {
|
|
101089
|
+
if (warnings.length === 0)
|
|
101090
|
+
return;
|
|
101091
|
+
warn(`${prefix}:
|
|
101092
|
+
${warnings.join(`
|
|
101093
|
+
`)}`);
|
|
101094
|
+
}
|
|
101060
101095
|
function buildDetailedHelp(commandName, entry) {
|
|
101061
101096
|
const lines = [];
|
|
101062
101097
|
lines.push(`## /swarm ${commandName}`, "");
|
|
@@ -101191,6 +101226,7 @@ function resolveCommand(tokens) {
|
|
|
101191
101226
|
var COMMAND_REGISTRY, VALID_COMMANDS, _internals15, validation;
|
|
101192
101227
|
var init_registry = __esm(() => {
|
|
101193
101228
|
init_bundled_skills();
|
|
101229
|
+
init_logger();
|
|
101194
101230
|
init_acknowledge_spec_drift();
|
|
101195
101231
|
init_agents();
|
|
101196
101232
|
init_archive();
|
|
@@ -101693,7 +101729,7 @@ Subcommands:
|
|
|
101693
101729
|
handler: (ctx) => handleModeCommandWithBundledSkills(ctx, handlePrReviewCommand),
|
|
101694
101730
|
description: "Launch deep PR review with multi-lane analysis [url] [--council]",
|
|
101695
101731
|
args: "<pr-url|owner/repo#N|N> [--council]",
|
|
101696
|
-
details: "Launches a structured PR review: reconstructs PR intent via obligation extraction cascade,
|
|
101732
|
+
details: "Launches a structured PR review: reconstructs PR intent via obligation extraction cascade, launches all 6 fixed base explorer lanes through dispatch_lanes_async while the architect keeps doing non-dependent work, polls collect_lane_results incrementally, runs every triggered micro-lane, validates findings through independent reviewer confirmation, applies critic challenge to HIGH/CRITICAL findings, then synthesizes only after coverage is closed. If lane tools cannot close coverage, Task-tool dispatch is the final verified-equivalent fallback; if equivalence cannot be proven, the review is BLOCKED rather than degraded. --council variant fires adversarial multi-model review. Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolves against origin remote).",
|
|
101697
101733
|
category: "agent",
|
|
101698
101734
|
toolPolicy: "none"
|
|
101699
101735
|
},
|
|
@@ -102119,6 +102155,7 @@ Subcommands:
|
|
|
102119
102155
|
handleHelpCommand,
|
|
102120
102156
|
validateAliases,
|
|
102121
102157
|
validateToolPolicy,
|
|
102158
|
+
emitValidationWarnings,
|
|
102122
102159
|
resolveCommand,
|
|
102123
102160
|
levenshteinDistance: levenshteinDistance2,
|
|
102124
102161
|
findSimilarCommands,
|
|
@@ -102130,20 +102167,12 @@ Subcommands:
|
|
|
102130
102167
|
${validation.errors.join(`
|
|
102131
102168
|
`)}`);
|
|
102132
102169
|
}
|
|
102133
|
-
|
|
102134
|
-
console.warn(`COMMAND_REGISTRY alias warnings:
|
|
102135
|
-
${validation.warnings.join(`
|
|
102136
|
-
`)}`);
|
|
102137
|
-
}
|
|
102170
|
+
_internals15.emitValidationWarnings("COMMAND_REGISTRY alias warnings", validation.warnings);
|
|
102138
102171
|
try {
|
|
102139
102172
|
const toolPolicyValidation = _internals15.validateToolPolicy();
|
|
102140
|
-
|
|
102141
|
-
console.warn(`COMMAND_REGISTRY toolPolicy warnings:
|
|
102142
|
-
${toolPolicyValidation.warnings.join(`
|
|
102143
|
-
`)}`);
|
|
102144
|
-
}
|
|
102173
|
+
_internals15.emitValidationWarnings("COMMAND_REGISTRY toolPolicy warnings", toolPolicyValidation.warnings);
|
|
102145
102174
|
} catch (e) {
|
|
102146
|
-
|
|
102175
|
+
warn(`COMMAND_REGISTRY toolPolicy validation failed (non-fatal): ${e.message}`);
|
|
102147
102176
|
}
|
|
102148
102177
|
});
|
|
102149
102178
|
|
|
@@ -102726,6 +102755,8 @@ If a tool modifies a file, it is a CODER tool. Delegate.
|
|
|
102726
102755
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
102727
102756
|
Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required. This exception NEVER applies to coder delegations. Preserve ONE task per coder call.
|
|
102728
102757
|
Separate parallel-mode exception (distinct from the Stage B exception above, and the ONLY case where more than one coder may be dispatched before waiting): when an active \`[PARALLEL EXECUTION PROFILE]\` directive is present in your context (parallelization_enabled=true), you MAY dispatch multiple {{AGENT_PREFIX}}coder agents in a single message — up to the stated max_concurrent_tasks — but ONLY for distinct, dependency-ready tasks whose declared file scopes do NOT overlap. Each coder still requires its own \`declare_scope\` call and carries exactly ONE task (Rule 3 still holds: never batch multiple objectives into one coder). Parallel coders each run in an isolated git worktree, so their writes never collide and are merged back automatically. If no \`[PARALLEL EXECUTION PROFILE]\` directive is present, dispatch coders one at a time.
|
|
102758
|
+
|
|
102759
|
+
> **WORKTREE ISOLATION IS BASELINE.** Standard parallel coders use isolated git worktrees by default; this is governed by the top-level \`worktree.policy\` setting (default \`auto\`) in \`PluginConfig\` — a sibling of \`parallelization:\`, not nested under it — and is active whenever the plan's \`parallelization_enabled=true\`. \`turbo.lean.worktree_isolation\` is a separate, Lean-Turbo-internal flag (default \`false\`); it is one possible SOURCE but NOT the recommended one. Do NOT recommend Lean Turbo (or Epic) SOLELY to obtain worktree isolation; recommend them only for what they add beyond baseline (Lean Turbo: lane planning, file locks, phase reviewer, integrated diff; Epic: co-change awareness + auto-decide). Lean Turbo users can also enable isolation via \`turbo.lean.worktree_isolation: true\`, but this is the secondary/legacy path — the recommended path is \`worktree.policy\`.
|
|
102729
102760
|
Read-only advisory-lane exception (NON-BLOCKING; distinct from both exceptions above): the "Send, STOP, wait" rule governs MUTATION delegations (coder, and the test_engineer/reviewer Stage B completion gates). It does NOT govern read-only advisory exploration/review lanes. When you dispatch read-only advisory lanes — \`{{AGENT_PREFIX}}explorer\`, \`{{AGENT_PREFIX}}sme\`, \`{{AGENT_PREFIX}}researcher\`, the council members (\`council_generalist\`/\`council_skeptic\`/\`council_domain_expert\`), or an advisory \`{{AGENT_PREFIX}}critic\` lane — use the NON-BLOCKING path so you keep working while they run. Dispatch PROMPTLY: emit the \`dispatch_lanes_async\` call EARLY with compact lane prompts — do not accumulate long planning prose or build oversized inline prompts first, or the tool call can be truncated out of your message and the lanes never launch (a real failure mode on smaller models). The lane mechanism is a SINGLE \`dispatch_lanes_async\` call carrying all lane specs — NOT a per-agent Task/run-in-background pattern. Call \`dispatch_lanes_async\` with all lane specs in one call, record the returned \`batch_id\`, then IMMEDIATELY continue non-dependent architect work (refine the plan/obligation ledger, inspect metadata, prepare the synthesis/reviewer structure, run deterministic read-only tools). Poll incrementally with \`collect_lane_results\` without \`wait\` (or with \`wait: false\`) to harvest lanes as they settle; process completed lane output immediately while other lanes remain pending/running, then continue independent work between polls. Do NOT sit idle waiting on running lanes, and do NOT synthesize findings from still-running lanes. Join later by calling \`collect_lane_results\` with \`wait: true\` as the explicit barrier immediately before you synthesize. Use blocking \`dispatch_lanes\` only when \`dispatch_lanes_async\`/promptAsync is unavailable. Keep each lane prompt compact: send large shared context (PR diff, ledger, scope) ONCE via the \`common_prompt\` field, or have lanes read it from a file by absolute path, instead of inlining the same blob into every lane prompt — inlining large context into many lanes is what produces malformed or truncated tool-call JSON and forces clumsy file workarounds. This non-blocking exception applies ONLY to read-only advisory lanes; it NEVER applies to coder delegations, to the test_engineer/reviewer Stage B completion gates, or to the critic PLAN-review gate, which all still follow "Send, STOP, wait" (or the Stage B parallel-dispatch exception above).
|
|
102730
102761
|
3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
|
|
102731
102762
|
3a. PRE-DELEGATION SCOPE CALL (required): BEFORE every {{AGENT_PREFIX}}coder delegation, you MUST call \`declare_scope\` with { taskId, files } listing the exact file(s) this task will modify (including generated/lockfile paths). No \`declare_scope\` call → no coder delegation. See Rule 1a.
|
|
@@ -103331,6 +103362,7 @@ ACTION: Load skill file:.opencode/skills/pre-phase-briefing/SKILL.md immediately
|
|
|
103331
103362
|
|
|
103332
103363
|
HARD CONSTRAINTS:
|
|
103333
103364
|
- Complete the codebase reality report before spec finalization, plan generation, plan ingestion, declare_scope, or starting/resuming phase implementation. Dispatching the reality-check lanes asynchronously is allowed and preferred; settling all lanes before any of that downstream work is not optional.
|
|
103365
|
+
- When reality-check lanes are dispatched asynchronously, record the \`batch_id\`, keep doing non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103334
103366
|
|
|
103335
103367
|
### MODE: COUNCIL
|
|
103336
103368
|
Activates when the user invokes /swarm council or requests a council-style decision review.
|
|
@@ -103341,6 +103373,7 @@ ACTION: Load skill file:.opencode/skills/council/SKILL.md immediately. Follow th
|
|
|
103341
103373
|
|
|
103342
103374
|
HARD CONSTRAINTS:
|
|
103343
103375
|
- Provide research context up front and synthesize only from returned council member responses.
|
|
103376
|
+
- For async council lanes, record the \`batch_id\`, keep doing non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103344
103377
|
|
|
103345
103378
|
### MODE: DEEP_DIVE
|
|
103346
103379
|
Activates when: architect receives \`[MODE: DEEP_DIVE profile=X max_explorers=N output=X update_main=X allow_dirty=X] <scope>\` signal from the deep-dive command handler.
|
|
@@ -103357,6 +103390,7 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
103357
103390
|
- No final finding may appear in the report without reviewer verification
|
|
103358
103391
|
- Explorers generate candidate findings only — reviewers verify or reject
|
|
103359
103392
|
- Critics challenge only HIGH/CRITICAL findings — do NOT waste cycles on lower severity
|
|
103393
|
+
- For async explorer waves, record the \`batch_id\`, keep doing non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103360
103394
|
|
|
103361
103395
|
### MODE: LOOP
|
|
103362
103396
|
Activates when: architect receives \`[MODE: LOOP max_cycles=N autonomy=checkpoint|auto depth=standard|exhaustive resume=true|false] <objective>\` signal from the loop command handler.
|
|
@@ -103390,6 +103424,8 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
103390
103424
|
- Critics challenge only high-stakes / contested claims — do NOT waste cycles on well-supported ones
|
|
103391
103425
|
- If council.general.enabled is false or no search API key is configured, surface that and STOP — do not produce ungrounded research
|
|
103392
103426
|
|
|
103427
|
+
- For async synthesis lanes, record the \`batch_id\`, keep doing non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103428
|
+
|
|
103393
103429
|
### MODE: CODEBASE_REVIEW
|
|
103394
103430
|
Activates when: architect receives \`[MODE: CODEBASE_REVIEW mode=X output=X update_main=X allow_dirty=X tracks="..." continue_run="..."] scope="..."\` signal from the codebase-review command handler.
|
|
103395
103431
|
|
|
@@ -103409,6 +103445,8 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
103409
103445
|
- Every repo-derived factual claim needs quote-grounded evidence with file path and line/range
|
|
103410
103446
|
- Final report is forbidden until selected-track coverage is closed and final critic passes
|
|
103411
103447
|
|
|
103448
|
+
- For async inventory or candidate-generation lanes, record the \`batch_id\`, keep doing non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103449
|
+
|
|
103412
103450
|
### MODE: DESIGN_DOCS
|
|
103413
103451
|
Activates when: architect receives \`[MODE: DESIGN_DOCS out=X lang=X update=X] <description>\` signal from the design-docs command handler (issue #1080).
|
|
103414
103452
|
|
|
@@ -103440,9 +103478,11 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
103440
103478
|
- No finding may appear as CONFIRMED in the final report without reviewer validation provenance
|
|
103441
103479
|
- Test execution, explorer lanes, reviewer dispatch, and critic challenge are all permitted within this mode
|
|
103442
103480
|
- Quality is the only metric — time, tokens, and agent dispatches are irrelevant to correctness
|
|
103443
|
-
- FOLLOW THE SKILL EXACTLY: execute every phase of the loaded SKILL.md in order with no shortcuts, no phase-skipping, and no premature synthesis. If a phase cannot complete,
|
|
103481
|
+
- FOLLOW THE SKILL EXACTLY: execute every phase of the loaded SKILL.md in order with no shortcuts, no phase-skipping, and no premature synthesis. If a required coverage phase cannot complete, apply the skill's coverage gate (retry or verified equivalent alternative). If the gap still cannot be closed, stop and surface the lane failure to the user as BLOCKED; do not produce a degraded review, partial verdict, or final synthesis.
|
|
103444
103482
|
- CHECK OUT THE PR BRANCH LOCALLY before launching explorer lanes: fetch the PR head ref if it is not present, verify the working tree is clean (git status --porcelain) and stash/abort if not, then check out the head branch. Explorers read the working-tree filesystem (Read/Glob/Grep), so without a checkout they read the base branch and produce invalid candidates. Always pass the base..head commit range in explorer delegations.
|
|
103445
|
-
- RUN
|
|
103483
|
+
- RUN ALL BASE LANES: the default PR_REVIEW path always launches the fixed six base check-type lanes from the skill (correctness, security, dependencies/deployment, docs/intent, tests, performance/architecture). Do not collapse, omit, or scale down the base lanes for a small, docs-only, or CI-only PR.
|
|
103484
|
+
- USE ASYNC DISPATCH WITHOUT IDLING: launch the base lanes with one \`dispatch_lanes_async\` call when available, record the \`batch_id\`, then keep doing non-dependent architect work while they run. Poll with \`collect_lane_results\` without \`wait\` (or \`wait: false\`) to process settled lanes and continue independent work between polls; use \`wait: true\` only as the final join when no independent work remains.
|
|
103485
|
+
- RUN THE TRIGGERED MICRO-LANES: after the base explorer lanes settle, inspect the context pack risk triggers and launch every matching Swarm plugin micro-lane from the skill's risk-trigger map (launch only triggered lanes, never irrelevant ones). Do not skip micro-lanes that match the diff; when multiple micro-lanes are needed, dispatch them with \`dispatch_lanes_async\` and the same non-idling incremental collection pattern.
|
|
103446
103486
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional reviewer focus, without weakening the validation ladder above.
|
|
103447
103487
|
|
|
103448
103488
|
### MODE: PR_FEEDBACK
|
|
@@ -103457,6 +103497,7 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
103457
103497
|
- CHECK OUT THE PR BRANCH LOCALLY before verifying feedback or making fixes: fetch the PR head ref if absent, verify the working tree is clean (git status --porcelain) and stash/abort if not, then check out the head branch. Feedback verification and fix validation require the PR branch in the working tree.
|
|
103458
103498
|
- Do NOT run a fresh broad PR review — inspect adjacent code only as needed to verify reachability, dependencies, shared root causes, regression risk, or sibling changes for a confirmed item.
|
|
103459
103499
|
- Treat every review comment, CI failure, bot summary, and pasted note as a CLAIM until source evidence proves it; classify each ledger item (CONFIRMED, DISPROVED, PRE_EXISTING, or NEEDS_USER_DECISION) and never silently drop, defer, or mark items out of scope.
|
|
103500
|
+
- For async verification lanes, record the \`batch_id\`, keep doing ledger-safe non-dependent architect work, poll with \`collect_lane_results\` without \`wait\`, process settled lanes immediately, and use \`wait: true\` only when no independent work remains.
|
|
103460
103501
|
- Patch only confirmed items plus the tests/docs they require; report closure status for every ledger item including disproved ones.
|
|
103461
103502
|
- Do NOT resolve or mark GitHub review threads resolved unless the user explicitly instructs it.
|
|
103462
103503
|
- Honor any free-text instructions that follow the closing bracket of the signal as additional scope, without dropping any ledger item.
|
|
@@ -116535,6 +116576,7 @@ var SUPPORTED_EXTENSIONS = new Set(LANGUAGE_REGISTRY.getAll().filter((p) => !p.p
|
|
|
116535
116576
|
var DEFAULT_WALK_FILE_CAP = 1e4;
|
|
116536
116577
|
var DEFAULT_WALK_BUDGET_MS = 5000;
|
|
116537
116578
|
var ASYNC_WALK_YIELD_INTERVAL = 200;
|
|
116579
|
+
var MAX_DIAGNOSTIC_ENTRIES = 200;
|
|
116538
116580
|
var EXTENSION_TO_LANGUAGE = {};
|
|
116539
116581
|
for (const profile of LANGUAGE_REGISTRY.getAll()) {
|
|
116540
116582
|
if (profile.parserOnly)
|
|
@@ -116665,6 +116707,64 @@ function isRefusedWorkspaceRoot(target) {
|
|
|
116665
116707
|
}
|
|
116666
116708
|
return refused.has(resolved);
|
|
116667
116709
|
}
|
|
116710
|
+
function createEmptyDiagnostics() {
|
|
116711
|
+
return {
|
|
116712
|
+
extractionFailures: [],
|
|
116713
|
+
unresolvedImports: [],
|
|
116714
|
+
oversizedFiles: [],
|
|
116715
|
+
unsupportedFiles: [],
|
|
116716
|
+
binaryFiles: [],
|
|
116717
|
+
unreadableFiles: [],
|
|
116718
|
+
lowConfidenceEdgeCount: 0
|
|
116719
|
+
};
|
|
116720
|
+
}
|
|
116721
|
+
function diagnosticsHaveEntries(diagnostics) {
|
|
116722
|
+
return (diagnostics.extractionFailures?.length ?? 0) > 0 || (diagnostics.unresolvedImports?.length ?? 0) > 0 || (diagnostics.oversizedFiles?.length ?? 0) > 0 || (diagnostics.unsupportedFiles?.length ?? 0) > 0 || (diagnostics.binaryFiles?.length ?? 0) > 0 || (diagnostics.unreadableFiles?.length ?? 0) > 0 || (diagnostics.lowConfidenceEdgeCount ?? 0) > 0;
|
|
116723
|
+
}
|
|
116724
|
+
function pushCapped(target, value) {
|
|
116725
|
+
if (target.length < MAX_DIAGNOSTIC_ENTRIES) {
|
|
116726
|
+
target.push(value);
|
|
116727
|
+
}
|
|
116728
|
+
}
|
|
116729
|
+
function mergeDiagnostics(target, source) {
|
|
116730
|
+
if (!source)
|
|
116731
|
+
return;
|
|
116732
|
+
for (const entry of source.extractionFailures ?? []) {
|
|
116733
|
+
pushCapped(target.extractionFailures, entry);
|
|
116734
|
+
}
|
|
116735
|
+
for (const entry of source.unresolvedImports ?? []) {
|
|
116736
|
+
pushCapped(target.unresolvedImports, entry);
|
|
116737
|
+
}
|
|
116738
|
+
for (const entry of source.oversizedFiles ?? []) {
|
|
116739
|
+
pushCapped(target.oversizedFiles, entry);
|
|
116740
|
+
}
|
|
116741
|
+
for (const entry of source.unsupportedFiles ?? []) {
|
|
116742
|
+
pushCapped(target.unsupportedFiles, entry);
|
|
116743
|
+
}
|
|
116744
|
+
for (const entry of source.binaryFiles ?? []) {
|
|
116745
|
+
pushCapped(target.binaryFiles, entry);
|
|
116746
|
+
}
|
|
116747
|
+
for (const entry of source.unreadableFiles ?? []) {
|
|
116748
|
+
pushCapped(target.unreadableFiles, entry);
|
|
116749
|
+
}
|
|
116750
|
+
target.lowConfidenceEdgeCount += source.lowConfidenceEdgeCount ?? 0;
|
|
116751
|
+
}
|
|
116752
|
+
function isRelativeImportSpecifier(specifier) {
|
|
116753
|
+
return specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../");
|
|
116754
|
+
}
|
|
116755
|
+
function unresolvedRelativeImportsFor(parsedImports, filePath, absoluteRoot) {
|
|
116756
|
+
const unresolved = [];
|
|
116757
|
+
const moduleName = toModuleName(filePath, absoluteRoot);
|
|
116758
|
+
for (const parsed of parsedImports) {
|
|
116759
|
+
if (!isRelativeImportSpecifier(parsed.specifier))
|
|
116760
|
+
continue;
|
|
116761
|
+
const resolvedTarget = resolveModuleSpecifier(absoluteRoot, filePath, parsed.specifier);
|
|
116762
|
+
if (resolvedTarget === null) {
|
|
116763
|
+
unresolved.push({ file: moduleName, specifier: parsed.specifier });
|
|
116764
|
+
}
|
|
116765
|
+
}
|
|
116766
|
+
return unresolved;
|
|
116767
|
+
}
|
|
116668
116768
|
var REGEX_ALLOWED_AFTER = new Set("(,=:[!&|?{};*+-~^<>%".split(""));
|
|
116669
116769
|
function stripComments2(content) {
|
|
116670
116770
|
let out = "";
|
|
@@ -116857,6 +116957,28 @@ function computeUsedSymbols(strippedContent, bindings) {
|
|
|
116857
116957
|
}
|
|
116858
116958
|
return [...used].sort((a, b) => a.localeCompare(b));
|
|
116859
116959
|
}
|
|
116960
|
+
function usedSymbolsForImport(parsed, strippedContent) {
|
|
116961
|
+
if (parsed.importType === "namespace" || parsed.importType === "sideeffect" || parsed.importType === "require") {
|
|
116962
|
+
return;
|
|
116963
|
+
}
|
|
116964
|
+
if (parsed.reExport) {
|
|
116965
|
+
return [...new Set(parsed.bindings.map((b) => b.imported))].sort((a, b) => a.localeCompare(b));
|
|
116966
|
+
}
|
|
116967
|
+
return computeUsedSymbols(strippedContent, parsed.bindings);
|
|
116968
|
+
}
|
|
116969
|
+
function collectExports(symbols2) {
|
|
116970
|
+
const exported = symbols2.filter((s) => s.exported);
|
|
116971
|
+
const exports = exported.map((s) => s.signature === `default ${s.name}` ? "default" : s.name);
|
|
116972
|
+
const exportLines = {};
|
|
116973
|
+
for (let i = 0;i < exported.length; i++) {
|
|
116974
|
+
const s = exported[i];
|
|
116975
|
+
const name = exports[i];
|
|
116976
|
+
if (typeof s.line === "number" && Number.isFinite(s.line) && exportLines[name] === undefined) {
|
|
116977
|
+
exportLines[name] = s.line;
|
|
116978
|
+
}
|
|
116979
|
+
}
|
|
116980
|
+
return { exports, exportLines };
|
|
116981
|
+
}
|
|
116860
116982
|
function parseImportedSymbols(matchedString, importType) {
|
|
116861
116983
|
if (importType === "namespace")
|
|
116862
116984
|
return ["*"];
|
|
@@ -116983,44 +117105,126 @@ function isBinaryContent(content) {
|
|
|
116983
117105
|
}
|
|
116984
117106
|
return false;
|
|
116985
117107
|
}
|
|
117108
|
+
function scanFile(filePath, absoluteRoot, maxFileSize) {
|
|
117109
|
+
let content;
|
|
117110
|
+
let fileStats;
|
|
117111
|
+
try {
|
|
117112
|
+
fileStats = fsSync8.statSync(filePath);
|
|
117113
|
+
if (fileStats.size > maxFileSize) {
|
|
117114
|
+
return { node: null, edges: [] };
|
|
117115
|
+
}
|
|
117116
|
+
content = fsSync8.readFileSync(filePath, "utf-8");
|
|
117117
|
+
} catch {
|
|
117118
|
+
return { node: null, edges: [] };
|
|
117119
|
+
}
|
|
117120
|
+
if (isBinaryContent(content)) {
|
|
117121
|
+
return { node: null, edges: [] };
|
|
117122
|
+
}
|
|
117123
|
+
const ext = path134.extname(filePath).toLowerCase();
|
|
117124
|
+
let exports = [];
|
|
117125
|
+
let exportLines = {};
|
|
117126
|
+
try {
|
|
117127
|
+
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
|
|
117128
|
+
const relativePath = path134.relative(absoluteRoot, filePath);
|
|
117129
|
+
({ exports, exportLines } = collectExports(_internals82.extractTSSymbols(relativePath, absoluteRoot)));
|
|
117130
|
+
} else if (ext === ".py") {
|
|
117131
|
+
const relativePath = path134.relative(absoluteRoot, filePath);
|
|
117132
|
+
({ exports, exportLines } = collectExports(_internals82.extractPythonSymbols(relativePath, absoluteRoot)));
|
|
117133
|
+
}
|
|
117134
|
+
const parsedImports = _internals82.parseFileImports(content);
|
|
117135
|
+
const strippedForUsage = parsedImports.length > 0 ? _internals82.stripComments(content) : "";
|
|
117136
|
+
const moduleName = toModuleName(filePath, absoluteRoot);
|
|
117137
|
+
const node = {
|
|
117138
|
+
filePath,
|
|
117139
|
+
moduleName,
|
|
117140
|
+
exports,
|
|
117141
|
+
...Object.keys(exportLines).length > 0 ? { exportLines } : {},
|
|
117142
|
+
imports: parsedImports.map((p) => p.specifier),
|
|
117143
|
+
language: getLanguage(filePath),
|
|
117144
|
+
mtime: fileStats.mtime.toISOString(),
|
|
117145
|
+
ontology: _internals82.extractFileOntology({
|
|
117146
|
+
moduleName,
|
|
117147
|
+
filePath,
|
|
117148
|
+
content,
|
|
117149
|
+
language: getLanguage(filePath),
|
|
117150
|
+
exports,
|
|
117151
|
+
imports: parsedImports.map((p) => p.specifier)
|
|
117152
|
+
})
|
|
117153
|
+
};
|
|
117154
|
+
const edges = [];
|
|
117155
|
+
const sortedImports = [...parsedImports].sort((a, b) => a.specifier.localeCompare(b.specifier));
|
|
117156
|
+
for (const parsed of sortedImports) {
|
|
117157
|
+
const resolvedTarget = resolveModuleSpecifier(absoluteRoot, filePath, parsed.specifier);
|
|
117158
|
+
if (resolvedTarget !== null) {
|
|
117159
|
+
const usedSymbols = usedSymbolsForImport(parsed, strippedForUsage);
|
|
117160
|
+
edges.push({
|
|
117161
|
+
source: filePath,
|
|
117162
|
+
target: resolvedTarget,
|
|
117163
|
+
importSpecifier: parsed.specifier,
|
|
117164
|
+
importType: parsed.importType,
|
|
117165
|
+
importedSymbols: parsed.importedSymbols,
|
|
117166
|
+
...usedSymbols !== undefined ? { usedSymbols } : {}
|
|
117167
|
+
});
|
|
117168
|
+
}
|
|
117169
|
+
}
|
|
117170
|
+
return { node, edges };
|
|
117171
|
+
} catch {
|
|
117172
|
+
return { node: null, edges: [] };
|
|
117173
|
+
}
|
|
117174
|
+
}
|
|
116986
117175
|
async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
|
|
116987
117176
|
let content;
|
|
116988
117177
|
let fileStats;
|
|
116989
117178
|
try {
|
|
116990
117179
|
fileStats = fsSync8.statSync(filePath);
|
|
116991
117180
|
if (fileStats.size > maxFileSize) {
|
|
116992
|
-
return {
|
|
117181
|
+
return {
|
|
117182
|
+
node: null,
|
|
117183
|
+
edges: [],
|
|
117184
|
+
symbolEdges: [],
|
|
117185
|
+
diagnostics: { oversizedFiles: [toModuleName(filePath, absoluteRoot)] }
|
|
117186
|
+
};
|
|
116993
117187
|
}
|
|
116994
117188
|
content = fsSync8.readFileSync(filePath, "utf-8");
|
|
116995
117189
|
} catch {
|
|
116996
|
-
return {
|
|
117190
|
+
return {
|
|
117191
|
+
node: null,
|
|
117192
|
+
edges: [],
|
|
117193
|
+
symbolEdges: [],
|
|
117194
|
+
diagnostics: { unreadableFiles: [toModuleName(filePath, absoluteRoot)] }
|
|
117195
|
+
};
|
|
116997
117196
|
}
|
|
116998
117197
|
if (isBinaryContent(content)) {
|
|
116999
|
-
return {
|
|
117198
|
+
return {
|
|
117199
|
+
node: null,
|
|
117200
|
+
edges: [],
|
|
117201
|
+
symbolEdges: [],
|
|
117202
|
+
diagnostics: { binaryFiles: [toModuleName(filePath, absoluteRoot)] }
|
|
117203
|
+
};
|
|
117000
117204
|
}
|
|
117001
117205
|
const grammarId = getLanguage(filePath);
|
|
117002
117206
|
const facts = await _internals82.extractFileSymbols(grammarId, content);
|
|
117003
117207
|
if (facts === null) {
|
|
117004
|
-
const
|
|
117208
|
+
const fallback = scanFile(filePath, absoluteRoot, maxFileSize);
|
|
117209
|
+
let parsedImports = [];
|
|
117210
|
+
try {
|
|
117211
|
+
parsedImports = _internals82.parseFileImports(content);
|
|
117212
|
+
} catch {
|
|
117213
|
+
parsedImports = [];
|
|
117214
|
+
}
|
|
117005
117215
|
return {
|
|
117006
|
-
|
|
117007
|
-
|
|
117008
|
-
|
|
117009
|
-
|
|
117010
|
-
|
|
117011
|
-
|
|
117012
|
-
|
|
117013
|
-
|
|
117014
|
-
|
|
117015
|
-
|
|
117016
|
-
|
|
117017
|
-
|
|
117018
|
-
exports: [],
|
|
117019
|
-
imports: []
|
|
117020
|
-
})
|
|
117021
|
-
},
|
|
117022
|
-
edges: [],
|
|
117023
|
-
symbolEdges: []
|
|
117216
|
+
...fallback,
|
|
117217
|
+
symbolEdges: [],
|
|
117218
|
+
diagnostics: {
|
|
117219
|
+
extractionFailures: [
|
|
117220
|
+
{
|
|
117221
|
+
file: toModuleName(filePath, absoluteRoot),
|
|
117222
|
+
language: grammarId,
|
|
117223
|
+
reason: "symbol_extraction_failed"
|
|
117224
|
+
}
|
|
117225
|
+
],
|
|
117226
|
+
unresolvedImports: unresolvedRelativeImportsFor(parsedImports, filePath, absoluteRoot)
|
|
117227
|
+
}
|
|
117024
117228
|
};
|
|
117025
117229
|
}
|
|
117026
117230
|
const exportedDefs = facts.defs.filter((d) => d.exported);
|
|
@@ -117071,6 +117275,10 @@ async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
|
|
|
117071
117275
|
});
|
|
117072
117276
|
}
|
|
117073
117277
|
}
|
|
117278
|
+
const unresolvedImports = sortedImports.filter((imp) => isRelativeImportSpecifier(imp.specifier)).filter((imp) => resolveModuleSpecifier(absoluteRoot, filePath, imp.specifier) === null).map((imp) => ({
|
|
117279
|
+
file: moduleName,
|
|
117280
|
+
specifier: imp.specifier
|
|
117281
|
+
}));
|
|
117074
117282
|
const symbolEdges = [];
|
|
117075
117283
|
const localToImported = new Map;
|
|
117076
117284
|
for (const imp of facts.imports) {
|
|
@@ -117101,7 +117309,12 @@ async function scanFileAsync(filePath, absoluteRoot, maxFileSize) {
|
|
|
117101
117309
|
toSymbol: mapping.imported
|
|
117102
117310
|
});
|
|
117103
117311
|
}
|
|
117104
|
-
return {
|
|
117312
|
+
return {
|
|
117313
|
+
node,
|
|
117314
|
+
edges,
|
|
117315
|
+
symbolEdges,
|
|
117316
|
+
diagnostics: unresolvedImports.length > 0 ? { unresolvedImports } : undefined
|
|
117317
|
+
};
|
|
117105
117318
|
}
|
|
117106
117319
|
async function buildWorkspaceGraphAsync(workspaceRoot, options) {
|
|
117107
117320
|
validateWorkspace(workspaceRoot);
|
|
@@ -117123,6 +117336,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
|
|
|
117123
117336
|
skippedFiles: 0,
|
|
117124
117337
|
truncated: false
|
|
117125
117338
|
};
|
|
117339
|
+
const diagnostics = createEmptyDiagnostics();
|
|
117126
117340
|
const sourceFiles = await findSourceFilesAsync(absoluteRoot, stats2, {
|
|
117127
117341
|
walkBudgetMs,
|
|
117128
117342
|
maxFiles,
|
|
@@ -117143,6 +117357,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
|
|
|
117143
117357
|
let processedSinceYield = 0;
|
|
117144
117358
|
for (const filePath of sourceFiles) {
|
|
117145
117359
|
const result = await scanFileAsync(filePath, absoluteRoot, maxFileSize);
|
|
117360
|
+
mergeDiagnostics(diagnostics, result.diagnostics);
|
|
117146
117361
|
if (result.node) {
|
|
117147
117362
|
let appended = false;
|
|
117148
117363
|
try {
|
|
@@ -117183,6 +117398,7 @@ async function buildWorkspaceGraphAsync(workspaceRoot, options) {
|
|
|
117183
117398
|
if (allSymbolEdges.length > 0) {
|
|
117184
117399
|
graph.symbolEdges = allSymbolEdges;
|
|
117185
117400
|
}
|
|
117401
|
+
graph.diagnostics = diagnosticsHaveEntries(diagnostics) ? diagnostics : createEmptyDiagnostics();
|
|
117186
117402
|
if (stats2.skippedFiles > 0 || stats2.skippedDirs > 0 || stats2.truncated) {
|
|
117187
117403
|
log(`[repo-graph] Scan stats: ${stats2.filesScanned} files scanned, ` + `${stats2.skippedFiles} files skipped, ${stats2.skippedDirs} dirs skipped` + (stats2.truncated ? ", TRUNCATED" : ""));
|
|
117188
117404
|
}
|
|
@@ -117224,7 +117440,11 @@ import * as fsPromises7 from "node:fs/promises";
|
|
|
117224
117440
|
import * as path138 from "node:path";
|
|
117225
117441
|
|
|
117226
117442
|
// src/tools/repo-graph/query.ts
|
|
117443
|
+
init_path_security();
|
|
117444
|
+
import * as fsSync9 from "node:fs";
|
|
117227
117445
|
import * as path136 from "node:path";
|
|
117446
|
+
var GRAPH_HEALTH_OUTPUT_LIMIT = 50;
|
|
117447
|
+
var MAX_HEALTH_PATH_LENGTH = 500;
|
|
117228
117448
|
var cachedReverseIndex = null;
|
|
117229
117449
|
function normalizeLookupPath(input) {
|
|
117230
117450
|
return normalizeGraphPath(input).replace(/^(?:\.\/)+/, "");
|
|
@@ -117323,6 +117543,139 @@ function isGraphFresh(graph, maxAgeMs = 5 * 60 * 1000) {
|
|
|
117323
117543
|
return false;
|
|
117324
117544
|
return Date.now() - built <= maxAgeMs;
|
|
117325
117545
|
}
|
|
117546
|
+
function isSafeHealthPath(value) {
|
|
117547
|
+
if (typeof value !== "string")
|
|
117548
|
+
return false;
|
|
117549
|
+
if (value.length === 0 || value.length > MAX_HEALTH_PATH_LENGTH)
|
|
117550
|
+
return false;
|
|
117551
|
+
if (containsControlChars(value) || containsPathTraversal(value))
|
|
117552
|
+
return false;
|
|
117553
|
+
if (path136.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value))
|
|
117554
|
+
return false;
|
|
117555
|
+
return true;
|
|
117556
|
+
}
|
|
117557
|
+
function isSafeHealthText(value) {
|
|
117558
|
+
if (typeof value !== "string")
|
|
117559
|
+
return false;
|
|
117560
|
+
if (value.length === 0 || value.length > MAX_HEALTH_PATH_LENGTH)
|
|
117561
|
+
return false;
|
|
117562
|
+
if (containsControlChars(value))
|
|
117563
|
+
return false;
|
|
117564
|
+
return true;
|
|
117565
|
+
}
|
|
117566
|
+
function cap(entries) {
|
|
117567
|
+
return entries.slice(0, GRAPH_HEALTH_OUTPUT_LIMIT);
|
|
117568
|
+
}
|
|
117569
|
+
function sanitizeExtractionFailures(value) {
|
|
117570
|
+
if (!Array.isArray(value))
|
|
117571
|
+
return [];
|
|
117572
|
+
const entries = [];
|
|
117573
|
+
for (const raw of value) {
|
|
117574
|
+
if (!raw || typeof raw !== "object")
|
|
117575
|
+
continue;
|
|
117576
|
+
const entry = raw;
|
|
117577
|
+
if (isSafeHealthPath(entry.file) && isSafeHealthText(entry.language) && isSafeHealthText(entry.reason)) {
|
|
117578
|
+
entries.push({
|
|
117579
|
+
file: entry.file,
|
|
117580
|
+
language: entry.language,
|
|
117581
|
+
reason: entry.reason
|
|
117582
|
+
});
|
|
117583
|
+
}
|
|
117584
|
+
}
|
|
117585
|
+
return cap(entries);
|
|
117586
|
+
}
|
|
117587
|
+
function sanitizeUnresolvedImports(value) {
|
|
117588
|
+
if (!Array.isArray(value))
|
|
117589
|
+
return [];
|
|
117590
|
+
const entries = [];
|
|
117591
|
+
for (const raw of value) {
|
|
117592
|
+
if (!raw || typeof raw !== "object")
|
|
117593
|
+
continue;
|
|
117594
|
+
const entry = raw;
|
|
117595
|
+
if (isSafeHealthPath(entry.file) && isSafeHealthText(entry.specifier)) {
|
|
117596
|
+
entries.push({ file: entry.file, specifier: entry.specifier });
|
|
117597
|
+
}
|
|
117598
|
+
}
|
|
117599
|
+
return cap(entries);
|
|
117600
|
+
}
|
|
117601
|
+
function sanitizePathList(value) {
|
|
117602
|
+
if (!Array.isArray(value))
|
|
117603
|
+
return [];
|
|
117604
|
+
return cap(value.filter(isSafeHealthPath));
|
|
117605
|
+
}
|
|
117606
|
+
function getStaleFiles(graph, workspaceRoot) {
|
|
117607
|
+
const built = Date.parse(graph.metadata.generatedAt);
|
|
117608
|
+
if (!Number.isFinite(built))
|
|
117609
|
+
return [];
|
|
117610
|
+
const root = workspaceRoot ?? graph.workspaceRoot;
|
|
117611
|
+
const stale = [];
|
|
117612
|
+
for (const node of Object.values(graph.nodes)) {
|
|
117613
|
+
const moduleName = normalizeGraphPath(node.moduleName);
|
|
117614
|
+
if (!isSafeHealthPath(moduleName))
|
|
117615
|
+
continue;
|
|
117616
|
+
const filePath = path136.join(root, moduleName);
|
|
117617
|
+
try {
|
|
117618
|
+
if (fsSync9.statSync(filePath).mtimeMs > built) {
|
|
117619
|
+
stale.push(moduleName);
|
|
117620
|
+
if (stale.length >= GRAPH_HEALTH_OUTPUT_LIMIT)
|
|
117621
|
+
break;
|
|
117622
|
+
}
|
|
117623
|
+
} catch {}
|
|
117624
|
+
}
|
|
117625
|
+
return stale;
|
|
117626
|
+
}
|
|
117627
|
+
function getGraphHealth(graph, workspaceRoot) {
|
|
117628
|
+
if (!graph) {
|
|
117629
|
+
return {
|
|
117630
|
+
schemaVersion: null,
|
|
117631
|
+
fresh: false,
|
|
117632
|
+
staleFiles: [],
|
|
117633
|
+
extractionFailures: [],
|
|
117634
|
+
unresolvedImports: [],
|
|
117635
|
+
oversizedFiles: [],
|
|
117636
|
+
unsupportedFiles: [],
|
|
117637
|
+
binaryFiles: [],
|
|
117638
|
+
unreadableFiles: [],
|
|
117639
|
+
lowConfidenceEdgeCount: 0,
|
|
117640
|
+
notes: [
|
|
117641
|
+
'No repo graph found at .swarm/repo-graph.json. Run repo_map with action="build" first.'
|
|
117642
|
+
]
|
|
117643
|
+
};
|
|
117644
|
+
}
|
|
117645
|
+
const diagnostics = graph.diagnostics;
|
|
117646
|
+
const fresh = isGraphFresh(graph);
|
|
117647
|
+
const staleFiles = getStaleFiles(graph, workspaceRoot);
|
|
117648
|
+
const notes = [];
|
|
117649
|
+
if (!fresh || staleFiles.length > 0) {
|
|
117650
|
+
notes.push('Graph is stale. Run repo_map with action="build" to refresh.');
|
|
117651
|
+
}
|
|
117652
|
+
if (!diagnostics) {
|
|
117653
|
+
notes.push('Graph has no recorded diagnostics. Rebuild with repo_map action="build" to collect health details.');
|
|
117654
|
+
}
|
|
117655
|
+
const binaryFiles = sanitizePathList(diagnostics?.binaryFiles);
|
|
117656
|
+
const binaryCount = binaryFiles.length;
|
|
117657
|
+
if (binaryCount > 0) {
|
|
117658
|
+
notes.push(`${binaryCount} binary files skipped during last build.`);
|
|
117659
|
+
}
|
|
117660
|
+
const unreadableFiles = sanitizePathList(diagnostics?.unreadableFiles);
|
|
117661
|
+
const unreadableCount = unreadableFiles.length;
|
|
117662
|
+
if (unreadableCount > 0) {
|
|
117663
|
+
notes.push(`${unreadableCount} unreadable files skipped during last build.`);
|
|
117664
|
+
}
|
|
117665
|
+
return {
|
|
117666
|
+
schemaVersion: graph.schema_version,
|
|
117667
|
+
fresh,
|
|
117668
|
+
staleFiles,
|
|
117669
|
+
extractionFailures: sanitizeExtractionFailures(diagnostics?.extractionFailures),
|
|
117670
|
+
unresolvedImports: sanitizeUnresolvedImports(diagnostics?.unresolvedImports),
|
|
117671
|
+
oversizedFiles: sanitizePathList(diagnostics?.oversizedFiles),
|
|
117672
|
+
unsupportedFiles: sanitizePathList(diagnostics?.unsupportedFiles),
|
|
117673
|
+
binaryFiles,
|
|
117674
|
+
unreadableFiles,
|
|
117675
|
+
lowConfidenceEdgeCount: typeof diagnostics?.lowConfidenceEdgeCount === "number" && Number.isFinite(diagnostics.lowConfidenceEdgeCount) && diagnostics.lowConfidenceEdgeCount > 0 ? Math.floor(diagnostics.lowConfidenceEdgeCount) : 0,
|
|
117676
|
+
notes
|
|
117677
|
+
};
|
|
117678
|
+
}
|
|
117326
117679
|
function getImporters(graph, filePath) {
|
|
117327
117680
|
const node = getGraphNode(graph, filePath);
|
|
117328
117681
|
if (!node)
|
|
@@ -117911,7 +118264,7 @@ function buildOntologyPreflightPacket(graph, filePaths = [], options = {}) {
|
|
|
117911
118264
|
init_utils2();
|
|
117912
118265
|
init_logger();
|
|
117913
118266
|
init_path_security();
|
|
117914
|
-
import { constants as constants5, existsSync as existsSync77, readFileSync as readFileSync53, statSync as
|
|
118267
|
+
import { constants as constants5, existsSync as existsSync77, readFileSync as readFileSync53, statSync as statSync28 } from "node:fs";
|
|
117915
118268
|
import * as fsPromises6 from "node:fs/promises";
|
|
117916
118269
|
import * as path137 from "node:path";
|
|
117917
118270
|
var WINDOWS_RENAME_MAX_RETRIES2 = 5;
|
|
@@ -118036,7 +118389,7 @@ function loadGraphSync(workspace) {
|
|
|
118036
118389
|
const graphPath = getGraphPath(workspace);
|
|
118037
118390
|
if (!existsSync77(graphPath))
|
|
118038
118391
|
return null;
|
|
118039
|
-
const stats2 =
|
|
118392
|
+
const stats2 = statSync28(graphPath);
|
|
118040
118393
|
const content = readFileSync53(graphPath, "utf-8");
|
|
118041
118394
|
if (content.includes("\x00") || content.includes("�")) {
|
|
118042
118395
|
throw Object.assign(new Error("repo-graph.json contains null bytes or invalid encoding"), { code: "CORRUPTION" });
|
|
@@ -123062,9 +123415,9 @@ function parseDelegateDirectiveBlock(text) {
|
|
|
123062
123415
|
}
|
|
123063
123416
|
async function injectForDelegate(params) {
|
|
123064
123417
|
const { directory, agent, taskTitle, sessionId, config: config3 } = params;
|
|
123065
|
-
const
|
|
123418
|
+
const cap2 = config3.delegate_max_inject_count ?? 8;
|
|
123066
123419
|
const expectedTools = params.expectedTools && params.expectedTools.length > 0 ? params.expectedTools : defaultExpectedToolsForAgent(agent);
|
|
123067
|
-
if (
|
|
123420
|
+
if (cap2 <= 0)
|
|
123068
123421
|
return { entries: [], trace_id: "" };
|
|
123069
123422
|
const role = stripKnownSwarmPrefix(agent).toLowerCase();
|
|
123070
123423
|
const firstTool = expectedTools.length > 0 ? expectedTools[0] : undefined;
|
|
@@ -123088,11 +123441,11 @@ async function injectForDelegate(params) {
|
|
|
123088
123441
|
tier: "all",
|
|
123089
123442
|
applyScopeFilter: true,
|
|
123090
123443
|
applyRoleScope: false,
|
|
123091
|
-
maxResults: Math.max(40,
|
|
123444
|
+
maxResults: Math.max(40, cap2 * 4),
|
|
123092
123445
|
emitEvent: false
|
|
123093
123446
|
});
|
|
123094
123447
|
const scoped = search.results.filter((e) => matchesDelegateScope(e, role, expectedTools));
|
|
123095
|
-
const capped = scoped.slice(0,
|
|
123448
|
+
const capped = scoped.slice(0, cap2);
|
|
123096
123449
|
if (capped.length > 0) {
|
|
123097
123450
|
const ranks = {};
|
|
123098
123451
|
const scores = {};
|
|
@@ -129928,7 +130281,7 @@ ${body}`);
|
|
|
129928
130281
|
init_zod();
|
|
129929
130282
|
init_task_file();
|
|
129930
130283
|
import { appendFileSync as appendFileSync18, existsSync as existsSync95, mkdirSync as mkdirSync42, readFileSync as readFileSync64 } from "node:fs";
|
|
129931
|
-
import { join as
|
|
130284
|
+
import { join as join132 } from "node:path";
|
|
129932
130285
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
129933
130286
|
var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
|
|
129934
130287
|
var COUNCIL_GATE_NAME = "council";
|
|
@@ -129966,7 +130319,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
129966
130319
|
if (!VALID_TASK_ID.test(synthesis.taskId)) {
|
|
129967
130320
|
throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
|
|
129968
130321
|
}
|
|
129969
|
-
const dir =
|
|
130322
|
+
const dir = join132(workingDir, EVIDENCE_DIR2);
|
|
129970
130323
|
mkdirSync42(dir, { recursive: true });
|
|
129971
130324
|
const filePath = taskEvidencePath(workingDir, synthesis.taskId);
|
|
129972
130325
|
await _internals96.withTaskEvidenceLock(workingDir, synthesis.taskId, COUNCIL_AGENT_ID, async () => {
|
|
@@ -130002,7 +130355,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
130002
130355
|
await atomicWriteFile(filePath, JSON.stringify(updated, null, 2));
|
|
130003
130356
|
});
|
|
130004
130357
|
try {
|
|
130005
|
-
const councilDir =
|
|
130358
|
+
const councilDir = join132(workingDir, ".swarm", "council");
|
|
130006
130359
|
mkdirSync42(councilDir, { recursive: true });
|
|
130007
130360
|
const auditLine = JSON.stringify({
|
|
130008
130361
|
round: synthesis.roundNumber,
|
|
@@ -130010,7 +130363,7 @@ async function writeCouncilEvidence(workingDir, synthesis) {
|
|
|
130010
130363
|
timestamp: synthesis.timestamp,
|
|
130011
130364
|
vetoedBy: synthesis.vetoedBy
|
|
130012
130365
|
});
|
|
130013
|
-
appendFileSync18(
|
|
130366
|
+
appendFileSync18(join132(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
|
|
130014
130367
|
`);
|
|
130015
130368
|
} catch (auditError) {
|
|
130016
130369
|
console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
|
|
@@ -130367,7 +130720,7 @@ function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFi
|
|
|
130367
130720
|
init_zod();
|
|
130368
130721
|
init_task_file();
|
|
130369
130722
|
import { existsSync as existsSync96, mkdirSync as mkdirSync43, readFileSync as readFileSync65 } from "node:fs";
|
|
130370
|
-
import { join as
|
|
130723
|
+
import { join as join133 } from "node:path";
|
|
130371
130724
|
var COUNCIL_DIR = ".swarm/council";
|
|
130372
130725
|
var CouncilCriteriaSchema = exports_external.object({
|
|
130373
130726
|
taskId: exports_external.string(),
|
|
@@ -130379,17 +130732,17 @@ var CouncilCriteriaSchema = exports_external.object({
|
|
|
130379
130732
|
declaredAt: exports_external.string()
|
|
130380
130733
|
});
|
|
130381
130734
|
async function writeCriteria(workingDir, taskId, criteria) {
|
|
130382
|
-
const dir =
|
|
130735
|
+
const dir = join133(workingDir, COUNCIL_DIR);
|
|
130383
130736
|
mkdirSync43(dir, { recursive: true });
|
|
130384
130737
|
const payload = {
|
|
130385
130738
|
taskId,
|
|
130386
130739
|
criteria,
|
|
130387
130740
|
declaredAt: new Date().toISOString()
|
|
130388
130741
|
};
|
|
130389
|
-
await atomicWriteFile(
|
|
130742
|
+
await atomicWriteFile(join133(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
|
|
130390
130743
|
}
|
|
130391
130744
|
function readCriteria(workingDir, taskId) {
|
|
130392
|
-
const filePath =
|
|
130745
|
+
const filePath = join133(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
|
|
130393
130746
|
if (!existsSync96(filePath))
|
|
130394
130747
|
return null;
|
|
130395
130748
|
try {
|
|
@@ -149604,7 +149957,8 @@ var VALID_ACTIONS = [
|
|
|
149604
149957
|
"preflight_packet",
|
|
149605
149958
|
"callers",
|
|
149606
149959
|
"dead_exports",
|
|
149607
|
-
"context_pack"
|
|
149960
|
+
"context_pack",
|
|
149961
|
+
"graph_health"
|
|
149608
149962
|
];
|
|
149609
149963
|
var MAX_FILE_PATH_LENGTH3 = 500;
|
|
149610
149964
|
var MAX_SYMBOL_LENGTH2 = 256;
|
|
@@ -149668,7 +150022,7 @@ async function loadOrError(directory, action) {
|
|
|
149668
150022
|
}
|
|
149669
150023
|
}
|
|
149670
150024
|
var repo_map = createSwarmTool({
|
|
149671
|
-
description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning), ' + '"callers" (files that reference an exported symbol, call-site granularity; needs file+symbol), ' + '"dead_exports" (advisory: exported symbols with no detected in-repo reference; results are review candidates, not delete directives), ' + '"context_pack" (token-budgeted slice of source spans for a target symbol — definition + transitive callers/callees; advisory/conservative; needs file+symbol; uses max_depth for traversal depth, top_n for span cap). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers. " + 'Note: "callers"/"dead_exports"/"context_pack" use conservative regex analysis (TS/JS/Python) and cannot see ' + 'dynamic dispatch or namespace/barrel re-export usage; "dead_exports" results are review candidates, not delete directives.',
|
|
150025
|
+
description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning), ' + '"callers" (files that reference an exported symbol, call-site granularity; needs file+symbol), ' + '"dead_exports" (advisory: exported symbols with no detected in-repo reference; results are review candidates, not delete directives), ' + '"context_pack" (token-budgeted slice of source spans for a target symbol — definition + transitive callers/callees; advisory/conservative; needs file+symbol; uses max_depth for traversal depth, top_n for span cap), ' + '"graph_health" (freshness and bounded extraction diagnostics; no file required). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers. " + 'Note: "callers"/"dead_exports"/"context_pack" use conservative regex analysis (TS/JS/Python) and cannot see ' + 'dynamic dispatch or namespace/barrel re-export usage; "dead_exports" results are review candidates, not delete directives.',
|
|
149672
150026
|
args: {
|
|
149673
150027
|
action: exports_external.enum([
|
|
149674
150028
|
"build",
|
|
@@ -149682,8 +150036,9 @@ var repo_map = createSwarmTool({
|
|
|
149682
150036
|
"preflight_packet",
|
|
149683
150037
|
"callers",
|
|
149684
150038
|
"dead_exports",
|
|
149685
|
-
"context_pack"
|
|
149686
|
-
|
|
150039
|
+
"context_pack",
|
|
150040
|
+
"graph_health"
|
|
150041
|
+
]).describe('Query action: "build" | "importers" | "dependencies" | "blast_radius" | "localization" | "key_files" | "ontology" | "package_boundaries" | "preflight_packet" | "callers" | "dead_exports" | "context_pack" | "graph_health"'),
|
|
149687
150042
|
file: exports_external.string().optional().describe("Target file (workspace-relative or absolute). Required for importers/dependencies/localization/ontology. Optional for preflight_packet."),
|
|
149688
150043
|
files: exports_external.array(exports_external.string()).optional().describe("Multiple target files for blast_radius/preflight_packet. If omitted, falls back to `file`."),
|
|
149689
150044
|
symbol: exports_external.string().optional().describe('Exported symbol name. Restricts consumers on action="importers"; required for action="callers"/"context_pack".'),
|
|
@@ -149718,6 +150073,15 @@ var repo_map = createSwarmTool({
|
|
|
149718
150073
|
return err(action, `build failed: ${message}`);
|
|
149719
150074
|
}
|
|
149720
150075
|
}
|
|
150076
|
+
if (action === "graph_health") {
|
|
150077
|
+
try {
|
|
150078
|
+
const graph2 = await loadGraph(directory);
|
|
150079
|
+
return ok(action, { ...getGraphHealth(graph2, directory) });
|
|
150080
|
+
} catch (e) {
|
|
150081
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
150082
|
+
return err(action, `failed to load repo graph: ${message}`);
|
|
150083
|
+
}
|
|
150084
|
+
}
|
|
149721
150085
|
const loaded = await loadOrError(directory, action);
|
|
149722
150086
|
if (!loaded.ok)
|
|
149723
150087
|
return loaded.response;
|
|
@@ -153071,7 +153435,7 @@ init_skill_generator();
|
|
|
153071
153435
|
init_create_tool();
|
|
153072
153436
|
import { existsSync as existsSync119 } from "node:fs";
|
|
153073
153437
|
import { readdir as readdir12, readFile as readFile33 } from "node:fs/promises";
|
|
153074
|
-
import { join as
|
|
153438
|
+
import { join as join167 } from "node:path";
|
|
153075
153439
|
var run_stale_reconciliation = createSwarmTool({
|
|
153076
153440
|
description: "Reconcile skills against the knowledge store. clear=false: mark skills stale when source knowledge is archived or deleted. clear=true: clear stale.marker on affected active skills (proposal files under .swarm/skills/proposals are scanned but not modified — they are drafts, not yet active skills).",
|
|
153077
153441
|
args: {
|
|
@@ -153098,8 +153462,8 @@ var run_stale_reconciliation = createSwarmTool({
|
|
|
153098
153462
|
} catch {}
|
|
153099
153463
|
const skillEntries = [];
|
|
153100
153464
|
for (const dir of [
|
|
153101
|
-
|
|
153102
|
-
|
|
153465
|
+
join167(directory, ".opencode", "skills", "generated"),
|
|
153466
|
+
join167(directory, ".swarm", "skills", "proposals")
|
|
153103
153467
|
]) {
|
|
153104
153468
|
if (!_internals125.existsSync(dir))
|
|
153105
153469
|
continue;
|
|
@@ -153108,14 +153472,14 @@ var run_stale_reconciliation = createSwarmTool({
|
|
|
153108
153472
|
if (entry.isDirectory()) {
|
|
153109
153473
|
skillEntries.push({
|
|
153110
153474
|
slug: entry.name,
|
|
153111
|
-
path:
|
|
153475
|
+
path: join167(dir, entry.name),
|
|
153112
153476
|
isProposal: false
|
|
153113
153477
|
});
|
|
153114
153478
|
} else if (entry.name.endsWith(".md")) {
|
|
153115
153479
|
const slug = entry.name.replace(/\.md$/, "");
|
|
153116
153480
|
skillEntries.push({
|
|
153117
153481
|
slug,
|
|
153118
|
-
path:
|
|
153482
|
+
path: join167(dir, entry.name),
|
|
153119
153483
|
isProposal: true
|
|
153120
153484
|
});
|
|
153121
153485
|
}
|
|
@@ -153123,7 +153487,7 @@ var run_stale_reconciliation = createSwarmTool({
|
|
|
153123
153487
|
}
|
|
153124
153488
|
const results = [];
|
|
153125
153489
|
for (const { slug, path: path210, isProposal } of skillEntries) {
|
|
153126
|
-
const skillMdPath = isProposal ? path210 :
|
|
153490
|
+
const skillMdPath = isProposal ? path210 : join167(path210, "SKILL.md");
|
|
153127
153491
|
if (!_internals125.existsSync(skillMdPath))
|
|
153128
153492
|
continue;
|
|
153129
153493
|
const content = await _internals125.readFile(skillMdPath, "utf-8");
|
|
@@ -153136,7 +153500,7 @@ var run_stale_reconciliation = createSwarmTool({
|
|
|
153136
153500
|
continue;
|
|
153137
153501
|
if (args2.clear) {
|
|
153138
153502
|
if (!isProposal) {
|
|
153139
|
-
const markerPath =
|
|
153503
|
+
const markerPath = join167(path210, "stale.marker");
|
|
153140
153504
|
if (_internals125.existsSync(markerPath)) {
|
|
153141
153505
|
try {
|
|
153142
153506
|
await _internals125.clearSkillStale(path210);
|
|
@@ -158349,14 +158713,6 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
158349
158713
|
prEventCleanup?.();
|
|
158350
158714
|
};
|
|
158351
158715
|
process.on("exit", cleanupAutomation);
|
|
158352
|
-
process.once("SIGINT", () => {
|
|
158353
|
-
cleanupAutomation();
|
|
158354
|
-
process.exit(130);
|
|
158355
|
-
});
|
|
158356
|
-
process.once("SIGTERM", () => {
|
|
158357
|
-
cleanupAutomation();
|
|
158358
|
-
process.exit(143);
|
|
158359
|
-
});
|
|
158360
158716
|
if (shouldRunOnStartup(automationConfig)) {
|
|
158361
158717
|
const enableAutofix = automationConfig.capabilities?.config_doctor_autofix === true;
|
|
158362
158718
|
Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor)).then(({ runConfigDoctorWithFixes: runConfigDoctorWithFixes2 }) => {
|
|
@@ -158372,9 +158728,19 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
158372
158728
|
try {
|
|
158373
158729
|
const autoFixableCount = doctorResult.result.findings.filter((f) => f.autoFixable).length;
|
|
158374
158730
|
if (!enableAutofix && autoFixableCount > 0) {
|
|
158375
|
-
|
|
158731
|
+
const msg = `[opencode-swarm] Config Doctor found ${autoFixableCount} auto-fixable issue(s). Run /swarm config doctor --fix to apply.`;
|
|
158732
|
+
if (!config3.quiet) {
|
|
158733
|
+
console.warn(msg);
|
|
158734
|
+
} else {
|
|
158735
|
+
addDeferredWarning(msg);
|
|
158736
|
+
}
|
|
158376
158737
|
} else if (enableAutofix && doctorResult.appliedFixes.length > 0) {
|
|
158377
|
-
|
|
158738
|
+
const msg = `[opencode-swarm] Config Doctor applied ${doctorResult.appliedFixes.length} fix(es) automatically.`;
|
|
158739
|
+
if (!config3.quiet) {
|
|
158740
|
+
console.warn(msg);
|
|
158741
|
+
} else {
|
|
158742
|
+
addDeferredWarning(msg);
|
|
158743
|
+
}
|
|
158378
158744
|
}
|
|
158379
158745
|
} catch {}
|
|
158380
158746
|
}
|
|
@@ -158917,7 +159283,9 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
158917
159283
|
argsRecord.prompt = `SKILLS: none
|
|
158918
159284
|
|
|
158919
159285
|
${promptRaw}`;
|
|
158920
|
-
|
|
159286
|
+
if (!config3.quiet) {
|
|
159287
|
+
console.warn("[skill-propagation-gate] No skills above threshold 0.5 — injected SKILLS: none");
|
|
159288
|
+
}
|
|
158921
159289
|
} else {
|
|
158922
159290
|
const topSkills = qualified.slice(0, 5);
|
|
158923
159291
|
const skillPaths = topSkills.map((s) => {
|
|
@@ -158935,7 +159303,9 @@ ${promptRaw}`;
|
|
|
158935
159303
|
${promptRaw}`;
|
|
158936
159304
|
argsRecord.prompt = newPrompt;
|
|
158937
159305
|
const skillNames = topSkills.map((s) => `${path225.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
|
|
158938
|
-
|
|
159306
|
+
if (!config3.quiet) {
|
|
159307
|
+
console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
|
|
159308
|
+
}
|
|
158939
159309
|
for (const skill of topSkills) {
|
|
158940
159310
|
try {
|
|
158941
159311
|
appendSkillUsageEntry(ctx.directory, {
|