opencode-swarm 7.121.0 → 7.121.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 (37) hide show
  1. package/.opencode/skills/commit-pr/SKILL.md +4 -0
  2. package/.opencode/skills/issue-ingest/SKILL.md +9 -7
  3. package/dist/cli/{curation-policy-re2kaysb.js → curation-policy-10wnhpwn.js} +2 -2
  4. package/dist/cli/{curator-llm-factory-jta9tggm.js → curator-llm-factory-vcyvbny0.js} +10 -10
  5. package/dist/cli/{curator-8m0pf9cz.js → curator-mwvrryf3.js} +10 -10
  6. package/dist/cli/{evidence-summary-service-jtx5q3r6.js → evidence-summary-service-k14ykm03.js} +2 -2
  7. package/dist/cli/{guardrail-explain-nsfm5ksb.js → guardrail-explain-tgkk87z5.js} +11 -11
  8. package/dist/cli/{hive-promoter-8303gnfk.js → hive-promoter-z4tw1gfv.js} +10 -10
  9. package/dist/cli/{index-rywkdacj.js → index-0f800qg7.js} +3 -3
  10. package/dist/cli/{index-3je67dcz.js → index-1y03193n.js} +1 -1
  11. package/dist/cli/{index-3ygv9535.js → index-2ygp3qq4.js} +11 -11
  12. package/dist/cli/{index-nhgw7de8.js → index-35xyj7gn.js} +1 -1
  13. package/dist/cli/{index-69ms9ny1.js → index-5a3xsnzg.js} +1 -1
  14. package/dist/cli/{index-3366gs26.js → index-db82jxt7.js} +2 -2
  15. package/dist/cli/{index-j5xv8zbp.js → index-emkb0bbe.js} +1 -1
  16. package/dist/cli/{index-0k4r8111.js → index-ft6jw816.js} +4 -4
  17. package/dist/cli/{index-0e1v02hq.js → index-j8349ac0.js} +1 -1
  18. package/dist/cli/{index-0mbmfmt0.js → index-qt2yaghg.js} +2 -1
  19. package/dist/cli/{index-fndvr7fr.js → index-rbfq77sz.js} +1 -1
  20. package/dist/cli/{index-78b192v7.js → index-x4fwtd59.js} +3 -3
  21. package/dist/cli/{index-gmc34yzt.js → index-y0qa3xf0.js} +770 -669
  22. package/dist/cli/index.js +10 -10
  23. package/dist/cli/{knowledge-escalator-0ejqs8c9.js → knowledge-escalator-8ajq1fhb.js} +3 -3
  24. package/dist/cli/{knowledge-events-wx9e37ba.js → knowledge-events-2sjy3aa7.js} +1 -1
  25. package/dist/cli/{knowledge-store-rnajx33a.js → knowledge-store-27cesthc.js} +1 -1
  26. package/dist/cli/{knowledge-validator-1f4mkzmc.js → knowledge-validator-9keh3p8j.js} +4 -4
  27. package/dist/cli/{scan-cursor-qapm0sxj.js → scan-cursor-qmsmmg1b.js} +2 -2
  28. package/dist/cli/{skill-generator-tkv7s12g.js → skill-generator-hkyxdc3j.js} +5 -5
  29. package/dist/commands/issue.d.ts +12 -0
  30. package/dist/config/plan-schema.d.ts +3 -0
  31. package/dist/hooks/delegation-gate.d.ts +120 -0
  32. package/dist/hooks/issue-trace-reducer.d.ts +66 -0
  33. package/dist/hooks/issue-trace-state.d.ts +64 -0
  34. package/dist/hooks/issue-trace.d.ts +31 -0
  35. package/dist/index.js +258 -232
  36. package/dist/tools/save-plan.d.ts +2 -0
  37. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -7,28 +7,28 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-gmc34yzt.js";
10
+ } from "./index-y0qa3xf0.js";
11
11
  import"./index-kyvg2cmp.js";
12
12
  import"./index-vqcgmy4y.js";
13
- import"./index-j5xv8zbp.js";
13
+ import"./index-emkb0bbe.js";
14
14
  import"./index-4905hd2m.js";
