opencode-swarm 7.99.0 → 7.99.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +11 -1
  2. package/.opencode/skills/loop/SKILL.md +13 -4
  3. package/.opencode/skills/plan/SKILL.md +8 -1
  4. package/.opencode/skills/specify/SKILL.md +4 -2
  5. package/README.md +5 -3
  6. package/dist/agents/_prompt-helpers.d.ts +1 -0
  7. package/dist/cli/{evidence-summary-service-4hs44n2c.js → evidence-summary-service-wxarfgt8.js} +2 -2
  8. package/dist/cli/{explorer-jc46negv.js → explorer-rwfvbbx5.js} +1 -1
  9. package/dist/cli/{guardrail-explain-ktm6szbm.js → guardrail-explain-t3prwa5b.js} +6 -6
  10. package/dist/cli/{index-hw8r3e5p.js → index-62tmq1kc.js} +212 -109
  11. package/dist/cli/{index-wwnjw0fb.js → index-6wgwybzj.js} +27 -14
  12. package/dist/cli/{index-we94wkty.js → index-94qwbx11.js} +48 -0
  13. package/dist/cli/{index-4pt2py5p.js → index-ryns3fqt.js} +6 -6
  14. package/dist/cli/{index-5fxjagjt.js → index-s3esnz7m.js} +1 -1
  15. package/dist/cli/{index-ft4gehk1.js → index-y4stbk2f.js} +1 -1
  16. package/dist/cli/{index-2a6ppa65.js → index-ydgy7vg5.js} +12 -7
  17. package/dist/cli/index.js +5 -5
  18. package/dist/cli/{pending-delegations-c785p5n2.js → pending-delegations-vh2nae3n.js} +1 -1
  19. package/dist/cli/{pr-subscriptions-fqrzm0tv.js → pr-subscriptions-d57tq0c0.js} +2 -2
  20. package/dist/commands/loop.d.ts +5 -0
  21. package/dist/commands/registry.d.ts +5 -2
  22. package/dist/db/qa-gate-profile.d.ts +10 -0
  23. package/dist/evidence/manager.d.ts +5 -0
  24. package/dist/hooks/auto-review.d.ts +1 -1
  25. package/dist/hooks/knowledge-migrator.d.ts +7 -0
  26. package/dist/index.js +2265 -2033
  27. package/dist/plan/utils.d.ts +8 -3
  28. package/dist/tools/dispatch-lanes.d.ts +14 -0
  29. package/dist/tools/phase-complete/gates/gate-helpers.d.ts +2 -0
  30. package/dist/tools/phase-complete/gates/types.d.ts +4 -0
  31. package/dist/turbo/lean/conflicts.d.ts +2 -13
  32. package/dist/utils/path.d.ts +1 -0
  33. package/package.json +1 -1
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  readSwarmFileAsync,
10
10
  validateSwarmPath
11
- } from "./index-we94wkty.js";
11
+ } from "./index-94qwbx11.js";
12
12
  import {
13
13
  readCachedParsedFile
14
14
  } from "./index-jtqkh8jf.js";
