opencode-swarm 7.189.0 → 7.190.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -6
- package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
- package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
- package/dist/agents/architect.d.ts +1 -1
- package/dist/background/completion-observer.d.ts +6 -0
- package/dist/cli/{auto-checkpoint-m7c4x8bk.js → auto-checkpoint-26m3h5f4.js} +9 -9
- package/dist/cli/{checkpoint-m7q9886q.js → checkpoint-7a0achpv.js} +18 -20
- package/dist/cli/{coder-settlement-hh81z2f7.js → coder-settlement-dsb1t39t.js} +13 -13
- package/dist/cli/{config-doctor-0rexy3xt.js → config-doctor-9txawzng.js} +23 -20
- package/dist/cli/{core-ays6stz4.js → core-kv7jz9ty.js} +4 -4
- package/dist/cli/{curation-policy-65ckmmd5.js → curation-policy-6p6fcfjb.js} +6 -6
- package/dist/cli/{curator-dfzkfgs0.js → curator-26yzg8wp.js} +44 -44
- package/dist/cli/{curator-drift-nts5atks.js → curator-drift-kxn5sk3z.js} +9 -9
- package/dist/cli/curator-llm-factory-hs2y9kmw.js +82 -0
- package/dist/cli/{doctor-knowledge-receipt-phase-id-ec40k5hn.js → doctor-knowledge-receipt-phase-id-z8wea03e.js} +9 -9
- package/dist/cli/{evidence-summary-service-vgtm72g5.js → evidence-summary-service-bg5zq78s.js} +21 -22
- package/dist/cli/{gate-evidence-vmn65kxe.js → gate-evidence-0beg9c7r.js} +8 -8
- package/dist/cli/guardrail-explain-n8bm0hpm.js +83 -0
- package/dist/cli/{guardrail-log-hc8fjm4x.js → guardrail-log-r2zazmbn.js} +8 -8
- package/dist/cli/{guardrail-reset-ct00w300.js → guardrail-reset-0q54t44w.js} +44 -44
- package/dist/cli/{hive-promoter-2xax6van.js → hive-promoter-7dr5b9zv.js} +44 -44
- package/dist/cli/{index-9gqb5nkj.js → index-0gve0mkj.js} +4 -4
- package/dist/cli/{index-34vsyj15.js → index-0yrg10jx.js} +5 -5
- package/dist/cli/{index-mfxef20k.js → index-0z8vdkvg.js} +4 -4
- package/dist/cli/{index-2nexf5vz.js → index-1f084tqn.js} +22223 -15395
- package/dist/cli/{index-qx3g1bbe.js → index-26e2s6r1.js} +1 -1
- package/dist/cli/{index-kqegvngt.js → index-2c47efpt.js} +4 -4
- package/dist/cli/{index-9tvjwv01.js → index-2fpv2wmc.js} +1 -1
- package/dist/cli/{index-8q8783g0.js → index-3zwhxk98.js} +1 -1
- package/dist/cli/{index-9j3ybyfs.js → index-4a15347d.js} +7 -7
- package/dist/cli/{index-5cfhwd65.js → index-67a6mk20.js} +2 -2
- package/dist/cli/{index-d3rjvm9e.js → index-7vnr5cmp.js} +2 -2
- package/dist/cli/{index-twb3f4tk.js → index-8bx6vhzk.js} +1 -1
- package/dist/cli/{index-0gejy4cx.js → index-ay8ggtgr.js} +2 -2
- package/dist/cli/{index-8xabhfwr.js → index-b2j231jy.js} +2 -2
- package/dist/cli/{index-cbqkag7q.js → index-bcwmn4d7.js} +3 -3
- package/dist/cli/{index-fbgjj7h5.js → index-bfyh3gpe.js} +101 -13
- package/dist/cli/{index-skdpgjar.js → index-cewpxcwm.js} +862 -1221
- package/dist/cli/{index-bzbxn24k.js → index-dbdkh7pq.js} +5 -5
- package/dist/cli/{index-w4zscz6p.js → index-dnp4qtpf.js} +5 -5
- package/dist/cli/{index-waxvpwbv.js → index-efztf4yw.js} +127 -13
- package/dist/cli/{index-4p3jh14t.js → index-enq166kw.js} +4 -4
- package/dist/cli/{index-g03m5jhw.js → index-f46dbyew.js} +3 -3
- package/dist/cli/{index-ntda6tje.js → index-feaxsjrt.js} +10 -2
- package/dist/cli/{index-89f56ej4.js → index-fthhppnc.js} +46 -46
- package/dist/cli/{index-wqa31xnf.js → index-gqb8etv4.js} +7 -7
- package/dist/cli/{index-1cwmj1gs.js → index-ha0w0a7t.js} +77 -16
- package/dist/cli/{index-nhsafbrh.js → index-hx1fmywk.js} +5 -5
- package/dist/cli/{index-kaqdzhj3.js → index-kfevm0n5.js} +2 -2
- package/dist/cli/{index-z29hksyw.js → index-m28dzyy8.js} +26 -10
- package/dist/cli/{index-x1eh2636.js → index-mcv0v2dp.js} +1 -1
- package/dist/cli/{index-9czp2dcg.js → index-mjfpaxw3.js} +7 -7
- package/dist/cli/{index-f6exny9m.js → index-n8q43eff.js} +2 -2
- package/dist/cli/{index-7hr5m7yp.js → index-nkgzkt7c.js} +1 -1
- package/dist/cli/{index-p2tqzwh6.js → index-qs9myayh.js} +5 -10
- package/dist/cli/{index-hh7pa3yk.js → index-r9yvpr2m.js} +2 -2
- package/dist/cli/{index-cdjxzs2r.js → index-rjzn7hf0.js} +7 -7
- package/dist/cli/{index-zfbxfm2m.js → index-s8b67e6h.js} +33 -12
- package/dist/cli/{index-bskex45z.js → index-s9cgnd2z.js} +2 -2
- package/dist/cli/{index-cj4mp0fm.js → index-sc7t1tev.js} +6 -6
- package/dist/cli/{index-3dx0c7ve.js → index-sfjpbnqs.js} +44 -44
- package/dist/cli/{index-16m2m4ah.js → index-spgfs5y7.js} +1 -1
- package/dist/cli/{index-q9kefh33.js → index-sxj143a4.js} +1 -1
- package/dist/cli/{index-zsrtt42s.js → index-v3bmwmqw.js} +25 -1
- package/dist/cli/{index-5v09zw1q.js → index-v3j9fk16.js} +18 -8
- package/dist/cli/{index-2btybj8w.js → index-xdq6mak1.js} +4 -4
- package/dist/cli/{index-k8sarme1.js → index-y9v41185.js} +1 -1
- package/dist/cli/{index-4a7j5f60.js → index-yy5yyyst.js} +4 -4
- package/dist/cli/{index-1xa8wq76.js → index-z3ahy9c2.js} +2 -2
- package/dist/cli/{index-wrdktgpy.js → index-zk6ttjkt.js} +2 -2
- package/dist/cli/index.js +45 -45
- package/dist/cli/{knowledge-escalator-v3c8w86g.js → knowledge-escalator-hdrqt5pj.js} +15 -15
- package/dist/cli/{knowledge-events-kagaccj9.js → knowledge-events-ttebc8gn.js} +13 -13
- package/dist/cli/{knowledge-link-h4pdcnya.js → knowledge-link-6hw3z6wn.js} +5 -5
- package/dist/cli/{knowledge-store-ywxjk5pb.js → knowledge-store-8cxv6w2n.js} +6 -6
- package/dist/cli/{knowledge-validator-210gkpec.js → knowledge-validator-kr5xmqpa.js} +8 -8
- package/dist/cli/{mcp-58hgx6av.js → mcp-jtk79ygp.js} +1 -1
- package/dist/cli/{model-preflight-e69b48n0.js → model-preflight-d6tt7479.js} +44 -44
- package/dist/cli/{pending-delegations-g24d3qa9.js → pending-delegations-ttk09f50.js} +12 -12
- package/dist/cli/{pr-subscriptions-qprh2zsm.js → pr-subscriptions-2kbeezsr.js} +12 -12
- package/dist/cli/{pr-workflow-gate-pj7dhkv4.js → pr-workflow-gate-3ra6p7k4.js} +44 -44
- package/dist/cli/{runner-4hfmp1ag.js → runner-ssnsje35.js} +6 -6
- package/dist/cli/{scan-cursor-s5wsxxd1.js → scan-cursor-zzccy117.js} +7 -7
- package/dist/cli/{schema-smgngq9m.js → schema-9prswg1x.js} +14 -2
- package/dist/cli/{scope-persistence-tbv2ar4w.js → scope-persistence-eqm8vwnx.js} +16 -16
- package/dist/cli/{server-7p2ds5w2.js → server-swjre81h.js} +45 -45
- package/dist/cli/{skill-generator-r4hsne71.js → skill-generator-d8dk9vps.js} +17 -17
- package/dist/cli/{snapshot-coordination-init-ydjwh56d.js → snapshot-coordination-init-f49y0zr0.js} +44 -44
- package/dist/cli/{speckit-checkoff-aj75vmjq.js → speckit-checkoff-pnb6q9gd.js} +5 -5
- package/dist/cli/{telemetry-kvhe94gs.js → telemetry-9szfdfqv.js} +1 -1
- package/dist/cli/{workspace-snapshot-30kdf018.js → workspace-snapshot-9f12p1s1.js} +3 -3
- package/dist/cli/{worktree-collision-ownership-0bxj9f0t.js → worktree-collision-ownership-3c7xyc4p.js} +14 -14
- package/dist/cli/{worktree-isolation-h0c22pm2.js → worktree-isolation-jggdf28m.js} +46 -44
- package/dist/commands/coupling.d.ts +21 -4
- package/dist/commands/epic-learning.d.ts +28 -0
- package/dist/commands/epic.d.ts +81 -36
- package/dist/commands/registry.d.ts +8 -8
- package/dist/commands/turbo.d.ts +16 -1
- package/dist/config/constants.d.ts +11 -1
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/schema.d.ts +291 -77
- package/dist/db/coordination-store.d.ts +6 -0
- package/dist/epic/close.d.ts +209 -0
- package/dist/{turbo/epic → epic}/cochange-conflict.d.ts +5 -4
- package/dist/{turbo/epic → epic}/cochange-source.d.ts +10 -1
- package/dist/epic/components.d.ts +238 -0
- package/dist/epic/config-gate.d.ts +25 -0
- package/dist/epic/config.d.ts +101 -0
- package/dist/{turbo/epic → epic}/coupling-report.d.ts +3 -1
- package/dist/epic/declared-scopes.d.ts +37 -0
- package/dist/epic/epic-branch.d.ts +157 -0
- package/dist/epic/gate-policy.d.ts +114 -0
- package/dist/epic/git-once.d.ts +26 -0
- package/dist/epic/learning-store.d.ts +176 -0
- package/dist/epic/learning.d.ts +198 -0
- package/dist/epic/legacy-migration.d.ts +26 -0
- package/dist/epic/lifecycle.d.ts +437 -0
- package/dist/epic/markers.d.ts +123 -0
- package/dist/epic/merge-epoch.d.ts +90 -0
- package/dist/epic/next-wave-format.d.ts +101 -0
- package/dist/epic/next-wave.d.ts +102 -0
- package/dist/epic/phase-readiness.d.ts +221 -0
- package/dist/epic/plan-key.d.ts +104 -0
- package/dist/epic/plan-shaping-seam.d.ts +112 -0
- package/dist/epic/planning-signals.d.ts +38 -0
- package/dist/epic/report.d.ts +152 -0
- package/dist/epic/residue-commit.d.ts +151 -0
- package/dist/epic/scorecard.d.ts +130 -0
- package/dist/epic/shaping-sizing.d.ts +108 -0
- package/dist/epic/shaping-suggestions.d.ts +172 -0
- package/dist/epic/shaping.d.ts +112 -0
- package/dist/epic/sizing.d.ts +60 -0
- package/dist/epic/start.d.ts +187 -0
- package/dist/epic/task-landing.d.ts +83 -0
- package/dist/epic/wave-close.d.ts +112 -0
- package/dist/epic/wave-select.d.ts +95 -0
- package/dist/evaluation/contracts.d.ts +1 -1
- package/dist/evaluation/public-api.d.ts +1 -1
- package/dist/full-auto/policy.d.ts +12 -1
- package/dist/hooks/delegation-gate/worktree-isolation.d.ts +16 -0
- package/dist/hooks/delegation-gate.d.ts +11 -0
- package/dist/hooks/guardrails/index.d.ts +7 -0
- package/dist/hooks/promotion-evidence-store.d.ts +2 -2
- package/dist/index.js +814 -807
- package/dist/plan/manager.d.ts +6 -8
- package/dist/plan/parallel-verdict.d.ts +13 -2
- package/dist/retention/caps.d.ts +1 -1
- package/dist/review/contracts.d.ts +8 -1
- package/dist/services/config-doctor.d.ts +38 -0
- package/dist/session/snapshot-reader.d.ts +2 -0
- package/dist/session/snapshot-writer.d.ts +13 -2
- package/dist/state.d.ts +13 -18
- package/dist/tools/epic-next-wave.d.ts +20 -0
- package/dist/tools/epic-phase-review.d.ts +43 -0
- package/dist/tools/index.d.ts +2 -3
- package/dist/tools/manifest.d.ts +2 -3
- package/dist/tools/phase-complete.d.ts +5 -1
- package/dist/tools/save-plan.d.ts +7 -0
- package/dist/tools/tool-metadata.d.ts +4 -8
- package/dist/worktree/merge.d.ts +14 -1
- package/opencode-swarm.schema.json +273 -28
- package/package.json +1 -1
- package/dist/cli/curator-llm-factory-nrn6n2p1.js +0 -82
- package/dist/cli/guardrail-explain-9jmr8ezt.js +0 -83
- package/dist/tools/epic-plan-waves.d.ts +0 -79
- package/dist/tools/epic-record-divergence.d.ts +0 -73
- package/dist/tools/epic-run-phase.d.ts +0 -179
- package/dist/turbo/epic/activation.d.ts +0 -193
- package/dist/turbo/epic/calibration-engine.d.ts +0 -88
- package/dist/turbo/epic/calibration.d.ts +0 -80
- package/dist/turbo/epic/divergence-recorder.d.ts +0 -123
- package/dist/turbo/epic/index.d.ts +0 -24
- package/dist/turbo/epic/promotion-evidence.d.ts +0 -42
- package/dist/turbo/epic/state.d.ts +0 -69
- package/dist/turbo/epic/task-commit.d.ts +0 -110
- package/dist/turbo/epic/upstream-commits.d.ts +0 -82
- package/dist/turbo/epic/wave-planner.d.ts +0 -83
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan identity for Epic commits (Epic v2 C0, reshaped in C3).
|
|
3
|
+
*
|
|
4
|
+
* Every commit Epic writes for a task — the worktree LANDING merge commit of
|
|
5
|
+
* its coder and the RESIDUE commit of a non-coder writer (test_engineer,
|
|
6
|
+
* docs, …) — has the subject `swarm(task <id>): …` and a final
|
|
7
|
+
* `Swarm-Plan: <planKey>` trailer, where
|
|
8
|
+
* `planKey = sha256(planIdentityHash + '|' + (planEpoch ?? '')).slice(0, 16)`.
|
|
9
|
+
* The plan epoch is minted per ledger root, so two consecutive plans with the
|
|
10
|
+
* same title (every plan has a `1.1`) get different keys. Task ids repeat
|
|
11
|
+
* across plans, so a commit is attributed to a task only when its trailer
|
|
12
|
+
* names the current plan.
|
|
13
|
+
*
|
|
14
|
+
* Predecessor evidence no longer greps history: wave close records each
|
|
15
|
+
* task's commit in the epic record and mirrors it to the ref
|
|
16
|
+
* `refs/swarm/epics/<epicKey>/tasks/<id>` (`markers.ts`). The marker
|
|
17
|
+
* subject + trailer remain the way wave close and `/swarm epic status
|
|
18
|
+
* --repair-refs` find a task's commit inside the epic's own commit range.
|
|
19
|
+
*
|
|
20
|
+
* Records are NUL-separated (`git log -z`); git refuses NUL bytes in commit
|
|
21
|
+
* messages, so a message body cannot forge a record boundary.
|
|
22
|
+
*
|
|
23
|
+
* Subprocess discipline: no git call is made here; readers go through
|
|
24
|
+
* `src/git/branch.ts` `gitExec` (AGENTS.md #3).
|
|
25
|
+
*/
|
|
26
|
+
import type { Plan } from '../config/plan-schema.js';
|
|
27
|
+
import { readLedgerEvents as readLedgerEvents_import, readPlanEpochIdentity as readPlanEpochIdentity_import } from '../plan/ledger.js';
|
|
28
|
+
/** The commit-message trailer key binding a marker to its plan. */
|
|
29
|
+
export declare const SWARM_PLAN_TRAILER_KEY = "Swarm-Plan";
|
|
30
|
+
/** Identity of the plan whose markers may be honored. */
|
|
31
|
+
export interface PlanMarkerScope {
|
|
32
|
+
/** 16-hex-char key written into the `Swarm-Plan:` trailer. */
|
|
33
|
+
planKey: string;
|
|
34
|
+
/**
|
|
35
|
+
* Earliest plan-ledger event time (ms since epoch), or null when the
|
|
36
|
+
* ledger is absent / carries no parseable timestamp.
|
|
37
|
+
*/
|
|
38
|
+
rootTimestampMs: number | null;
|
|
39
|
+
}
|
|
40
|
+
/** One `swarm(task <id>):` commit read back from git. */
|
|
41
|
+
export interface ParsedTaskMarker {
|
|
42
|
+
/** Full commit id (`%H`). */
|
|
43
|
+
sha: string;
|
|
44
|
+
taskId: string;
|
|
45
|
+
/** Last `Swarm-Plan:` trailer value; null when the commit carries none. */
|
|
46
|
+
planKey: string | null;
|
|
47
|
+
}
|
|
48
|
+
/** Subject shape produced by {@link formatEpicTaskCommitMessage}. */
|
|
49
|
+
export declare const SWARM_TASK_SUBJECT_RE: RegExp;
|
|
50
|
+
/**
|
|
51
|
+
* `git log -z --format` producing `<sha> US <raw message>` per commit,
|
|
52
|
+
* records separated by NUL (`-z`).
|
|
53
|
+
*/
|
|
54
|
+
export declare const MARKER_LOG_FORMAT = "--format=%H%x1f%B";
|
|
55
|
+
/** Bound on scope resolution (ledger reads) so status never hangs. */
|
|
56
|
+
export declare const PLAN_SCOPE_RESOLVE_TIMEOUT_MS = 10000;
|
|
57
|
+
/** Max (directory, planKey) entries in the root-timestamp cache (FIFO). */
|
|
58
|
+
export declare const MAX_ROOT_TS_CACHE_ENTRIES = 16;
|
|
59
|
+
/**
|
|
60
|
+
* Defensive taskId scrubber for commit-subject and grep-pattern use (Phase
|
|
61
|
+
* 17 C.H2). Plan-ledger dep ids flow here from LLM-authored plans without
|
|
62
|
+
* re-validation; a `)` or newline would corrupt the subject regex. Keeps
|
|
63
|
+
* only characters safe in both a git subject and an ERE literal.
|
|
64
|
+
*/
|
|
65
|
+
export declare function scrubTaskIdForGitSubject(taskId: string): string;
|
|
66
|
+
/** `sha256(planIdentityHash + '|' + (planEpoch ?? '')).slice(0, 16)`. */
|
|
67
|
+
export declare function computePlanKey(planIdentityHash: string, planEpoch: string | null): string;
|
|
68
|
+
/** The trailer line closing every Epic task commit message. */
|
|
69
|
+
export declare function formatSwarmPlanTrailer(planKey: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* The message of an Epic task commit (landing merge or residue): subject
|
|
72
|
+
* `swarm(task <id>): <summary>` — the summary whitespace-collapsed and
|
|
73
|
+
* truncated to keep the subject within git's conventional 72 columns — and a
|
|
74
|
+
* final `Swarm-Plan: <planKey>` trailer. The task id is scrubbed by
|
|
75
|
+
* {@link scrubTaskIdForGitSubject}. Treat the shape as a stable contract.
|
|
76
|
+
*/
|
|
77
|
+
export declare function formatEpicTaskCommitMessage(taskId: string, planKey: string, summary?: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the plan scope (planKey + plan root time) for `plan` — used by
|
|
80
|
+
* `/swarm epic status` to date recorded merge failures. Throws when the
|
|
81
|
+
* ledger identity is invalid/conflicting or the read exceeds
|
|
82
|
+
* {@link PLAN_SCOPE_RESOLVE_TIMEOUT_MS}; the caller then reports the root as
|
|
83
|
+
* unknown (every failure blocking).
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolvePlanMarkerScope(directory: string, plan: Plan): Promise<PlanMarkerScope>;
|
|
86
|
+
/**
|
|
87
|
+
* Parse `git log -z` output produced with {@link MARKER_LOG_FORMAT}. Records
|
|
88
|
+
* whose subject is not a `swarm(task <id>):` marker are dropped (a squash
|
|
89
|
+
* message may quote a marker in its body).
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseTaskMarkerLog(output: string): ParsedTaskMarker[];
|
|
92
|
+
/** Test-only: number of cached root timestamps. */
|
|
93
|
+
export declare function _rootTimestampCacheSizeForTest(): number;
|
|
94
|
+
/** Test-only: clear the root-timestamp cache. */
|
|
95
|
+
export declare function _resetRootTimestampCacheForTest(): void;
|
|
96
|
+
/**
|
|
97
|
+
* DI seam (AGENTS.md invariant 7) — tests substitute ledger readers and the
|
|
98
|
+
* resolution deadline without `mock.module`. Restore in `afterEach`.
|
|
99
|
+
*/
|
|
100
|
+
export declare const _internals: {
|
|
101
|
+
readLedgerEvents: typeof readLedgerEvents_import;
|
|
102
|
+
readPlanEpochIdentity: typeof readPlanEpochIdentity_import;
|
|
103
|
+
resolveTimeoutMs: number;
|
|
104
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Epic v2 C7 — the `save_plan` plan-shaping seam (Epic side).
|
|
3
|
+
*
|
|
4
|
+
* `save_plan` calls {@link computeSavePlanEpicShaping} ONLY when the config
|
|
5
|
+
* it already loaded has `epic.mode.enabled === true`, AFTER the plan
|
|
6
|
+
* lock is released, inside its own try/catch (a throw ⇒ the save still
|
|
7
|
+
* succeeds, without `epic_shaping`). With Epic off nothing here runs: no
|
|
8
|
+
* config read, no I/O, no await (the seam is in `src/tools/save-plan.ts`).
|
|
9
|
+
*
|
|
10
|
+
* What it does (Epic on):
|
|
11
|
+
* - nothing while an epic is open for the project: shaping is pre-start
|
|
12
|
+
* advice; a plan revised mid-epic (e.g. fix tasks) is planned by
|
|
13
|
+
* `epic_next_wave`;
|
|
14
|
+
* - inputs as `/swarm epic start` sizes the plan: live declared scopes,
|
|
15
|
+
* learned signals from the project prior, the wave width
|
|
16
|
+
* (`max_parallel_coders` in a git project, 1 otherwise — detected by a
|
|
17
|
+
* `.git` entry in the project root or an ancestor, no git subprocess),
|
|
18
|
+
* and co-change from the WARM in-memory cache only
|
|
19
|
+
* (`peekCoChangeData`; no git scan on a plan save). Cold ⇒ path-only,
|
|
20
|
+
* flagged `cochange: 'cold'` (r2 critic MINOR 12: the cache is filled by
|
|
21
|
+
* `/swarm epic start`, `/swarm coupling` and `epic_next_wave`, so a
|
|
22
|
+
* save_plan before any of them shapes path-only);
|
|
23
|
+
* - `shapeEpicPlan` (pure, bounded by its budget);
|
|
24
|
+
* - an iteration counter per plan identity in `.swarm/epic/shaping.json`
|
|
25
|
+
* (a different plan restarts at 1); from iteration
|
|
26
|
+
* {@link SHAPING_ACCEPT_ITERATION} the next step says to accept the plan
|
|
27
|
+
* and proceed rather than reshape again.
|
|
28
|
+
*
|
|
29
|
+
* Result (`SavePlanResult.epic_shaping`): a one-line advisory when the plan
|
|
30
|
+
* is not epic-sized and no suggestion fixes that (r2 critic M5 — "run it in
|
|
31
|
+
* Balanced") or when it is over the shaping budget; otherwise the full
|
|
32
|
+
* advisory with ranked suggestions, a `next_step` and the iteration.
|
|
33
|
+
*/
|
|
34
|
+
import type { Plan } from '../config/plan-schema.js';
|
|
35
|
+
import type { PluginConfig } from '../config/schema.js';
|
|
36
|
+
import { loadPlanJsonOnly } from '../plan/manager.js';
|
|
37
|
+
import { peekCoChangeData } from './cochange-source.js';
|
|
38
|
+
import { resolveEpicDeclaredScopes } from './declared-scopes.js';
|
|
39
|
+
import { loadEpicLearningView } from './learning-store.js';
|
|
40
|
+
import { isEpicOpenForProject } from './lifecycle.js';
|
|
41
|
+
import { type EpicShapingReport, shapeEpicPlan } from './shaping.js';
|
|
42
|
+
import type { EpicShapingSuggestion } from './shaping-suggestions.js';
|
|
43
|
+
/** `.swarm/epic/shaping.json` — the iteration counter. */
|
|
44
|
+
export declare const EPIC_SHAPING_RELATIVE_PATH: string;
|
|
45
|
+
/** From this iteration on, `next_step` says accept and proceed. */
|
|
46
|
+
export declare const SHAPING_ACCEPT_ITERATION = 3;
|
|
47
|
+
export type EpicShapingCochangeState = 'warm' | 'cold' | 'disabled';
|
|
48
|
+
/**
|
|
49
|
+
* One suggestion as `save_plan` reports it: the shaping suggestion with
|
|
50
|
+
* every key in snake_case (`task_ids`, `file_kind`, `edge_share`,
|
|
51
|
+
* `patch.new_task`, `patch.edits[].task_id`, …), its ΔS_eff as
|
|
52
|
+
* `delta_effective_speedup` and the what-if as `what_if`.
|
|
53
|
+
*/
|
|
54
|
+
export type EpicShapingAdvisorySuggestion = {
|
|
55
|
+
type: EpicShapingSuggestion['type'];
|
|
56
|
+
summary: string;
|
|
57
|
+
delta_effective_speedup: number | null;
|
|
58
|
+
what_if: {
|
|
59
|
+
effective_speedup: number;
|
|
60
|
+
epic_sized: boolean;
|
|
61
|
+
} | null;
|
|
62
|
+
} & Record<string, unknown>;
|
|
63
|
+
export interface EpicShapingAdvisory {
|
|
64
|
+
status: 'acceptable' | 'improvable';
|
|
65
|
+
epic_sized: boolean;
|
|
66
|
+
effective_speedup: number;
|
|
67
|
+
pending_tasks: number;
|
|
68
|
+
serial_steps: number;
|
|
69
|
+
/** Sizing reasons (empty when epic-sized). */
|
|
70
|
+
reasons: string[];
|
|
71
|
+
cochange: EpicShapingCochangeState;
|
|
72
|
+
suggestions: EpicShapingAdvisorySuggestion[];
|
|
73
|
+
iteration: number;
|
|
74
|
+
next_step: string;
|
|
75
|
+
}
|
|
76
|
+
/** `SavePlanResult.epic_shaping`. */
|
|
77
|
+
export type EpicSavePlanShaping = {
|
|
78
|
+
status: 'not-epic-sized';
|
|
79
|
+
message: string;
|
|
80
|
+
cochange: EpicShapingCochangeState;
|
|
81
|
+
} | {
|
|
82
|
+
status: 'skipped-budget';
|
|
83
|
+
message: string;
|
|
84
|
+
cochange: EpicShapingCochangeState;
|
|
85
|
+
} | EpicShapingAdvisory;
|
|
86
|
+
/**
|
|
87
|
+
* Count this save as one shaping iteration of the plan (identity hash +
|
|
88
|
+
* ledger root ⇒ a new plan or a re-rooted ledger restarts at 1). A write
|
|
89
|
+
* failure is logged; the advisory still carries the computed iteration.
|
|
90
|
+
*/
|
|
91
|
+
export declare function bumpShapingIteration(directory: string, plan: Plan): number;
|
|
92
|
+
/** The `epic_shaping` value of one shaping report. */
|
|
93
|
+
export declare function toSavePlanShaping(report: EpicShapingReport, iteration: number, cochange: EpicShapingCochangeState): EpicSavePlanShaping;
|
|
94
|
+
/**
|
|
95
|
+
* The `epic_shaping` of a saved plan, or null when an epic is open. The
|
|
96
|
+
* caller has checked the Epic config gate; may throw (the caller fails
|
|
97
|
+
* open). Shapes the PERSISTED plan (`plan.json` as save_plan just wrote it
|
|
98
|
+
* — statuses preserved, current_phase normalized), falling back to the
|
|
99
|
+
* plan the caller saved when it cannot be read.
|
|
100
|
+
*/
|
|
101
|
+
export declare function computeSavePlanEpicShaping(directory: string, savedArgsPlan: Plan, config: PluginConfig): Promise<EpicSavePlanShaping | null>;
|
|
102
|
+
/** Test-only DI seam (AGENTS.md invariant 7). */
|
|
103
|
+
export declare const _internals: {
|
|
104
|
+
isEpicOpenForProject: typeof isEpicOpenForProject;
|
|
105
|
+
loadPlanJsonOnly: typeof loadPlanJsonOnly;
|
|
106
|
+
peekCoChangeData: typeof peekCoChangeData;
|
|
107
|
+
loadEpicLearningView: typeof loadEpicLearningView;
|
|
108
|
+
resolveEpicDeclaredScopes: typeof resolveEpicDeclaredScopes;
|
|
109
|
+
shapeEpicPlan: typeof shapeEpicPlan;
|
|
110
|
+
existsSync: (target: string) => boolean;
|
|
111
|
+
now: () => number;
|
|
112
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Epic v2 C5/C6 — the planner's inputs beyond the plan, read ONCE per
|
|
3
|
+
* planning call by both callers of the component planner (`epic_next_wave`
|
|
4
|
+
* and the `/swarm epic start` sizing dry-run) so the two plan with the same
|
|
5
|
+
* signals:
|
|
6
|
+
*
|
|
7
|
+
* - learned signals (`learning.ts`) when `epic.learning.enabled` is
|
|
8
|
+
* not false: the hot files (tasks declaring one run alone) and the
|
|
9
|
+
* co-write weights that expand each task's scope in the conflict graph.
|
|
10
|
+
* Read from the open epic's posterior (`epic_next_wave`), else from the
|
|
11
|
+
* project prior (the start's sizing, before the epic exists);
|
|
12
|
+
* - co-change: the git co-change pairs + threshold when
|
|
13
|
+
* `epic.cochange.enabled` (null otherwise ⇒ path-only conflicts);
|
|
14
|
+
* - density threshold: the static `epic.mode.activation_threshold`
|
|
15
|
+
* — the intra-component density above which a component runs serially.
|
|
16
|
+
*
|
|
17
|
+
* A failed read degrades to "no signal" (no learned signal / path-only),
|
|
18
|
+
* the fail-open the planner always had for these optional signals; the
|
|
19
|
+
* wave verdict still guards every issued wave. The sources are injected so
|
|
20
|
+
* each caller keeps its own `_internals` DI seam.
|
|
21
|
+
*/
|
|
22
|
+
import type { PluginConfig } from '../config/schema.js';
|
|
23
|
+
import type { CoChangeData } from './cochange-source.js';
|
|
24
|
+
import { type EpicCochangeSignal } from './components.js';
|
|
25
|
+
import { type EpicCoWriteIndex, type EpicLearningSettings } from './learning.js';
|
|
26
|
+
import type { EpicIdentity, EpicLearningView } from './learning-store.js';
|
|
27
|
+
export interface EpicPlanningSignals {
|
|
28
|
+
hotFiles: string[];
|
|
29
|
+
coWrites: EpicCoWriteIndex | null;
|
|
30
|
+
cochange: EpicCochangeSignal | null;
|
|
31
|
+
densityThreshold: number;
|
|
32
|
+
}
|
|
33
|
+
export interface EpicPlanningSignalSources {
|
|
34
|
+
loadLearningView: (directory: string, epic: EpicIdentity | null, settings: EpicLearningSettings, nowMs: number) => EpicLearningView;
|
|
35
|
+
getCoChangeData: (directory: string) => Promise<CoChangeData>;
|
|
36
|
+
now: () => number;
|
|
37
|
+
}
|
|
38
|
+
export declare function loadEpicPlanningSignals(directory: string, config: Pick<PluginConfig, 'turbo' | 'epic'>, sources: EpicPlanningSignalSources, epic: EpicIdentity | null): Promise<EpicPlanningSignals>;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/swarm epic report [<key>|last] [--format json]` (Epic v2 C8): the
|
|
3
|
+
* scorecard (`scorecard.ts`) of the open epic, computed live from its
|
|
4
|
+
* record, or of a past epic, read from its close report in
|
|
5
|
+
* `.swarm/epic-prior/reports/` (kept across `/swarm close`, newest 50).
|
|
6
|
+
*
|
|
7
|
+
* Selection: no selector ⇒ the open epic when one is open, else the newest
|
|
8
|
+
* report; `last` ⇒ the newest report; `<key>` ⇒ the report `<key>.json`
|
|
9
|
+
* (a report key) or the newest report of epic `<key>` (an epic key).
|
|
10
|
+
* "Newest" is by file mtime (as the 50-report pruning), then name.
|
|
11
|
+
*
|
|
12
|
+
* Strict reader: a report is read only when it is an `epic-report-v2`
|
|
13
|
+
* report whose embedded scorecard validates group by group (every field the
|
|
14
|
+
* formatter reads, {@link EpicScorecardSchema}); anything else is reported
|
|
15
|
+
* as unreadable, never half-rendered.
|
|
16
|
+
*
|
|
17
|
+
* Read-only and bounded: one directory listing, `stat` per report, and one
|
|
18
|
+
* report read (≤ {@link MAX_EPIC_REPORT_BYTES}); the live view is sentinel
|
|
19
|
+
* first (no sentinel ⇒ no DB open). Like `/swarm epic status` it works
|
|
20
|
+
* regardless of the Epic config gate and for an orphaned epic (both noted).
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import { loadPlanJsonOnly } from '../plan/manager.js';
|
|
24
|
+
import { type EpicLearningSettings } from './learning.js';
|
|
25
|
+
import { loadEpicLearningView } from './learning-store.js';
|
|
26
|
+
import { type EpicRecordV1, epicSentinelExists, inspectEpic } from './lifecycle.js';
|
|
27
|
+
import { completedBeforeEpic } from './next-wave.js';
|
|
28
|
+
import { type EpicScorecardV1 } from './scorecard.js';
|
|
29
|
+
/** Largest close report read (reports are bounded by the record). */
|
|
30
|
+
export declare const MAX_EPIC_REPORT_BYTES: number;
|
|
31
|
+
/** DI seam (AGENTS.md invariant 7). Restore in `afterEach`. */
|
|
32
|
+
export declare const _internals: {
|
|
33
|
+
epicSentinelExists: typeof epicSentinelExists;
|
|
34
|
+
inspectEpic: typeof inspectEpic;
|
|
35
|
+
loadPlanJsonOnly: typeof loadPlanJsonOnly;
|
|
36
|
+
completedBeforeEpic: typeof completedBeforeEpic;
|
|
37
|
+
loadEpicLearningView: typeof loadEpicLearningView;
|
|
38
|
+
now: () => number;
|
|
39
|
+
};
|
|
40
|
+
export type EpicReportSelection = {
|
|
41
|
+
status: 'ok';
|
|
42
|
+
source: 'live' | 'report';
|
|
43
|
+
/** Null for the live scorecard. */
|
|
44
|
+
reportKey: string | null;
|
|
45
|
+
scorecard: EpicScorecardV1;
|
|
46
|
+
/** Caveats shown with a live scorecard (orphaned, config off). */
|
|
47
|
+
notes?: string[];
|
|
48
|
+
} | {
|
|
49
|
+
status: 'none';
|
|
50
|
+
message: string;
|
|
51
|
+
} | {
|
|
52
|
+
status: 'error';
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
/** Report file names in `.swarm/epic-prior/reports/`, newest first. */
|
|
56
|
+
export declare function listEpicPriorReports(directory: string): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Every scorecard group the formatter and the JSON consumers read. Unknown
|
|
59
|
+
* keys are allowed (a newer writer may add fields); a missing or mistyped
|
|
60
|
+
* known field makes the report unreadable.
|
|
61
|
+
*/
|
|
62
|
+
export declare const EpicScorecardSchema: z.ZodObject<{
|
|
63
|
+
schema: z.ZodLiteral<"epic-scorecard-v1">;
|
|
64
|
+
epicKey: z.ZodString;
|
|
65
|
+
planId: z.ZodString;
|
|
66
|
+
outcome: z.ZodEnum<{
|
|
67
|
+
completed: "completed";
|
|
68
|
+
open: "open";
|
|
69
|
+
abandoned: "abandoned";
|
|
70
|
+
"abandoned-by-swarm-close": "abandoned-by-swarm-close";
|
|
71
|
+
}>;
|
|
72
|
+
startedAt: z.ZodString;
|
|
73
|
+
closedAt: z.ZodNullable<z.ZodString>;
|
|
74
|
+
forced: z.ZodBoolean;
|
|
75
|
+
sizingAtStart: z.ZodObject<{
|
|
76
|
+
epicSized: z.ZodBoolean;
|
|
77
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
78
|
+
pendingTasks: z.ZodNumber;
|
|
79
|
+
scopedTasks: z.ZodNumber;
|
|
80
|
+
scopeCoverage: z.ZodNumber;
|
|
81
|
+
serialSteps: z.ZodNumber;
|
|
82
|
+
concurrency: z.ZodNumber;
|
|
83
|
+
effectiveSpeedup: z.ZodNumber;
|
|
84
|
+
}, z.core.$strip>;
|
|
85
|
+
tasks: z.ZodObject<{
|
|
86
|
+
total: z.ZodNullable<z.ZodNumber>;
|
|
87
|
+
completedInEpic: z.ZodNumber;
|
|
88
|
+
adoptedAtStart: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
exclusive: z.ZodNumber;
|
|
90
|
+
serialComponent: z.ZodNumber;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
waves: z.ZodObject<{
|
|
93
|
+
count: z.ZodNumber;
|
|
94
|
+
parallel: z.ZodNumber;
|
|
95
|
+
meanWidth: z.ZodNumber;
|
|
96
|
+
maxWidth: z.ZodNumber;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
time: z.ZodObject<{
|
|
99
|
+
method: z.ZodLiteral<"wave-span-v1">;
|
|
100
|
+
label: z.ZodString;
|
|
101
|
+
spanMs: z.ZodNumber;
|
|
102
|
+
workMs: z.ZodNumber;
|
|
103
|
+
concurrencyFactor: z.ZodNullable<z.ZodNumber>;
|
|
104
|
+
interWaveIdleMs: z.ZodNumber;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
conflicts: z.ZodObject<{
|
|
107
|
+
mergeFailures: z.ZodNumber;
|
|
108
|
+
undeclaredWriteTasks: z.ZodNumber;
|
|
109
|
+
undeclaredFiles: z.ZodArray<z.ZodString>;
|
|
110
|
+
undeclaredFilesTotal: z.ZodNumber;
|
|
111
|
+
}, z.core.$strip>;
|
|
112
|
+
rework: z.ZodObject<{
|
|
113
|
+
tasksWithRework: z.ZodNumber;
|
|
114
|
+
extraGenerations: z.ZodNumber;
|
|
115
|
+
reopened: z.ZodNumber;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
gates: z.ZodObject<{
|
|
118
|
+
stageAFirstPass: z.ZodObject<{
|
|
119
|
+
passed: z.ZodNumber;
|
|
120
|
+
of: z.ZodNumber;
|
|
121
|
+
rate: z.ZodNullable<z.ZodNumber>;
|
|
122
|
+
}, z.core.$strip>;
|
|
123
|
+
stageBFirstPass: z.ZodObject<{
|
|
124
|
+
passed: z.ZodNumber;
|
|
125
|
+
of: z.ZodNumber;
|
|
126
|
+
rate: z.ZodNullable<z.ZodNumber>;
|
|
127
|
+
}, z.core.$strip>;
|
|
128
|
+
phaseReviewFirstPass: z.ZodObject<{
|
|
129
|
+
passed: z.ZodNumber;
|
|
130
|
+
of: z.ZodNumber;
|
|
131
|
+
rate: z.ZodNullable<z.ZodNumber>;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
boundedHistory: z.ZodLiteral<true>;
|
|
134
|
+
}, z.core.$strip>;
|
|
135
|
+
learning: z.ZodObject<{
|
|
136
|
+
priorDigest: z.ZodNullable<z.ZodString>;
|
|
137
|
+
topHotFiles: z.ZodArray<z.ZodString>;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
}, z.core.$strip>;
|
|
140
|
+
/**
|
|
141
|
+
* The scorecard of a parsed close report, or an error naming the first
|
|
142
|
+
* problem (wrong schema, or the scorecard field that failed validation).
|
|
143
|
+
*/
|
|
144
|
+
export declare function scorecardFromReport(raw: unknown): {
|
|
145
|
+
scorecard: EpicScorecardV1;
|
|
146
|
+
} | {
|
|
147
|
+
error: string;
|
|
148
|
+
};
|
|
149
|
+
/** The live scorecard of the epic record `epic` (open or closing). */
|
|
150
|
+
export declare function liveEpicScorecard(directory: string, epic: EpicRecordV1, learning: EpicLearningSettings): Promise<EpicScorecardV1>;
|
|
151
|
+
/** Resolve `selector` (null / `last` / a report or epic key). */
|
|
152
|
+
export declare function selectEpicReport(directory: string, selector: string | null, learning: EpicLearningSettings): Promise<EpicReportSelection>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Epic v2 C3 (X1) — residue commits for non-coder writers.
|
|
3
|
+
*
|
|
4
|
+
* Coders of an open git epic are isolated in worktrees and their work lands
|
|
5
|
+
* as a commit (`task-landing.ts`). Other agents write in the MAIN tree: the
|
|
6
|
+
* test_engineer writes the task's tests, the docs agent writes docs, and so
|
|
7
|
+
* on. Left uncommitted, those files are invisible to a rework coder (its
|
|
8
|
+
* worktree is cut from HEAD) and collide with its landing. So:
|
|
9
|
+
*
|
|
10
|
+
* - when a non-coder delegation for a task of the open epic returns —
|
|
11
|
+
* a foreground Task (the delegation gate's after-hook) or a background
|
|
12
|
+
* one (the completion observer) — {@link commitEpicResidueAfterDelegation}
|
|
13
|
+
* commits the main-tree writes still uncommitted on the epic branch:
|
|
14
|
+
* the returning agent's own recorded writes as
|
|
15
|
+
* `swarm(task <id>): <agent> residue`, then the rest of the task's
|
|
16
|
+
* attribution and frozen scope as `swarm(task <id>): residue` (no
|
|
17
|
+
* agent is claimed for files it did not record), each with the plan's
|
|
18
|
+
* `Swarm-Plan:` trailer. Files another still-running delegation has
|
|
19
|
+
* recorded (the concurrent Stage B reviewer/test_engineer) are left
|
|
20
|
+
* alone: they belong to that writer and may be half-written;
|
|
21
|
+
* - `epic_next_wave` runs {@link commitTaskResidue} again for each task of
|
|
22
|
+
* the closing wave (a missed after-hook, a failed attempt); before a new
|
|
23
|
+
* wave the remaining dirty paths belong to no task
|
|
24
|
+
* ({@link classifyDirtyBaseline}).
|
|
25
|
+
*
|
|
26
|
+
* Attribution of a path to a task: the task's declared scope in the CURRENT
|
|
27
|
+
* (or closing) wave — literal files or directories, never globs — its write
|
|
28
|
+
* attribution in any session of this project, or, for the after-hook, any
|
|
29
|
+
* write recorded on the delegated agent's own child session. Scopes of
|
|
30
|
+
* earlier waves never attribute anything (later edits there are the
|
|
31
|
+
* user's). Never `.swarm/` (at any depth), never git pathspec magic; staging
|
|
32
|
+
* and the commit use `:(literal)` pathspecs, and the commit is `--only` so
|
|
33
|
+
* anything else in the index stays out of it. Every residue write is
|
|
34
|
+
* serialized with worktree merge-backs (one writer of the primary index at
|
|
35
|
+
* a time), and a failed commit restores the touched index entries exactly
|
|
36
|
+
* (the user's own staged state included).
|
|
37
|
+
*
|
|
38
|
+
* Subprocess discipline (AGENTS.md #3): status is read through `gitExec`;
|
|
39
|
+
* `git add` / `git commit` (and the index restore after a failed commit) run ONCE
|
|
40
|
+
* each through `gitExecOnce`; the commit is non-interactive
|
|
41
|
+
* (`--no-verify`, `-c commit.gpgsign=false`, closed stdin, timeout).
|
|
42
|
+
* Fail-open: every failure is a critical warning — the after-hook must never
|
|
43
|
+
* break, and `epic_next_wave` turns a persistent failure into `git-failed`.
|
|
44
|
+
*/
|
|
45
|
+
import { getAgentSession as getAgentSession_import } from '../state.js';
|
|
46
|
+
import { checkEpicBranch as checkEpicBranch_import } from './epic-branch.js';
|
|
47
|
+
import { gitExecOnce } from './git-once.js';
|
|
48
|
+
import { type EpicRecordV1, epicSentinelExists as epicSentinelExists_import, getOpenEpic as getOpenEpic_import, readPlanTaskRef as readPlanTaskRef_import } from './lifecycle.js';
|
|
49
|
+
import { collectTaskAttribution as collectTaskAttribution_import } from './wave-close.js';
|
|
50
|
+
/** DI seam (AGENTS.md invariant 7). Restore in `afterEach`. */
|
|
51
|
+
export declare const _internals: {
|
|
52
|
+
epicSentinelExists: typeof epicSentinelExists_import;
|
|
53
|
+
getOpenEpic: typeof getOpenEpic_import;
|
|
54
|
+
readPlanTaskRef: typeof readPlanTaskRef_import;
|
|
55
|
+
checkEpicBranch: typeof checkEpicBranch_import;
|
|
56
|
+
collectTaskAttribution: typeof collectTaskAttribution_import;
|
|
57
|
+
getAgentSession: typeof getAgentSession_import;
|
|
58
|
+
/** Every in-process agent session (id → state), for in-flight writers. */
|
|
59
|
+
listAgentSessions: () => MapIterator<[string, import("../state.js").AgentSessionState]>;
|
|
60
|
+
gitExec: (args: string[], cwd: string) => string;
|
|
61
|
+
gitExecOnce: typeof gitExecOnce;
|
|
62
|
+
/** One writer at a time in the primary checkout (merge-back queue). */
|
|
63
|
+
serializeWithMergeBacks: <T>(task: () => T | Promise<T>) => Promise<T>;
|
|
64
|
+
now: () => number;
|
|
65
|
+
};
|
|
66
|
+
/** True for a path inside a `.swarm/` directory at any depth. */
|
|
67
|
+
export declare function isSwarmStatePath(file: string): boolean;
|
|
68
|
+
/** One uncommitted path outside `.swarm/` (porcelain v1). */
|
|
69
|
+
export interface DirtyEntry {
|
|
70
|
+
path: string;
|
|
71
|
+
untracked: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* False when the change is fully staged already (worktree column blank):
|
|
74
|
+
* `git add` must skip it (a staged deletion no longer matches a pathspec).
|
|
75
|
+
*/
|
|
76
|
+
needsAdd?: boolean;
|
|
77
|
+
/** Source path of a rename/copy record (its deletion is staged). */
|
|
78
|
+
origin?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Uncommitted paths outside `.swarm/` (untracked files listed individually).
|
|
82
|
+
* Throws when git fails.
|
|
83
|
+
*/
|
|
84
|
+
export declare function listDirtyEntries(directory: string): DirtyEntry[];
|
|
85
|
+
/** Result of one residue commit attempt. */
|
|
86
|
+
export type ResidueCommitResult = {
|
|
87
|
+
status: 'nothing';
|
|
88
|
+
} | {
|
|
89
|
+
status: 'committed';
|
|
90
|
+
sha: string | null;
|
|
91
|
+
files: string[];
|
|
92
|
+
} | {
|
|
93
|
+
status: 'failed';
|
|
94
|
+
error: string;
|
|
95
|
+
files: string[];
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Commit, as task `taskId`'s residue, the dirty paths that are among
|
|
99
|
+
* `candidates` (exact repo-relative paths) or covered by `scopes` (declared
|
|
100
|
+
* files or directories). `dirty` is the current status (read once by the
|
|
101
|
+
* caller). Rename sources follow their destination.
|
|
102
|
+
*/
|
|
103
|
+
export declare function commitTaskResidue(args: {
|
|
104
|
+
directory: string;
|
|
105
|
+
epic: EpicRecordV1;
|
|
106
|
+
taskId: string;
|
|
107
|
+
label: string;
|
|
108
|
+
candidates: Iterable<string>;
|
|
109
|
+
scopes?: readonly string[];
|
|
110
|
+
dirty: readonly DirtyEntry[];
|
|
111
|
+
}): ResidueCommitResult;
|
|
112
|
+
/**
|
|
113
|
+
* The frozen declared scope of `taskId` in the epic's CURRENT wave (the
|
|
114
|
+
* issued, active one), or `[]`. Scopes of earlier, closed waves never
|
|
115
|
+
* attribute a later write: the user's edits there are not a task's residue.
|
|
116
|
+
*/
|
|
117
|
+
export declare function declaredScopeOf(epic: EpicRecordV1, taskId: string): string[];
|
|
118
|
+
/** What the delegation gate hands the after-hook seam. */
|
|
119
|
+
export interface EpicResidueRequest {
|
|
120
|
+
directory: string;
|
|
121
|
+
/** Raw `subagent_type` of the returned Task delegation. */
|
|
122
|
+
agent: string;
|
|
123
|
+
sessionID: string | undefined;
|
|
124
|
+
/** Plan task id(s) the delegation served (lazy: Epic-only work). */
|
|
125
|
+
resolveTaskIds: () => Promise<string[]>;
|
|
126
|
+
/** The delegated agent's child session id(s) (lazy). */
|
|
127
|
+
childSessionIds: () => Promise<Array<string | null | undefined>>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* After-hook seam (delegation gate, Task `tool.execute.after`): commit the
|
|
131
|
+
* residue of a non-coder writer that served a task of the open git epic.
|
|
132
|
+
* One `existsSync` and nothing else when no epic is open. Never throws.
|
|
133
|
+
*/
|
|
134
|
+
export declare function commitEpicResidueAfterDelegation(request: EpicResidueRequest): Promise<void>;
|
|
135
|
+
/** Dirty paths before a new wave, by what Epic does with them. */
|
|
136
|
+
export interface DirtyBaselineClassification {
|
|
137
|
+
/** Tracked changes: block the next wave (`dirty-baseline`). */
|
|
138
|
+
unattributedTracked: string[];
|
|
139
|
+
/** Untracked files: advisory only. */
|
|
140
|
+
unattributedUntracked: string[];
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Classify the dirty paths before a new wave (X1 `dirty-baseline`). Residue
|
|
144
|
+
* is only ever attributed to tasks of the CURRENT wave (the after-hook) or
|
|
145
|
+
* the closing wave (`epic_next_wave` commits it before the close), so when a
|
|
146
|
+
* new wave is about to be issued nothing dirty belongs to a task any more:
|
|
147
|
+
* a later edit under an earlier task's declared directory is the user's,
|
|
148
|
+
* never swept into that task's residue. Tracked changes block; untracked
|
|
149
|
+
* files are only reported.
|
|
150
|
+
*/
|
|
151
|
+
export declare function classifyDirtyBaseline(dirty: readonly DirtyEntry[]): DirtyBaselineClassification;
|