15
15
  import"./index-nprawcc0.js";
16
- import"./index-78b192v7.js";
17
- import"./index-0k4r8111.js";
18
- import"./index-0e1v02hq.js";
16
+ import"./index-x4fwtd59.js";
17
+ import"./index-ft6jw816.js";
18
+ import"./index-j8349ac0.js";
19
19
  import"./index-vxv732ex.js";
20
20
  import"./index-c8s9a3zh.js";
21
21
  import"./index-816rkp4y.js";
22
22
  import"./index-22e127jw.js";
23
- import"./index-3366gs26.js";
24
- import"./index-69ms9ny1.js";
23
+ import"./index-db82jxt7.js";
24
+ import"./index-5a3xsnzg.js";
25
25
  import"./index-rtry5xyf.js";
26
- import"./index-rywkdacj.js";
27
- import"./index-nhgw7de8.js";
26
+ import"./index-0f800qg7.js";
27
+ import"./index-35xyj7gn.js";
28
28
  import"./index-ae75rja9.js";
29
29
  import"./index-3jvqw5hy.js";
30
30
  import"./index-9xpgytj9.js";
31
- import"./index-0mbmfmt0.js";
31
+ import"./index-qt2yaghg.js";
32
32
  import"./index-gnp9x6se.js";
33
33
  import {
34
34
  DEFAULT_AGENT_CONFIGS
@@ -8,9 +8,9 @@ import {
8
8
  maybeEscalateOnViolation,
9
9
  maybeQuarantineOnContradiction,
10
10
  readRecentEscalations
11
- } from "./index-0k4r8111.js";
12
- import"./index-rywkdacj.js";
13
- import"./index-nhgw7de8.js";
11
+ } from "./index-ft6jw816.js";
12
+ import"./index-0f800qg7.js";
13
+ import"./index-35xyj7gn.js";
14
14
  import"./index-ae75rja9.js";
15
15
  import"./index-3jvqw5hy.js";
16
16
  import"./index-4rhcth7a.js";
@@ -25,7 +25,7 @@ import {
25
25
  resolveKnowledgeCounterBaselinePath,
26
26
  resolveKnowledgeEventsPath,
27
27
  resolveLegacyApplicationLogPath
28
- } from "./index-nhgw7de8.js";
28
+ } from "./index-35xyj7gn.js";
29
29
  import"./index-ae75rja9.js";
30
30
  import"./index-3jvqw5hy.js";
31
31
  import"./index-zgwm4ryv.js";
@@ -40,7 +40,7 @@ import {
40
40
  transactKnowledge,
41
41
  transactKnowledgeWithCas,
42
42
  wordBigrams
43
- } from "./index-rywkdacj.js";
43
+ } from "./index-0f800qg7.js";
44
44
  import {
45
45
  resolveHiveDataDir
46
46
  } from "./index-ae75rja9.js";
@@ -23,11 +23,11 @@ import {
23
23
  validateLesson,
24
24
  validateSkillCandidatePath,
25
25
  validateSkillPath
26
- } from "./index-3366gs26.js";
27
- import"./index-69ms9ny1.js";
26
+ } from "./index-db82jxt7.js";
27
+ import"./index-5a3xsnzg.js";
28
28
  import"./index-rtry5xyf.js";
29
- import"./index-rywkdacj.js";
30
- import"./index-nhgw7de8.js";
29
+ import"./index-0f800qg7.js";
30
+ import"./index-35xyj7gn.js";
31
31
  import"./index-ae75rja9.js";
32
32
  import"./index-3jvqw5hy.js";
33
33
  import"./index-4rhcth7a.js";
@@ -4,8 +4,8 @@ import {
4
4
  alreadyCuratedThisGeneration,
5
5
  claimNextScanBatch,
6
6
  getScanStatus
7
- } from "./index-0e1v02hq.js";
8
- import"./index-rywkdacj.js";
7
+ } from "./index-j8349ac0.js";
8
+ import"./index-0f800qg7.js";
9
9
  import"./index-ae75rja9.js";
10
10
  import"./index-3jvqw5hy.js";