@@ -939,6 +939,26 @@ var _internals2 = {
939
939
  // src/turbo/lean/conflicts.ts
940
940
  import * as fs3 from "fs";
941
941
  import * as path3 from "path";
942
+
943
+ // src/utils/path.ts
944
+ function normalizePath(p) {
945
+ if (!p)
946
+ return "";
947
+ let result = p.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
948
+ result = result.replace(/\/$/, "");
949
+ result = result.replace(/\/\.$/, "");
950
+ if (process.platform === "win32")
951
+ result = result.toLowerCase();
952
+ if (!result) {
953
+ const norm = p.replace(/\\/g, "/");
954
+ if (norm === "." || norm === "./")
955
+ return ".";
956
+ return "";
957
+ }
958
+ return result;
959
+ }
960
+
961
+ // src/turbo/lean/conflicts.ts
942
962
  var DEFAULT_GLOBAL_FILES = [
943
963
  "package.json",
944
964
  "package-lock.json",
@@ -997,18 +1017,6 @@ var BARREL_FILE_PATTERNS = [
997
1017
  /\/exports\.ts$/,
998
1018
  /\/types\.ts$/
999
1019
  ];
1000
- function normalizePath(filePath) {
1001
- if (filePath === "." || filePath === "./") {
1002
- return ".";
1003
- }
1004
- let result = filePath.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "").replace(/(?:^|\/)\.\//g, "/");
1005
- result = result.replace(/\/$/, "");
1006
- result = result.replace(/\/\.$/, "");
1007
- if (process.platform === "win32") {
1008
- result = result.toLowerCase();
1009
- }
1010
- return result;
1011
- }
1012
1020
  function pathsConflict(path1, path22) {
1013
1021
  if (process.platform === "win32") {
1014
1022
  path1 = path1.toLowerCase();
@@ -3535,6 +3543,7 @@ async function saveEvidence(directory, taskId, evidence) {
3535
3543
  const sanitizedTaskId = sanitizeTaskId2(taskId);
3536
3544
  const relativePath = path7.join("evidence", sanitizedTaskId, "evidence.json");
3537
3545
  validateSwarmPath(directory, relativePath);
3546
+ _internals5.validateEvidence(evidence);
3538
3547
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
3539
3548
  const evidencePath = validateSwarmPath(directory, relativePath);
3540
3549
  const evidenceDir = path7.dirname(evidencePath);
@@ -3771,11 +3780,15 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
3771
3780
  }
3772
3781
  return archived;
3773
3782
  }
3783
+ function validateEvidence(evidence) {
3784
+ return EvidenceSchema.parse(evidence);
3785
+ }
3774
3786
  var _internals5 = {
3775
3787
  wrapFlatRetrospective,
3776
3788
  loadEvidence,
3777
3789
  listEvidenceTaskIds,
3778
- validateProjectRoot
3790
+ validateProjectRoot,
3791
+ validateEvidence
3779
3792
  };
3780
3793
 
3781
3794
  // src/evidence/gate-bridge.ts
@@ -13,6 +13,7 @@ import {
13
13
  } from "./index-b9v501fr.js";
14
14
 
15
15
  // src/hooks/utils.ts
16
+ import * as fs from "fs";
16
17
  import * as path from "path";
17
18
  var _internals = {
18
19
  safeHook,
@@ -71,8 +72,55 @@ function validateSwarmPath(directory, filename) {
71
72
  throw new Error("Invalid filename: path escapes .swarm directory");
72
73
  }
73
74
  }
75
+ let realBaseDir;
76
+ try {
77
+ if (fs.lstatSync(baseDir).isSymbolicLink()) {
78
+ throw new Error("Invalid filename: path escapes .swarm directory");
79
+ }
80
+ realBaseDir = fs.realpathSync(baseDir);
81
+ } catch (error) {
82
+ if (error instanceof Error && error.message === "Invalid filename: path escapes .swarm directory") {
83
+ throw error;
84
+ }
85
+ if (error.code === "ENOENT") {
86
+ return resolved;
87
+ }
88
+ throw new Error("Invalid filename: failed to resolve .swarm directory");
89
+ }
90
+ let existingPath = resolved;
91
+ while (!fs.existsSync(existingPath)) {
92
+ const parent = path.dirname(existingPath);
93
+ if (parent === existingPath || !isPathWithin(parent, baseDir)) {
94
+ existingPath = baseDir;
95
+ break;
96
+ }
97
+ existingPath = parent;
98
+ }
99
+ let realExistingPath;
100
+ try {
101
+ realExistingPath = fs.realpathSync(existingPath);
102
+ } catch (error) {
103
+ if (error.code === "ENOENT") {
104
+ realExistingPath = realBaseDir;
105
+ } else {
106
+ throw new Error("Invalid filename: failed to resolve path");
107
+ }
108
+ }
109
+ if (!isPathWithin(realExistingPath, realBaseDir)) {
110
+ throw new Error("Invalid filename: path escapes .swarm directory");
111
+ }
74
112
  return resolved;
75
113
  }
114
+ function isPathWithin(candidate, base) {
115
+ const normalizedCandidate = path.normalize(candidate);
116
+ const normalizedBase = path.normalize(base);
117
+ if (process.platform === "win32") {
118
+ const candidateLower = normalizedCandidate.toLowerCase();
119
+ const baseLower = normalizedBase.toLowerCase();
120
+ return candidateLower === baseLower || candidateLower.startsWith(`${baseLower}${path.sep}`);
121
+ }
122
+ return normalizedCandidate === normalizedBase || normalizedCandidate.startsWith(`${normalizedBase}${path.sep}`);
123
+ }
76
124
  async function readSwarmFileAsync(directory, filename, cache) {
77
125
  if (cache !== undefined) {
78
126
  const key = `${directory}::${filename}`;
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-ft4gehk1.js";
4
+ } from "./index-y4stbk2f.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-h9ptj4b1.js";
@@ -79,9 +79,9 @@ import {
79
79
  handleWriteRetroCommand,
80
80
  normalizeSwarmCommandInput,
81
81
  resolveCommand
82
- } from "./index-hw8r3e5p.js";
82
+ } from "./index-62tmq1kc.js";
83
83
  import"./index-xsswhy6k.js";
84
- import"./index-2a6ppa65.js";
84
+ import"./index-ydgy7vg5.js";
85
85
  import"./index-vwyjkzgs.js";
86
86
  import"./index-f7nma02k.js";
87
87
  import"./index-svf2zjxs.js";
@@ -90,13 +90,13 @@ import {
90
90
  ORCHESTRATOR_NAME,
91
91
  stripKnownSwarmPrefix
92
92
  } from "./index-7hnwnw5g.js";
93
- import"./index-wwnjw0fb.js";
93
+ import"./index-6wgwybzj.js";
94
94
  import"./index-adz3nk9b.js";
95
95
  import"./index-v4fcn4tr.js";
96
96
  import"./index-r8f89sm9.js";
97
97
  import"./index-9b7qp18e.js";
98
- import"./index-5fxjagjt.js";
99
- import"./index-we94wkty.js";
98
+ import"./index-s3esnz7m.js";
99
+ import"./index-94qwbx11.js";
100
100
  import"./index-jtqkh8jf.js";
101
101
  import"./index-5e4e2hvv.js";
102
102
  import"./index-p0arc26j.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  validateSwarmPath
4
- } from "./index-we94wkty.js";
4
+ } from "./index-94qwbx11.js";
5
5
  import {
6
6
  log
7
7
  } from "./index-zgwm4ryv.js";
