opencode-swarm 7.78.6 → 7.78.8
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/swarm-pr-feedback/SKILL.md +111 -0
- package/README.md +1 -1
- package/dist/cli/index.js +21 -17
- package/dist/commands/conflict-registry.d.ts +9 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/index.js +42 -46
- package/package.json +1 -1
|
@@ -142,6 +142,42 @@ Build a complete feedback ledger before editing. Include every available source:
|
|
|
142
142
|
If a source is unavailable, record that limitation. Do not treat missing access as
|
|
143
143
|
evidence that no feedback exists.
|
|
144
144
|
|
|
145
|
+
### CI matrix cascade check (do this before fixing)
|
|
146
|
+
|
|
147
|
+
When the PR's `unit` job is a matrix across multiple OSes and downstream jobs
|
|
148
|
+
(`integration`, `smoke`) have `needs: unit`, an OS leg failure blocks the
|
|
149
|
+
entire pipeline. Before triaging, check:
|
|
150
|
+
|
|
151
|
+
1. Are `integration` or `smoke` jobs in `skipped` or `cancelled` state rather
|
|
152
|
+
than `failed`? That signals a unit matrix cascade — the unit job failed
|
|
153
|
+
on one OS leg, blocking the downstream jobs from running on the current
|
|
154
|
+
HEAD.
|
|
155
|
+
2. If a unit OS leg is the blocker, classify the failure:
|
|
156
|
+
- **Code issue** — the test itself fails. Reproduce locally; if the
|
|
157
|
+
test passes locally, the runner is the problem.
|
|
158
|
+
- **Runner performance** — the test step exceeds the configured timeout.
|
|
159
|
+
Run all files in the step locally with per-file timing; if cumulative
|
|
160
|
+
local runtime is <10 min and the runner can't complete in 60+ min, the
|
|
161
|
+
issue is runner performance. Bump the CI timeout as a stopgap and file
|
|
162
|
+
a follow-up issue for parallelization. Do not loop bumping the timeout
|
|
163
|
+
past 90 min without filing the follow-up.
|
|
164
|
+
3. Surface cascade failures to the user explicitly. The downstream jobs'
|
|
165
|
+
results don't exist; the code's coverage of the current HEAD cannot be
|
|
166
|
+
confirmed by CI alone.
|
|
167
|
+
|
|
168
|
+
### PR body claim verification
|
|
169
|
+
|
|
170
|
+
PR body text like "PHASE 2 council APPROVED (5/5, round 2)" or "Final council
|
|
171
|
+
APPROVED" must be backed by an evidence file in `.swarm/evidence/`
|
|
172
|
+
(typically `phase-council.json` or `final-council.json`). Bot-generated PR
|
|
173
|
+
bodies commonly auto-fill these claims without real review. Before accepting
|
|
174
|
+
such a claim as part of triage:
|
|
175
|
+
|
|
176
|
+
1. Check whether the corresponding evidence file exists with `verdict:APPROVED`.
|
|
177
|
+
2. If the claim is unsupported, mark the closure ledger item as
|
|
178
|
+
`NEEDS_MORE_EVIDENCE` rather than `CONFIRMED`. Do not silently drop the
|
|
179
|
+
claim — it indicates the PR body was generated without a real review.
|
|
180
|
+
|
|
145
181
|
## Feedback Ledger
|
|
146
182
|
|
|
147
183
|
Normalize each item before triage:
|
|
@@ -163,6 +199,59 @@ Rules:
|
|
|
163
199
|
`CONFLICT-001` for merge/base drift and `CI-001` for check failures, so PR
|
|
164
200
|
bodies can show exactly how operational blockers were closed.
|
|
165
201
|
|
|
202
|
+
### Mandatory: integrate all PR comments with feedback or findings before validation
|
|
203
|
+
|
|
204
|
+
**Before the Verification step begins, every PR comment that contains feedback
|
|
205
|
+
or findings MUST be integrated into the total feedback ledger as a
|
|
206
|
+
`FB-###` item.** This is a hard requirement, not a best-effort step.
|
|
207
|
+
|
|
208
|
+
What counts as "feedback or findings":
|
|
209
|
+
- A reviewer request for a code change ("please rename this", "add a test for
|
|
210
|
+
X", "this should call `_internals.foo`")
|
|
211
|
+
- A reviewer claim about correctness, security, or style ("this is
|
|
212
|
+
incorrect", "X will leak")
|
|
213
|
+
- A bot reviewer's findings table entries
|
|
214
|
+
- A CI failure with a specific file:line root cause
|
|
215
|
+
- A reviewer question that implies a code change is needed ("why is this
|
|
216
|
+
static?")
|
|
217
|
+
- PR review summaries or aggregate comments
|
|
218
|
+
|
|
219
|
+
What does NOT count (and is therefore not required to be a ledger item):
|
|
220
|
+
- Pure acknowledgements ("LGTM", "looks good")
|
|
221
|
+
- PR-level metadata changes (title, label, milestone)
|
|
222
|
+
- Force-push acknowledgements
|
|
223
|
+
|
|
224
|
+
Rules:
|
|
225
|
+
- **No finding may be addressed outside the ledger.** If you fix something a
|
|
226
|
+
reviewer mentioned, the corresponding `FB-###` item MUST be in the ledger
|
|
227
|
+
before the fix. If you skip the fix, the `FB-###` item MUST be in the
|
|
228
|
+
ledger with a `DISPROVED`, `PRE_EXISTING`, `NEEDS_MORE_EVIDENCE`, or
|
|
229
|
+
`NEEDS_USER_DECISION` status before validation can begin.
|
|
230
|
+
- **Status semantics for unaddressed items:**
|
|
231
|
+
- `CONFIRMED` and `PARTIAL` items must be addressed (fixed or
|
|
232
|
+
disproved) before validation can begin. A `CONFIRMED` item that is
|
|
233
|
+
left unaddressed is a regression against the review.
|
|
234
|
+
- `DISPROVED`, `PRE_EXISTING`, `NEEDS_MORE_EVIDENCE`, and
|
|
235
|
+
`NEEDS_USER_DECISION` items may remain open at validation time, but
|
|
236
|
+
each must be explicitly justified in the closure ledger.
|
|
237
|
+
- **The closure ledger at the end of the run must account for every `FB-###`
|
|
238
|
+
item** with a final status (fixed / disproved / pre-existing / needs user
|
|
239
|
+
decision / needs more evidence).
|
|
240
|
+
- **Comments from the latest bot round take precedence over earlier rounds**
|
|
241
|
+
for the same finding; the earlier-round `FB-###` item is updated with the
|
|
242
|
+
new evidence rather than a new item being created.
|
|
243
|
+
- **Multi-round pattern continues to apply** (see "Multi-Round Bot Reviews"
|
|
244
|
+
section). A new bot round adds new `FB-###` items for findings that
|
|
245
|
+
weren't in the prior round; the prior round's items are carried forward
|
|
246
|
+
and updated with the new evidence.
|
|
247
|
+
|
|
248
|
+
Rationale: silently addressing a review comment without a corresponding
|
|
249
|
+
ledger item means the closure summary at the end of the run cannot
|
|
250
|
+
demonstrate that every review comment was considered. The closure summary
|
|
251
|
+
is the only artifact the user/maintainer reads to confirm the PR is ready
|
|
252
|
+
to merge. Missing items in the ledger = missing items in the closure = a
|
|
253
|
+
PR that ships with unreviewed feedback.
|
|
254
|
+
|
|
166
255
|
## Verification
|
|
167
256
|
|
|
168
257
|
Classify every ledger item before fixing:
|
|
@@ -173,6 +262,7 @@ Classify every ledger item before fixing:
|
|
|
173
262
|
| `PARTIAL` | The comment points at a real concern, but the framing, severity, or requested fix is incomplete. |
|
|
174
263
|
| `DISPROVED` | Source, tests, or execution context prove the claim is false, unreachable, or already mitigated. |
|
|
175
264
|
| `PRE_EXISTING` | The issue exists on the base branch and is not materially worsened by the PR. |
|
|
265
|
+
| `NEEDS_MORE_EVIDENCE` | The claim (e.g., "council APPROVED") is unsupported by stored evidence (e.g., a missing or failed `.swarm/evidence/` artifact); more information is required before triage. |
|
|
176
266
|
| `NEEDS_USER_DECISION` | The item requires a product, UX, compatibility, or scope choice that cannot be inferred. |
|
|
177
267
|
|
|
178
268
|
Verification checklist:
|
|
@@ -184,6 +274,26 @@ Verification checklist:
|
|
|
184
274
|
- Check related tests and whether a failing/proposed test would prove the item.
|
|
185
275
|
- Check whether multiple feedback items share one root cause.
|
|
186
276
|
|
|
277
|
+
### DI seam migration validation
|
|
278
|
+
|
|
279
|
+
When a test file mutates a DI seam object (e.g., `_internals.foo = mock`),
|
|
280
|
+
verify that the production source reads from the seam at call time. A common
|
|
281
|
+
anti-pattern: the test mutates the seam object, but the production code
|
|
282
|
+
imports the named function (`import { foo } from './module'`) which is bound
|
|
283
|
+
at module load. The seam mutation has no effect on the named reference,
|
|
284
|
+
so the test fails even though the seam object's `foo === mock`.
|
|
285
|
+
|
|
286
|
+
Verification: open the source file and grep for call sites. If you see
|
|
287
|
+
`import { foo } from '...'` followed by `foo(...)` in the production code,
|
|
288
|
+
and the test does `_internals.foo = mock`, the test will fail. The fix is
|
|
289
|
+
to change the production code to call `_internals.foo(...)` (or equivalent
|
|
290
|
+
active-seam pattern) so the seam mutation is read at call time.
|
|
291
|
+
|
|
292
|
+
If only a few call sites exist, fix them in the source. If many call sites
|
|
293
|
+
exist, consider whether the migration should use `mock.module()` instead,
|
|
294
|
+
which replaces the entire module object (including the named export
|
|
295
|
+
reference).
|
|
296
|
+
|
|
187
297
|
## Fix Planning
|
|
188
298
|
|
|
189
299
|
Cluster ledger items by root cause before coding. Fix in this order unless a user
|
|
@@ -251,6 +361,7 @@ FB-001 | fixed | commit/test evidence
|
|
|
251
361
|
FB-002 | disproved | code evidence
|
|
252
362
|
FB-003 | pre-existing | base-branch evidence
|
|
253
363
|
FB-004 | needs user decision | decision required
|
|
364
|
+
FB-005 | needs more evidence | .swarm/evidence/phase-council.json missing
|
|
254
365
|
CONFLICT-001 | fixed | remote mergeability is MERGEABLE/CLEAN
|
|
255
366
|
CI-001 | fixed | current-head check/run evidence
|
|
256
367
|
```
|
package/README.md
CHANGED
|
@@ -1107,7 +1107,7 @@ Control how tool outputs are summarized for LLM context.
|
|
|
1107
1107
|
| `/swarm deep-dive <scope> [--profile <name>] [--max-explorers <n>]` | Read-only codebase audit with parallel explorers, dual reviewers, and critic challenge |
|
|
1108
1108
|
| `/swarm design-docs <description> [--out <dir>] [--lang <name>] [--update]` | Generate or sync language-agnostic design docs (requires `design_docs.enabled`) |
|
|
1109
1109
|
| `/swarm dark-matter` | Detect hidden file couplings from co-change history |
|
|
1110
|
-
| `/swarm finalize [--prune-branches] [--skill-review]` | Idempotent session close-out: retrospectives, lesson curation, evidence archive,
|
|
1110
|
+
| `/swarm finalize [--prune-branches] [--skill-review]` | Idempotent session close-out: retrospectives, lesson curation, evidence archive, git reset --hard + clean to align with remote branch (discards uncommitted changes), optional branch pruning, optional skill-improver proposal |
|
|
1111
1111
|
| `/swarm close [--prune-branches] [--skill-review]` | Deprecated alias for `/swarm finalize [--prune-branches] [--skill-review]` |
|
|
1112
1112
|
| `/swarm write-retro` | Write a phase retrospective manually |
|
|
1113
1113
|
| `/swarm handoff` | Generate a handoff summary for context-budget-critical sessions |
|
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.78.
|
|
55
|
+
version: "7.78.8",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -37020,15 +37020,19 @@ function writeCheckpointLog(log2, directory) {
|
|
|
37020
37020
|
fs9.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
37021
37021
|
fs9.renameSync(tempPath, logPath);
|
|
37022
37022
|
}
|
|
37023
|
-
function gitExec(args) {
|
|
37023
|
+
function gitExec(args, cwd) {
|
|
37024
37024
|
const result = child_process.spawnSync("git", args, {
|
|
37025
|
+
cwd,
|
|
37025
37026
|
encoding: "utf-8",
|
|
37026
37027
|
timeout: GIT_TIMEOUT_MS,
|
|
37027
|
-
stdio: ["
|
|
37028
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37029
|
+
windowsHide: true
|
|
37028
37030
|
});
|
|
37031
|
+
if (result.error) {
|
|
37032
|
+
throw new Error(`git failed to start: ${result.error.code ?? "unknown"} \u2014 ${result.error.message}`);
|
|
37033
|
+
}
|
|
37029
37034
|
if (result.status !== 0) {
|
|
37030
|
-
|
|
37031
|
-
throw err;
|
|
37035
|
+
throw new Error(result.stderr?.trim() || `git exited with code ${result.status}`);
|
|
37032
37036
|
}
|
|
37033
37037
|
return result.stdout;
|
|
37034
37038
|
}
|
|
@@ -37040,13 +37044,13 @@ function appendRetentionEvent(directory, event) {
|
|
|
37040
37044
|
fs9.appendFileSync(eventsPath, line);
|
|
37041
37045
|
} catch {}
|
|
37042
37046
|
}
|
|
37043
|
-
function getCurrentSha() {
|
|
37044
|
-
const output = gitExec(["rev-parse", "HEAD"]);
|
|
37047
|
+
function getCurrentSha(directory) {
|
|
37048
|
+
const output = gitExec(["rev-parse", "HEAD"], directory);
|
|
37045
37049
|
return output.trim();
|
|
37046
37050
|
}
|
|
37047
|
-
function isGitRepo() {
|
|
37051
|
+
function isGitRepo(directory) {
|
|
37048
37052
|
try {
|
|
37049
|
-
gitExec(["rev-parse", "--git-dir"]);
|
|
37053
|
+
gitExec(["rev-parse", "--git-dir"], directory);
|
|
37050
37054
|
return true;
|
|
37051
37055
|
} catch {
|
|
37052
37056
|
return false;
|
|
@@ -37071,21 +37075,21 @@ function handleSave(label, directory) {
|
|
|
37071
37075
|
}, null, 2);
|
|
37072
37076
|
}
|
|
37073
37077
|
const timestamp = new Date().toISOString();
|
|
37074
|
-
gitExec(["add", "--all", "--", ":!.swarm/"]);
|
|
37078
|
+
gitExec(["add", "--all", "--", ":!.swarm/"], directory);
|
|
37075
37079
|
const hasStagedChanges = (() => {
|
|
37076
37080
|
try {
|
|
37077
|
-
gitExec(["diff", "--cached", "--quiet"]);
|
|
37081
|
+
gitExec(["diff", "--cached", "--quiet"], directory);
|
|
37078
37082
|
return false;
|
|
37079
37083
|
} catch {
|
|
37080
37084
|
return true;
|
|
37081
37085
|
}
|
|
37082
37086
|
})();
|
|
37083
37087
|
if (hasStagedChanges) {
|
|
37084
|
-
gitExec(["commit", "-m", `checkpoint: ${label}`]);
|
|
37088
|
+
gitExec(["commit", "-m", `checkpoint: ${label}`], directory);
|
|
37085
37089
|
} else if (allowEmptyCommits) {
|
|
37086
|
-
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`]);
|
|
37090
|
+
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`], directory);
|
|
37087
37091
|
}
|
|
37088
|
-
const newSha = getCurrentSha();
|
|
37092
|
+
const newSha = getCurrentSha(directory);
|
|
37089
37093
|
log2.checkpoints.push({
|
|
37090
37094
|
label,
|
|
37091
37095
|
sha: newSha,
|
|
@@ -37130,7 +37134,7 @@ function handleRestore(label, directory) {
|
|
|
37130
37134
|
error: `checkpoint not found: "${label}"`
|
|
37131
37135
|
}, null, 2);
|
|
37132
37136
|
}
|
|
37133
|
-
gitExec(["reset", "--soft", checkpoint.sha]);
|
|
37137
|
+
gitExec(["reset", "--soft", checkpoint.sha], directory);
|
|
37134
37138
|
return JSON.stringify({
|
|
37135
37139
|
action: "restore",
|
|
37136
37140
|
success: true,
|
|
@@ -37201,7 +37205,7 @@ var init_checkpoint = __esm(() => {
|
|
|
37201
37205
|
label: exports_external.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
37202
37206
|
},
|
|
37203
37207
|
execute: async (args, directory) => {
|
|
37204
|
-
if (!isGitRepo()) {
|
|
37208
|
+
if (!isGitRepo(directory)) {
|
|
37205
37209
|
return JSON.stringify({
|
|
37206
37210
|
action: "unknown",
|
|
37207
37211
|
success: false,
|
|
@@ -67722,7 +67726,7 @@ var init_registry = __esm(() => {
|
|
|
67722
67726
|
sessionID: ctx.sessionID
|
|
67723
67727
|
}),
|
|
67724
67728
|
description: "Use /swarm finalize to finalize the swarm project and archive evidence",
|
|
67725
|
-
details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs
|
|
67729
|
+
details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs aggressive git reset --hard to the default remote branch, discarding uncommitted changes and untracked files; falls back to a cautious reset that preserves uncommitted changes when the aggressive path cannot proceed. WARNING: alignment discards local changes and untracked files. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation. Use --skill-review to run the quota-bounded skill_improver in proposal mode.",
|
|
67726
67730
|
args: "--prune-branches, --skill-review",
|
|
67727
67731
|
category: "core",
|
|
67728
67732
|
toolPolicy: "none"
|
|
@@ -83,3 +83,12 @@ export declare const CLAUDE_CODE_CONFLICTS: readonly [{
|
|
|
83
83
|
export declare const CRITICAL_CONFLICTS: Set<string>;
|
|
84
84
|
export declare const HIGH_CONFLICTS: Set<string>;
|
|
85
85
|
export declare const CONFLICT_MAP: Map<string, CommandConflict>;
|
|
86
|
+
/**
|
|
87
|
+
* CC_COMMAND_MAP — maps CC command names (without leading /) to their CommandConflict entry.
|
|
88
|
+
* Used by cc-command-intercept hook to look up conflicts by the bare CC command name.
|
|
89
|
+
* E.g., 'plan' → CommandConflict for /plan, 'reset' → CommandConflict for /reset, 'clear' → CommandConflict for /clear
|
|
90
|
+
*
|
|
91
|
+
* Known aliases are registered below: /clear is documented as an alias for /reset
|
|
92
|
+
* (see /reset entry's `ccBehavior` field), so it must be intercepted identically.
|
|
93
|
+
*/
|
|
94
|
+
export declare const CC_COMMAND_MAP: Map<string, CommandConflict>;
|
|
@@ -312,7 +312,7 @@ export declare const COMMAND_REGISTRY: {
|
|
|
312
312
|
readonly finalize: {
|
|
313
313
|
readonly handler: (ctx: CommandContext) => Promise<string>;
|
|
314
314
|
readonly description: "Use /swarm finalize to finalize the swarm project and archive evidence";
|
|
315
|
-
readonly details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs
|
|
315
|
+
readonly details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs aggressive git reset --hard to the default remote branch, discarding uncommitted changes and untracked files; falls back to a cautious reset that preserves uncommitted changes when the aggressive path cannot proceed. WARNING: alignment discards local changes and untracked files. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation. Use --skill-review to run the quota-bounded skill_improver in proposal mode.";
|
|
316
316
|
readonly args: "--prune-branches, --skill-review";
|
|
317
317
|
readonly category: "core";
|
|
318
318
|
readonly toolPolicy: "none";
|
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.78.
|
|
72
|
+
version: "7.78.8",
|
|
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",
|
|
@@ -58440,15 +58440,19 @@ function writeCheckpointLog(log2, directory) {
|
|
|
58440
58440
|
fs19.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
58441
58441
|
fs19.renameSync(tempPath, logPath);
|
|
58442
58442
|
}
|
|
58443
|
-
function gitExec(args2) {
|
|
58443
|
+
function gitExec(args2, cwd) {
|
|
58444
58444
|
const result = child_process.spawnSync("git", args2, {
|
|
58445
|
+
cwd,
|
|
58445
58446
|
encoding: "utf-8",
|
|
58446
58447
|
timeout: GIT_TIMEOUT_MS,
|
|
58447
|
-
stdio: ["
|
|
58448
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
58449
|
+
windowsHide: true
|
|
58448
58450
|
});
|
|
58451
|
+
if (result.error) {
|
|
58452
|
+
throw new Error(`git failed to start: ${result.error.code ?? "unknown"} — ${result.error.message}`);
|
|
58453
|
+
}
|
|
58449
58454
|
if (result.status !== 0) {
|
|
58450
|
-
|
|
58451
|
-
throw err2;
|
|
58455
|
+
throw new Error(result.stderr?.trim() || `git exited with code ${result.status}`);
|
|
58452
58456
|
}
|
|
58453
58457
|
return result.stdout;
|
|
58454
58458
|
}
|
|
@@ -58460,13 +58464,13 @@ function appendRetentionEvent(directory, event) {
|
|
|
58460
58464
|
fs19.appendFileSync(eventsPath, line);
|
|
58461
58465
|
} catch {}
|
|
58462
58466
|
}
|
|
58463
|
-
function getCurrentSha() {
|
|
58464
|
-
const output = gitExec(["rev-parse", "HEAD"]);
|
|
58467
|
+
function getCurrentSha(directory) {
|
|
58468
|
+
const output = gitExec(["rev-parse", "HEAD"], directory);
|
|
58465
58469
|
return output.trim();
|
|
58466
58470
|
}
|
|
58467
|
-
function isGitRepo() {
|
|
58471
|
+
function isGitRepo(directory) {
|
|
58468
58472
|
try {
|
|
58469
|
-
gitExec(["rev-parse", "--git-dir"]);
|
|
58473
|
+
gitExec(["rev-parse", "--git-dir"], directory);
|
|
58470
58474
|
return true;
|
|
58471
58475
|
} catch {
|
|
58472
58476
|
return false;
|
|
@@ -58491,21 +58495,21 @@ function handleSave(label, directory) {
|
|
|
58491
58495
|
}, null, 2);
|
|
58492
58496
|
}
|
|
58493
58497
|
const timestamp = new Date().toISOString();
|
|
58494
|
-
gitExec(["add", "--all", "--", ":!.swarm/"]);
|
|
58498
|
+
gitExec(["add", "--all", "--", ":!.swarm/"], directory);
|
|
58495
58499
|
const hasStagedChanges = (() => {
|
|
58496
58500
|
try {
|
|
58497
|
-
gitExec(["diff", "--cached", "--quiet"]);
|
|
58501
|
+
gitExec(["diff", "--cached", "--quiet"], directory);
|
|
58498
58502
|
return false;
|
|
58499
58503
|
} catch {
|
|
58500
58504
|
return true;
|
|
58501
58505
|
}
|
|
58502
58506
|
})();
|
|
58503
58507
|
if (hasStagedChanges) {
|
|
58504
|
-
gitExec(["commit", "-m", `checkpoint: ${label}`]);
|
|
58508
|
+
gitExec(["commit", "-m", `checkpoint: ${label}`], directory);
|
|
58505
58509
|
} else if (allowEmptyCommits) {
|
|
58506
|
-
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`]);
|
|
58510
|
+
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`], directory);
|
|
58507
58511
|
}
|
|
58508
|
-
const newSha = getCurrentSha();
|
|
58512
|
+
const newSha = getCurrentSha(directory);
|
|
58509
58513
|
log2.checkpoints.push({
|
|
58510
58514
|
label,
|
|
58511
58515
|
sha: newSha,
|
|
@@ -58550,9 +58554,9 @@ function saveCheckpointRecord(label, directory) {
|
|
|
58550
58554
|
return { success: false, error: `duplicate label: "${label}"` };
|
|
58551
58555
|
}
|
|
58552
58556
|
let sha = "";
|
|
58553
|
-
if (isGitRepo()) {
|
|
58557
|
+
if (isGitRepo(directory)) {
|
|
58554
58558
|
try {
|
|
58555
|
-
sha = getCurrentSha();
|
|
58559
|
+
sha = getCurrentSha(directory);
|
|
58556
58560
|
} catch {
|
|
58557
58561
|
sha = "";
|
|
58558
58562
|
}
|
|
@@ -58582,7 +58586,7 @@ function handleRestore(label, directory) {
|
|
|
58582
58586
|
error: `checkpoint not found: "${label}"`
|
|
58583
58587
|
}, null, 2);
|
|
58584
58588
|
}
|
|
58585
|
-
gitExec(["reset", "--soft", checkpoint.sha]);
|
|
58589
|
+
gitExec(["reset", "--soft", checkpoint.sha], directory);
|
|
58586
58590
|
return JSON.stringify({
|
|
58587
58591
|
action: "restore",
|
|
58588
58592
|
success: true,
|
|
@@ -58653,7 +58657,7 @@ var init_checkpoint = __esm(() => {
|
|
|
58653
58657
|
label: exports_external.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
58654
58658
|
},
|
|
58655
58659
|
execute: async (args2, directory) => {
|
|
58656
|
-
if (!isGitRepo()) {
|
|
58660
|
+
if (!isGitRepo(directory)) {
|
|
58657
58661
|
return JSON.stringify({
|
|
58658
58662
|
action: "unknown",
|
|
58659
58663
|
success: false,
|
|
@@ -70944,17 +70948,6 @@ var init_curate = __esm(() => {
|
|
|
70944
70948
|
});
|
|
70945
70949
|
|
|
70946
70950
|
// src/tools/co-change-analyzer.ts
|
|
70947
|
-
var exports_co_change_analyzer = {};
|
|
70948
|
-
__export(exports_co_change_analyzer, {
|
|
70949
|
-
parseGitLog: () => parseGitLog,
|
|
70950
|
-
getStaticEdges: () => getStaticEdges,
|
|
70951
|
-
formatDarkMatterOutput: () => formatDarkMatterOutput,
|
|
70952
|
-
detectDarkMatter: () => detectDarkMatter,
|
|
70953
|
-
darkMatterToKnowledgeEntries: () => darkMatterToKnowledgeEntries,
|
|
70954
|
-
co_change_analyzer: () => co_change_analyzer,
|
|
70955
|
-
buildCoChangeMatrix: () => buildCoChangeMatrix,
|
|
70956
|
-
_internals: () => _internals36
|
|
70957
|
-
});
|
|
70958
70951
|
import * as child_process3 from "node:child_process";
|
|
70959
70952
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
70960
70953
|
import { readdir as readdir4, readFile as readFile16, stat as stat8 } from "node:fs/promises";
|
|
@@ -92952,7 +92945,7 @@ var init_registry = __esm(() => {
|
|
|
92952
92945
|
sessionID: ctx.sessionID
|
|
92953
92946
|
}),
|
|
92954
92947
|
description: "Use /swarm finalize to finalize the swarm project and archive evidence",
|
|
92955
|
-
details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs
|
|
92948
|
+
details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs aggressive git reset --hard to the default remote branch, discarding uncommitted changes and untracked files; falls back to a cautious reset that preserves uncommitted changes when the aggressive path cannot proceed. WARNING: alignment discards local changes and untracked files. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation. Use --skill-review to run the quota-bounded skill_improver in proposal mode.",
|
|
92956
92949
|
args: "--prune-branches, --skill-review",
|
|
92957
92950
|
category: "core",
|
|
92958
92951
|
toolPolicy: "none"
|
|
@@ -106482,6 +106475,14 @@ var CLAUDE_CODE_CONFLICTS = [
|
|
|
106482
106475
|
var CRITICAL_CONFLICTS = new Set(CLAUDE_CODE_CONFLICTS.filter((c) => c.severity === "CRITICAL").map((c) => c.swarmCommand));
|
|
106483
106476
|
var HIGH_CONFLICTS = new Set(CLAUDE_CODE_CONFLICTS.filter((c) => c.severity === "HIGH").map((c) => c.swarmCommand));
|
|
106484
106477
|
var CONFLICT_MAP = new Map(CLAUDE_CODE_CONFLICTS.map((c) => [c.swarmCommand, c]));
|
|
106478
|
+
var CC_COMMAND_MAP = new Map;
|
|
106479
|
+
for (const conflict of CLAUDE_CODE_CONFLICTS) {
|
|
106480
|
+
const ccCommandName = conflict.ccCommand.replace(/^\//, "").toLowerCase();
|
|
106481
|
+
CC_COMMAND_MAP.set(ccCommandName, conflict);
|
|
106482
|
+
if (ccCommandName === "reset") {
|
|
106483
|
+
CC_COMMAND_MAP.set("clear", conflict);
|
|
106484
|
+
}
|
|
106485
|
+
}
|
|
106485
106486
|
|
|
106486
106487
|
// src/hooks/cc-command-intercept.ts
|
|
106487
106488
|
init_constants();
|
|
@@ -106547,8 +106548,7 @@ function createCcCommandInterceptHook(config3 = {}) {
|
|
|
106547
106548
|
continue;
|
|
106548
106549
|
}
|
|
106549
106550
|
const bareCmd = bareCmdMatch[1].toLowerCase();
|
|
106550
|
-
const
|
|
106551
|
-
const conflict = CONFLICT_MAP.get(effectiveCmd);
|
|
106551
|
+
const conflict = CC_COMMAND_MAP.get(bareCmd);
|
|
106552
106552
|
if (!conflict) {
|
|
106553
106553
|
if (CLAUDE_CODE_NATIVE_COMMANDS.has(bareCmd)) {
|
|
106554
106554
|
if (logIntercepts) {
|
|
@@ -111458,6 +111458,7 @@ init_status_service();
|
|
|
111458
111458
|
|
|
111459
111459
|
// src/hooks/system-enhancer.ts
|
|
111460
111460
|
init_telemetry();
|
|
111461
|
+
init_co_change_analyzer();
|
|
111461
111462
|
init_utils();
|
|
111462
111463
|
|
|
111463
111464
|
// src/hooks/adversarial-detector.ts
|
|
@@ -113111,34 +113112,29 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
113111
113112
|
try {
|
|
113112
113113
|
const darkMatterPath = validateSwarmPath(directory, "dark-matter.md");
|
|
113113
113114
|
if (!fs69.existsSync(darkMatterPath)) {
|
|
113114
|
-
const {
|
|
113115
|
-
detectDarkMatter: detectDarkMatter2,
|
|
113116
|
-
formatDarkMatterOutput: formatDarkMatterOutput2,
|
|
113117
|
-
darkMatterToKnowledgeEntries: darkMatterToKnowledgeEntries2
|
|
113118
|
-
} = await Promise.resolve().then(() => (init_co_change_analyzer(), exports_co_change_analyzer));
|
|
113119
|
-
const darkMatter = await detectDarkMatter2(directory, {
|
|
113115
|
+
const darkMatter = await _internals36.detectDarkMatter(directory, {
|
|
113120
113116
|
minCommits: 20,
|
|
113121
113117
|
minCoChanges: 3
|
|
113122
113118
|
});
|
|
113123
113119
|
await fs69.promises.mkdir(path122.dirname(darkMatterPath), {
|
|
113124
113120
|
recursive: true
|
|
113125
113121
|
});
|
|
113126
|
-
const darkMatterReport =
|
|
113122
|
+
const darkMatterReport = _internals36.formatDarkMatterOutput(darkMatter);
|
|
113127
113123
|
await fs69.promises.writeFile(darkMatterPath, darkMatterReport, "utf-8");
|
|
113128
113124
|
warn(`[system-enhancer] Dark matter scan complete: ${darkMatter.length} co-change patterns found`);
|
|
113129
113125
|
if (darkMatter.length > 0) {
|
|
113130
113126
|
try {
|
|
113131
113127
|
const projectName = path122.basename(path122.resolve(directory));
|
|
113132
|
-
const knowledgeEntries =
|
|
113133
|
-
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
113134
|
-
const existingEntries = await readKnowledge(knowledgePath);
|
|
113128
|
+
const knowledgeEntries = _internals36.darkMatterToKnowledgeEntries(darkMatter, projectName);
|
|
113129
|
+
const knowledgePath = _internals23.resolveSwarmKnowledgePath(directory);
|
|
113130
|
+
const existingEntries = await _internals23.readKnowledge(knowledgePath);
|
|
113135
113131
|
const existingLessons = new Set(existingEntries.map((e) => e.lesson));
|
|
113136
113132
|
const newEntries = knowledgeEntries.filter((e) => !existingLessons.has(e.lesson) && validateActionability(e).actionable);
|
|
113137
113133
|
if (newEntries.length === 0) {
|
|
113138
113134
|
warn(`[system-enhancer] No new knowledge entries (all duplicates)`);
|
|
113139
113135
|
} else {
|
|
113140
113136
|
for (const entry of newEntries) {
|
|
113141
|
-
await appendKnowledge(knowledgePath, entry);
|
|
113137
|
+
await _internals23.appendKnowledge(knowledgePath, entry);
|
|
113142
113138
|
}
|
|
113143
113139
|
warn(`[system-enhancer] Created ${newEntries.length} new knowledge entries (${knowledgeEntries.length - newEntries.length} duplicates skipped)`);
|
|
113144
113140
|
}
|
|
@@ -113148,15 +113144,15 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
113148
113144
|
}
|
|
113149
113145
|
}
|
|
113150
113146
|
try {
|
|
113151
|
-
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
113152
|
-
const allEntries = await readKnowledge(knowledgePath);
|
|
113147
|
+
const knowledgePath = _internals23.resolveSwarmKnowledgePath(directory);
|
|
113148
|
+
const allEntries = await _internals23.readKnowledge(knowledgePath);
|
|
113153
113149
|
const stale = allEntries.filter((e) => e.scope === "project" && e.auto_generated === true && Array.isArray(e.tags) && e.tags.includes("dark-matter"));
|
|
113154
113150
|
if (stale.length > 0) {
|
|
113155
113151
|
for (const e of stale) {
|
|
113156
113152
|
e.scope = "global";
|
|
113157
113153
|
e.updated_at = new Date().toISOString();
|
|
113158
113154
|
}
|
|
113159
|
-
await rewriteKnowledge(knowledgePath, allEntries);
|
|
113155
|
+
await _internals23.rewriteKnowledge(knowledgePath, allEntries);
|
|
113160
113156
|
warn(`[system-enhancer] Repaired ${stale.length} dark matter knowledge entries (scope: 'project' → 'global')`);
|
|
113161
113157
|
}
|
|
113162
113158
|
} catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.78.
|
|
3
|
+
"version": "7.78.8",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|