11
11
  import"./index-4rhcth7a.js";
@@ -33,12 +33,12 @@ import {
33
33
  sanitizeSlug,
34
34
  selectCandidateEntries,
35
35
  writeEvalStub
36
- } from "./index-78b192v7.js";
37
- import"./index-3366gs26.js";
38
- import"./index-69ms9ny1.js";
36
+ } from "./index-x4fwtd59.js";
37
+ import"./index-db82jxt7.js";
38
+ import"./index-5a3xsnzg.js";
39
39
  import"./index-rtry5xyf.js";
40
- import"./index-rywkdacj.js";
41
- import"./index-nhgw7de8.js";
40
+ import"./index-0f800qg7.js";
41
+ import"./index-35xyj7gn.js";
42
42
  import"./index-ae75rja9.js";
43
43
  import"./index-3jvqw5hy.js";
44
44
  import"./index-4rhcth7a.js";
@@ -10,4 +10,16 @@
10
10
  * --no-repro → appends noRepro=true to emitted signal
11
11
  * no args → returns usage string (no throw)
12
12
  */
13
+ import * as fs from 'node:fs';
14
+ /**
15
+ * DI seam for filesystem operations — allows tests to override synchronous fs calls.
16
+ */
17
+ export declare const _internals: {
18
+ writeFileSync: typeof fs.writeFileSync;
19
+ mkdirSync: typeof fs.mkdirSync;
20
+ renameSync: typeof fs.renameSync;
21
+ unlinkSync: typeof fs.unlinkSync;
22
+ readFileSync: typeof fs.readFileSync;
23
+ existsSync: typeof fs.existsSync;
24
+ };
13
25
  export declare function handleIssueCommand(directory: string, args: string[]): string;
@@ -69,6 +69,7 @@ export declare const TaskSchema: z.ZodObject<{
69
69
  files_touched: z.ZodDefault<z.ZodArray<z.ZodString>>;
70
70
  evidence_path: z.ZodOptional<z.ZodString>;
71
71
  blocked_reason: z.ZodOptional<z.ZodString>;
72
+ fr_refs: z.ZodOptional<z.ZodArray<z.ZodString>>;
72
73
  }, z.core.$strip>;
73
74
  export type Task = z.infer<typeof TaskSchema>;