@@ -12,7 +12,7 @@ import {
12
12
  detectPosixWrites,
13
13
  detectWindowsWrites,
14
14
  resolveWriteTargets
15
- } from "./index-hw8r3e5p.js";
15
+ } from "./index-62tmq1kc.js";
16
16
  import {
17
17
  checkFileAuthority,
18
18
  classifyFile,
@@ -1,4 +1,15 @@
1
1
  // @bun
2
+ // src/agents/_prompt-helpers.ts
3
+ function resolvePrompt(base, custom, append) {
4
+ if (custom)
5
+ return custom;
6
+ if (append)
7
+ return `${base}
8
+
9
+ ${append}`;
10
+ return base;
11
+ }
12
+
2
13
  // src/agents/read-only-lane-guidance.ts
3
14
  var READ_ONLY_LANE_GUIDANCE = `## READ-ONLY ADVISORY LANE CONTEXT
4
15
 
@@ -379,13 +390,7 @@ OUTPUT FORMAT:
379
390
  `;
380
391
  function createExplorerAgent(model, customPrompt, customAppendPrompt) {
381
392
  let prompt = EXPLORER_PROMPT;
382
- if (customPrompt) {
383
- prompt = customPrompt;
384
- } else if (customAppendPrompt) {
385
- prompt = `${EXPLORER_PROMPT}
386
-
387
- ${customAppendPrompt}`;
388
- }
393
+ prompt = resolvePrompt(prompt, customPrompt, customAppendPrompt);
389
394
  return {
390
395
  name: "explorer",
391
396
  description: "Fast codebase discovery and analysis. Scans directory structure, identifies languages/frameworks, summarizes key files, and identifies areas where specialized domain knowledge may be beneficial.",
package/dist/cli/index.js CHANGED
@@ -7,22 +7,22 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-hw8r3e5p.js";
10
+ } from "./index-62tmq1kc.js";
11
11
  import"./index-xsswhy6k.js";
12
- import"./index-2a6ppa65.js";
12
+ import"./index-ydgy7vg5.js";
13
13
  import"./index-vwyjkzgs.js";
14
14
  import"./index-f7nma02k.js";
15
15
  import"./index-svf2zjxs.js";
16
16
  import {
17
17
  DEFAULT_AGENT_CONFIGS
18
18
  } from "./index-7hnwnw5g.js";
19
- import"./index-wwnjw0fb.js";
19
+ import"./index-6wgwybzj.js";
20
20
  import"./index-adz3nk9b.js";
21
21
  import"./index-v4fcn4tr.js";
22
22
  import"./index-r8f89sm9.js";
23
23
  import"./index-9b7qp18e.js";
24
- import"./index-5fxjagjt.js";
25
- import"./index-we94wkty.js";
24
+ import"./index-s3esnz7m.js";
25
+ import"./index-94qwbx11.js";
26
26
  import"./index-jtqkh8jf.js";
27
27
  import"./index-5e4e2hvv.js";
28
28
  import"./index-p0arc26j.js";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  validateSwarmPath
4
- } from "./index-we94wkty.js";
4
+ } from "./index-94qwbx11.js";
5
5
  import"./index-jtqkh8jf.js";
6
6
  import"./index-5e4e2hvv.js";
7
7
  import"./index-p0arc26j.js";
@@ -9,8 +9,8 @@ import {
9
9
  sweepStale,
10
10
  unsubscribe,
11
11
  updateSnapshot
12
- } from "./index-5fxjagjt.js";
13
- import"./index-we94wkty.js";
12
+ } from "./index-s3esnz7m.js";
13
+ import"./index-94qwbx11.js";
14
14
  import"./index-jtqkh8jf.js";
15
15
  import"./index-5e4e2hvv.js";
16
16
  import"./index-p0arc26j.js";
@@ -12,4 +12,9 @@
12
12
  * headers or newline-based control sequences (mirrors the brainstorm /
13
13
  * deep-dive handlers).
14
14
  */
15
+ export declare const _internals: {
16
+ readLatestLoopState: (directory: string) => Promise<{
17
+ autonomy?: string;
18
+ } | null>;
19
+ };
15
20
  export declare function handleLoopCommand(_directory: string, args: string[]): Promise<string>;
@@ -469,7 +469,7 @@ export declare const COMMAND_REGISTRY: {
469
469
  readonly handler: (ctx: CommandContext) => CommandResult;
470
470
  readonly description: "Enter architect MODE: LOOP — compound-engineering loop: brainstorm → plan → build → review → improve, iterating until done [objective]";
471
471
  readonly args: "<objective> [--max-cycles 1..5] [--autonomy checkpoint|auto] [--depth standard|exhaustive] [--resume]";
472
- readonly details: "Triggers the architect to run the compound-engineering loop defined in .opencode/skills/loop/SKILL.md: BRAINSTORM (requirements) → PLAN (+ critic gate) → BUILD (execute) → REVIEW (independent reviewer + critic on the diff, report-only) → IMPROVE (phase-wrap retrospective + compounding learning capture), then evaluate stop conditions and loop for another improvement cycle if the objective is unmet and budget remains. Generator and reviewer/critic run in separate contexts; failing assertions must be fixed at the root cause, never weakened, mocked, or skipped. Defense-in-depth stop conditions: objective met, --max-cycles budget (default 3), no-progress/plateau, oscillation, unrecoverable error, or explicit user stop. --autonomy checkpoint (default) pauses at phase gates for user approval; --autonomy auto runs unattended with hard stops still enforced. --depth exhaustive widens exploration. --resume continues an existing loop run from durable .swarm/loop/ state. Distinct from full-auto (autonomous cross-phase oversight) and turbo (parallel lanes within a phase): loop is a user-initiated, gated, compounding workflow.";
472
+ readonly details: "Triggers the architect to run the compound-engineering loop defined in .opencode/skills/loop/SKILL.md: BRAINSTORM (requirements) → PLAN (+ critic gate) → BUILD (execute) → REVIEW (independent reviewer + critic on the diff, report-only) → IMPROVE (phase-wrap retrospective + compounding learning capture), then evaluate stop conditions and loop for another improvement cycle if the objective is unmet and budget remains. Generator and reviewer/critic run in separate contexts; failing assertions must be fixed at the root cause, never weakened, mocked, or skipped. Defense-in-depth stop conditions: objective met, --max-cycles budget (default 3), no-progress/plateau, oscillation, unrecoverable error, or explicit user stop. --autonomy auto (default) runs unattended with hard stops still enforced; --autonomy checkpoint pauses at phase gates for user approval. --depth exhaustive widens exploration. --resume continues an existing loop run from durable .swarm/loop/ state. Distinct from full-auto (autonomous cross-phase oversight) and turbo (parallel lanes within a phase): loop is a user-initiated, gated, compounding workflow.";
473
473
  readonly category: "agent";
474
474
  readonly toolPolicy: "none";
475
475
  };
@@ -895,7 +895,10 @@ export declare const VALID_COMMANDS: RegisteredCommand[];
895
895
  * Checks for:
896
896
  * - aliasOf pointing to an existing command
897
897
  * - circular alias chains (A → B → C → A)