74
75
  export declare const PhaseSchema: z.ZodObject<{
@@ -103,6 +104,7 @@ export declare const PhaseSchema: z.ZodObject<{
103
104
  files_touched: z.ZodDefault<z.ZodArray<z.ZodString>>;
104
105
  evidence_path: z.ZodOptional<z.ZodString>;
105
106
  blocked_reason: z.ZodOptional<z.ZodString>;
107
+ fr_refs: z.ZodOptional<z.ZodArray<z.ZodString>>;
106
108
  }, z.core.$strip>>>;
107
109
  type: z.ZodOptional<z.ZodEnum<{
108
110
  code: "code";
@@ -148,6 +150,7 @@ export declare const PlanSchema: z.ZodObject<{
148
150
  files_touched: z.ZodDefault<z.ZodArray<z.ZodString>>;
149
151
  evidence_path: z.ZodOptional<z.ZodString>;
150
152
  blocked_reason: z.ZodOptional<z.ZodString>;
153
+ fr_refs: z.ZodOptional<z.ZodArray<z.ZodString>>;
151
154
  }, z.core.$strip>>>;
152
155
  type: z.ZodOptional<z.ZodEnum<{
153
156
  code: "code";
@@ -66,6 +66,20 @@ interface ValidationContext {
66
66
  /**
67
67
  * Validates a DelegationEnvelope against the current plan and agent list.
68
68
  * Returns { valid: true } on success, or { valid: false; reason: string } on failure.
69
+ *
70
+ * NOTE (#1687, kept SEPARATE from {@link validateCoderReviewerAcceptanceField}):
71
+ * this validator operates on a *structured* `DelegationEnvelope` object recovered
72
+ * from the OLD `KEY:VALUE` envelope format (`taskId:`/`targetAgent:`/`action:`/
73
+ * `commandType:`/`files:`/`acceptanceCriteria:`) and is consumed ADVISORY-ONLY,
74
+ * POST-execution via {@link appendDelegationEnvelopeAdvisory} in `toolAfter` (it
75
+ * never throws). The free-text coder/reviewer format
76
+ * (`TASK:`/`FILE:`/`ACCEPTANCE:`) does NOT parse into this shape
77
+ * (`parseDelegationEnvelope` returns `null`), so this path never fires on a real
78
+ * coder/reviewer dispatch. The pre-dispatch, BLOCKING acceptance-field gate for
79
+ * the free-text format lives in {@link validateCoderReviewerAcceptanceField};
80
+ * the two are intentionally not merged (advisory-vs-blocking, structured-vs-
81
+ * free-text are different contracts). Do not delete this function — it retains a
82
+ * real purpose for the old envelope format and other delegation types.
69
83
  */
70
84
  export declare function validateDelegationEnvelope(envelope: unknown, context: ValidationContext): EnvelopeValidationResult;
71
85
  /**
@@ -87,6 +101,107 @@ export declare function validateDelegationEnvelope(envelope: unknown, context: V
87
101
  * the side effect (advisory push) is the production purpose.
88
102
  */
89
103
  export declare function appendDelegationEnvelopeAdvisory(session: AgentSessionState, promptText: string, context: ValidationContext): EnvelopeValidationResult | null;
104
+ /**
105
+ * FR-003 / SC-003 / SC-004 (issue #1687): pre-dispatch acceptance-criteria
106
+ * enforcement for **coder and reviewer** delegations, operating on the REAL
107
+ * free-text delegation prompt (`TASK:`/`FILE:`/`INPUT:`/`OUTPUT:`/`CONSTRAINT:`/
108
+ * `ACCEPTANCE:`/`SKILLS:`) the architect actually sends via the native Task tool.
109
+ *
110
+ * Deliberately kept SEPARATE from {@link validateDelegationEnvelope} (see the
111
+ * sibling note there), NOT a rename/reuse of it, because the semantics differ on
112
+ * two axes: this function parses the FREE-TEXT `ACCEPTANCE:` line (not a
113
+ * structured envelope object) and is consumed as a BLOCKING, PRE-execution
114
+ * (`toolBefore`) gate (not the advisory, post-execution `toolAfter` path that
115
+ * `validateDelegationEnvelope` feeds via {@link appendDelegationEnvelopeAdvisory}).
116
+ * The two formats never overlap — `parseDelegationEnvelope` returns `null` for
117
+ * the free-text coder/reviewer format — so merging them would conflate
118
+ * advisory-vs-blocking and structured-vs-free-text contracts. M15's advisory
119
+ * mechanism is left fully intact for the old envelope format and other
120
+ * delegation types.
121
+ *
122
+ * A dispatch passes when an `ACCEPTANCE:` line exists AND the field carries
123
+ * non-empty, non-whitespace content — either INLINE on the header line (the
124
+ * architect-instructed same-line format) OR on the lines that FOLLOW a bare
125
+ * `ACCEPTANCE:` header, up to (but excluding) the next INPUT-FORMAT field header
126
+ * (`^[A-Z][A-Z0-9_]*:`, e.g. `SKILLS:`) or end of input. The multi-line form is
127
+ * a plausible way to paste a verbatim FR/SC body that spans lines (PR #1864
128
+ * review feedback), so treating a bare header + following content as EMPTY would
129
+ * false-block a good-faith copy. The next-field-header terminator ensures
130
+ * content belonging to the NEXT field (e.g. `SKILLS: none`) is never miscounted
131
+ * as ACCEPTANCE content — an empty `ACCEPTANCE:` immediately followed by another
132
+ * field is still correctly rejected as empty. Prompts are split on `\r?\n` so a
133
+ * trailing `\r` (CRLF-authored prompts) never leaks into the captured content.
134
+ *
135
+ * Only NON-EMPTINESS is enforced here; verbatim-coverage of the mapped FR/SC
136
+ * bodies is a SEPARATE, later check ({@link checkAcceptanceCoversFrRefs}) that
137
+ * already newline-normalizes the whole prompt, so multi-line ACCEPTANCE content
138
+ * is visible to it regardless.
139
+ *
140
+ * @param promptText the assembled delegation prompt (all text-bearing Task args
141
+ * joined).
142
+ * @returns `{ valid: true }` when the ACCEPTANCE field is present and non-empty;
143
+ * otherwise `{ valid: false, reason }` with a diagnosable reason the caller
144
+ * turns into a blocking, pre-dispatch error.
145
+ */
146
+ export declare function validateCoderReviewerAcceptanceField(promptText: string): {
147
+ valid: boolean;
148
+ reason?: 'acceptance_field_missing' | 'acceptance_field_empty';
149
+ };
150
+ /**
151
+ * FR-001/FR-002/FR-005/SC-001/SC-002/SC-006 (issue #1687): pull the verbatim
152
+ * requirement BODY for a single spec id (`FR-###` / `SC-###`) out of `spec.md`.
153
+ *
154
+ * spec.md requirements are bold-prefixed bullets, one per (possibly wrapped)
155
+ * bullet, e.g.
156
+ * `- **FR-000 — Structured task-to-FR mapping.** The plan task model SHALL ...`
157
+ * `- **SC-000 (FR-000).** Given a plan ..., then ...`
158
+ * The requirement BODY is the text AFTER the FIRST closing `**` of the leading
159
+ * bold span — i.e. everything past the `**...**` id/title prefix. Handles both
160
+ * FR-### and SC-### uniformly (no special-casing) and stitches on any
161
+ * immediately-following continuation lines of a wrapped bullet until the next
162
+ * bullet / next FR-|SC- bullet / a blank line / a markdown heading.
163
+ *
164
+ * @returns the raw (untrimmed) body string, or `null` when the id is not present
165
+ * in the spec (unknown/typo/renamed) — the caller treats null as fail-open skip.
166
+ */
167
+ export declare function extractSpecRequirementBodyById(specText: string, id: string): string | null;
168
+ /**
169
+ * FR-001/FR-002 (issue #1687): symmetric normalization applied identically to
170
+ * BOTH the extracted spec body AND the ACCEPTANCE prompt text before the
171
+ * substring-coverage compare. Asymmetric normalization would false-block, so this
172
+ * is deliberately the single shared normalizer. Strips markdown emphasis/code
173
+ * markers and leading list markers, folds punctuation the architect's LLM copy
174
+ * routinely substitutes (dash-width, curly-vs-straight quotes) so a good-faith
175
+ * verbatim copy is not false-blocked on a `—`→`--` or `'`→`'` swap, collapses
176
+ * ALL whitespace (incl. newlines) to single spaces, trims, and lowercases. Every
177
+ * fold is symmetric (both sides go through this one function), so it can only
178
+ * REDUCE false-blocks — it never opens a bypass, since covered/uncovered still
179
+ * differ by whole words, not punctuation.
180
+ */
181
+ export declare function normalizeAcceptanceText(s: string): string;
182
+ /**
183
+ * FR-001/FR-002/FR-005/SC-001/SC-002/SC-006 (issue #1687): mechanical coverage
184
+ * check that the ACCEPTANCE text CONTAINS the verbatim requirement body for each
185
+ * mapped spec id. Fail-open by construction:
186
+ * - an id not present in spec.md (unknown/typo/renamed) is SKIPPED, never blocked;
187
+ * - an id whose body normalizes to empty is SKIPPED (neither trivially covered
188
+ * nor a miss);
189
+ * - each id is checked INDEPENDENTLY (per-id substring), because the architect
190
+ * concatenates multiple requirement bodies with an UNSPECIFIED separator — a
191
+ * whole-string equality compare would false-block multi-FR tasks.
192
+ *
193
+ * @returns `{ covered: true }` when every id is present-and-covered or skipped;
194
+ * `{ covered: false, missingId }` naming the FIRST id whose body is not a
195
+ * substring of the ACCEPTANCE text.
196
+ */
197
+ export declare function checkAcceptanceCoversFrRefs(params: {
198
+ acceptanceText: string;
199
+ frRefs: string[];
200
+ specText: string;
201
+ }): {
202
+ covered: boolean;
203
+ missingId?: string;
204
+ };
90
205
  interface MessageInfo {
91
206
  role: string;
92
207
  agent?: string;
@@ -101,6 +216,11 @@ interface MessageWithParts {
101
216
  info: MessageInfo;
102
217
  parts: MessagePart[];
103
218
  }
219
+ /**
220
+ * Returns whether the plan in the given directory has a valid plan-critic
221
+ * approval. Does not throw — returns `false` for any failure (fail-closed).
222
+ */
223
+ export declare function isPlanCriticApproved(directory: string): Promise<boolean>;
104
224
  declare function resolveDelegatedPlanTaskId(args: Record<string, unknown>, knownPlanTaskIds?: ReadonlySet<string>): string | null;
105
225
  /**
106
226
  * Parses structured per-task verdict lines from agent dispatch output.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Issue trace reducer — a pure transition function with zero I/O.
3
+ *
4
+ * Determines the next mode transition for an issue-trace workflow
5
+ * based on current trace state and workflow artifacts. Evaluated
6
+ * top-to-bottom (first-match-wins) against an 8-row decision table.
7
+ */
8
+ export interface IssueReference {
9
+ url: string;
10
+ owner: string;
11
+ repo: string;
12
+ number: number;
13
+ timestamp: string;
14
+ flags: {
15
+ plan?: boolean;
16
+ trace?: boolean;
17
+ noRepro?: boolean;
18
+ };
19
+ noReproWaiver?: {
20
+ waived: boolean;
21
+ reason: string;
22
+ timestamp: string;
23
+ };
24
+ }
25
+ export interface TraceState {
26
+ issueNumber: number;
27
+ lastTransition: string | null;
28
+ completed: boolean;
29
+ }
30
+ export interface WorkflowArtifacts {
31
+ specExists: boolean;
32
+ specIssueNumber: number | null;
33
+ planExists: boolean;
34
+ criticApproved: boolean;
35
+ allPhasesComplete: boolean;
36
+ }
37
+ export interface TransitionResult {
38
+ nextMode: string | null;
39
+ directive: string | null;
40
+ nextLastTransition: string | null;
41
+ nextCompleted: boolean;
42
+ }
43
+ export interface ComputeNextModeParams {
44
+ issueReference: IssueReference | null;
45
+ traceState: TraceState;
46
+ workflowArtifacts: WorkflowArtifacts;
47
+ }
48
+ /**
49
+ * Pure reducer: given trace state + workflow artifacts, return the
50
+ * next mode transition (or a no-op).
51
+ *
52
+ * Decision table (top-to-bottom, first match wins):
53
+ * (a) No issue reference or trace not requested → no-op
54
+ * (b) Trace already completed → no-op
55
+ * (c) Cross-issue guard (spec issue ≠ current issue) → no-op
56
+ * (d) Spec does not exist → no-op
57
+ * (e) Spec exists, no plan, never transitioned → PLAN
58
+ * (f) Plan exists but critic not approved → no-op
59
+ * (g) Critic approved, phases incomplete, not yet PLAN_TO_EXECUTE → EXECUTE
60
+ * (h) All phases complete, not yet EXECUTE_TO_COMMIT → COMMIT directive
61
+ *
62
+ * Idempotency: rows (e), (g), (h) return no-op when
63
+ * `traceState.lastTransition` already equals the target
64
+ * transition value.
65
+ */
66
+ export declare function computeNextMode(params: ComputeNextModeParams): TransitionResult;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Issue trace state adapter — reads and writes all artifacts for the
3
+ * issue-trace workflow engine.
4
+ *
5
+ * Uses the `_internals` DI seam pattern (AGENTS.md invariant 7) so
6
+ * tests can override filesystem calls without `mock.module` leakage.
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
9
+ import type { Plan } from '../config/plan-schema';
10
+ import type { IssueReference, TraceState } from './issue-trace-reducer';
11
+ declare function _defaultLoadPlanFromLedger(directory: string): Promise<Plan | null>;
12
+ export declare const _internals: {
13
+ readFileSync: typeof readFileSync;
14
+ writeFileSync: typeof writeFileSync;
15
+ existsSync: typeof existsSync;
16
+ renameSync: typeof renameSync;
17
+ mkdirSync: typeof mkdirSync;
18
+ unlinkSync: typeof unlinkSync;
19
+ loadPlanFromLedger: typeof _defaultLoadPlanFromLedger;
20
+ };
21
+ /**
22
+ * Reads `.swarm/issue-reference.json` and returns the parsed object,
23
+ * or null if the file is absent or malformed.
24
+ */
25
+ export declare function readIssueReference(directory: string): IssueReference | null;
26
+ /**
27
+ * Reads `.swarm/issue-trace-state.json` and returns the parsed state,
28
+ * or a default `{issueNumber:0, lastTransition:null, completed:false}`
29
+ * if the file is absent or malformed.
30
+ */
31
+ export declare function readTraceState(directory: string): TraceState;
32
+ /**
33
+ * Writes `.swarm/issue-trace-state.json` atomically: creates a temp file
34
+ * inside `.swarm/`, writes JSON, then renames. Cleans up the temp on failure.
35
+ */
36
+ export declare function writeTraceState(directory: string, state: TraceState): void;
37
+ /**
38
+ * Reads `.swarm/spec.md` and extracts the issue number from the
39
+ * `## Source Issue` section. Looks for `- Number: N` or `- URL: ...issues/N`
40
+ * under the heading. Returns null if the section is absent or the number
41
+ * is unparseable.
42
+ */
43
+ export declare function readSpecIssueNumber(directory: string): number | null;
44
+ /**
45
+ * Loads the plan from the ledger-aware plan loader.
46
+ * Async because the underlying ledger loader is async.
47
+ */
48
+ export declare function loadPlanFromLedger(directory: string): Promise<Plan | null>;
49
+ /**
50
+ * Checks whether all phases in the plan have a completed status.
51
+ * Returns `{allComplete: false}` if the plan is null.
52
+ */
53
+ export declare function readPlanPhaseStatus(directory: string): Promise<{
54
+ allComplete: boolean;
55
+ }>;
56
+ /**
57
+ * Returns whether `.swarm/spec.md` exists.
58
+ */
59
+ export declare function specExists(directory: string): boolean;
60
+ /**
61
+ * Returns whether `.swarm/plan.json` exists.
62
+ */
63
+ export declare function planExists(directory: string): boolean;
64
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Issue trace hook — the deterministic trace transition engine.
3
+ *
4
+ * Ties together the reducer (issue-trace-reducer), the state adapter
5
+ * (issue-trace-state), and the approval helper (delegation-gate) into
6
+ * a single `messagesTransform` hook that the composeHandlers chain
7
+ * calls on every architect message cycle.
8
+ *
9
+ * Uses the `_internals` DI seam pattern (AGENTS.md invariant 7) so
10
+ * tests can override adapter functions without `mock.module` leakage.
11
+ */
12
+ import { isPlanCriticApproved } from './delegation-gate';
13
+ import { planExists, readIssueReference, readPlanPhaseStatus, readSpecIssueNumber, readTraceState, specExists, writeTraceState } from './issue-trace-state';
14
+ export declare const _internals: {
15
+ readIssueReference: typeof readIssueReference;
16
+ readTraceState: typeof readTraceState;
17
+ writeTraceState: typeof writeTraceState;
18
+ readSpecIssueNumber: typeof readSpecIssueNumber;
19
+ readPlanPhaseStatus: typeof readPlanPhaseStatus;
20
+ specExists: typeof specExists;
21
+ planExists: typeof planExists;
22
+ isPlanCriticApproved: typeof isPlanCriticApproved;
23
+ };
24
+ export declare function createIssueTraceHook(_config: unknown, directory: string, approvalTimeoutMs?: number): {
25
+ messagesTransform: (input: unknown, output: unknown) => Promise<void>;
26
+ };
27
+ /**
28
+ * Reset the approval cache. Called by tests in afterEach to avoid
29
+ * cross-test pollution from the module-level cache.
30
+ */
31
+ export declare function resetApprovalCache(): void;