898
- * - duplicate alias targets (multiple aliases for different commands) — logged as warning, not error
898
+ *
899
+ * Duplicate aliases to the same canonical command are intentional in this repo
900
+ * (for example, dash-joined TUI shortcuts and legacy compatibility names), so
901
+ * they are not reported as warnings.
899
902
  */
900
903
  export declare function validateAliases(): {
901
904
  valid: boolean;
@@ -18,6 +18,7 @@ export declare const _internals: {
18
18
  setGates: typeof setGates;
19
19
  getEffectiveGates: typeof getEffectiveGates;
20
20
  computeProfileHash: typeof computeProfileHash;
21
+ hasAnyProfileWithEnabledGate: typeof hasAnyProfileWithEnabledGate;
21
22
  };
22
23
  /**
23
24
  * QA gate flags. All eleven gates are tracked explicitly.
@@ -62,6 +63,15 @@ export interface QaGateProfile {
62
63
  * `lockProfile`) continue to initialize the DB on demand.
63
64
  */
64
65
  export declare function getProfile(directory: string, planId: string): QaGateProfile | null;
66
+ /**
67
+ * Return true if any existing QA gate profile has `gate` enabled.
68
+ *
69
+ * This is intentionally read-only and does not create the project DB. It is used
70
+ * by fail-closed gates when plan.json is unavailable, so they can still honor a
71
+ * previously persisted hard gate instead of silently passing because plan
72
+ * identity could not be derived.
73
+ */
74
+ export declare function hasAnyProfileWithEnabledGate(directory: string, gate: keyof QaGates): boolean;
65
75
  /**
66
76
  * Return the existing profile for `planId`, or create a new one seeded with
67
77
  * `DEFAULT_QA_GATES` if none exists. Tolerates races on the UNIQUE index.
@@ -93,6 +93,10 @@ export declare function checkRequirementCoverage(phase: number, directory: strin
93
93
  * Returns array of archived (deleted) task IDs.
94
94
  */
95
95
  export declare function archiveEvidence(directory: string, maxAgeDays: number, maxBundles?: number): Promise<string[]>;
96
+ /**
97
+ * Validates evidence through Zod. Exposed via _internals for DI testing.
98
+ */
99
+ declare function validateEvidence(evidence: Evidence): Evidence;
96
100
  /**
97
101
  * DI seam for testability. Contains all test-mocked exports.
98
102
  * Internal calls should use _internals.fn() instead of fn() directly.
@@ -102,5 +106,6 @@ export declare const _internals: {
102
106
  loadEvidence: typeof loadEvidence;
103
107
  listEvidenceTaskIds: typeof listEvidenceTaskIds;
104
108
  validateProjectRoot: typeof validateProjectRoot;
109
+ validateEvidence: typeof validateEvidence;
105
110
  };
106
111
  export {};
@@ -42,7 +42,7 @@ export type ExecutionDiffResult = {
42
42
  * `maxBytes`.
43
43
  */
44
44
  declare function computeExecutionDiff(directory: string, maxBytes: number): Promise<ExecutionDiffResult>;
45
- declare function dispatchReviewer(directory: string, prompt: string, agentName: string, timeoutMs: number): Promise<string>;
45
+ declare function dispatchReviewer(directory: string, prompt: string, agentName: string, timeoutMs: number, parentSessionId: string): Promise<string>;
46
46
  export interface AutoReviewRunInput {
47
47
  directory: string;
48
48
  sessionID: string;
@@ -1,4 +1,6 @@
1
1
  /** One-time migration from .swarm/context.md → .swarm/knowledge.jsonl for existing projects. */
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
4
  import { appendKnowledge } from './knowledge-store.js';
3
5
  import type { KnowledgeCategory, KnowledgeConfig } from './knowledge-types.js';
4
6
  export interface MigrationResult {
@@ -32,6 +34,11 @@ export declare const _internals: {
32
34
  inferProjectName: typeof inferProjectName;
33
35
  writeSentinel: typeof writeSentinel;
34
36
  resolveLegacyHiveKnowledgePath: typeof resolveLegacyHiveKnowledgePath;
37
+ existsSync: typeof existsSync;
38
+ readFileSync: typeof readFileSync;
39
+ readFile: typeof readFile;
40
+ mkdir: typeof mkdir;
41
+ writeFile: typeof writeFile;
35
42
  };
36
43
  export declare function migrateContextToKnowledge(directory: string, config: KnowledgeConfig): Promise<MigrationResult>;
37
44
  /**