peaks-loop 4.0.46 → 4.0.48

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 (207) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/agents/karpathy-reviewer.md +11 -10
  5. package/dist/cli/cli-helpers.d.ts +34 -0
  6. package/dist/cli/cli-helpers.js +57 -0
  7. package/dist/cli/commands/code-job-shape-commands.js +8 -0
  8. package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
  9. package/dist/cli/commands/code-runtime-commands.js +139 -16
  10. package/dist/cli/commands/compact-command.js +241 -1
  11. package/dist/cli/commands/config-commands.js +15 -9
  12. package/dist/cli/commands/container-commands.js +3 -3
  13. package/dist/cli/commands/core/skill-command.js +45 -10
  14. package/dist/cli/commands/cron-commands.js +2 -1
  15. package/dist/cli/commands/dashboard-long-run.js +6 -0
  16. package/dist/cli/commands/dispatch-commands.js +11 -1
  17. package/dist/cli/commands/doctor/invoke-from-code.js +6 -0
  18. package/dist/cli/commands/e2e-verify.js +3 -3
  19. package/dist/cli/commands/governance-classify-contract-commands.js +1 -0
  20. package/dist/cli/commands/hooks-commands.js +14 -5
  21. package/dist/cli/commands/job-commands.js +8 -0
  22. package/dist/cli/commands/loop-commands.js +1 -0
  23. package/dist/cli/commands/loop-eval-commands.js +15 -0
  24. package/dist/cli/commands/perf-audit-commands.js +2 -0
  25. package/dist/cli/commands/playwright-commands.js +14 -1
  26. package/dist/cli/commands/prd-commands.js +1 -1
  27. package/dist/cli/commands/qa-commands.js +22 -0
  28. package/dist/cli/commands/reinject-command.d.ts +72 -0
  29. package/dist/cli/commands/reinject-command.js +174 -0
  30. package/dist/cli/commands/request-commands.js +14 -3
  31. package/dist/cli/commands/scan-commands.js +1 -1
  32. package/dist/cli/commands/security-audit-commands.js +2 -0
  33. package/dist/cli/commands/shadcn-commands.js +1 -0
  34. package/dist/cli/commands/slice-integrate-commands.js +5 -0
  35. package/dist/cli/commands/statusline-commands.js +44 -4
  36. package/dist/cli/commands/sub-agent/detached.d.ts +14 -1
  37. package/dist/cli/commands/sub-agent/detached.js +47 -22
  38. package/dist/cli/commands/sub-agent-shutdown-commands.js +11 -0
  39. package/dist/cli/commands/test-commands.js +2 -1
  40. package/dist/cli/commands/verdict-aggregate-command.js +95 -13
  41. package/dist/cli/commands/vm-commands.js +7 -7
  42. package/dist/cli/commands/workflow-commands.js +1 -1
  43. package/dist/cli/commands/workspace/init-command.js +24 -2
  44. package/dist/cli/commands/worktree-lease-commands.js +4 -4
  45. package/dist/cli/index.js +10 -3
  46. package/dist/cli/program.js +5 -0
  47. package/dist/hooks/pre-tool-use-sub-agent.js +1 -1
  48. package/dist/services/adapter/adapter-registry.js +1 -1
  49. package/dist/services/artifacts/artifact-prerequisites.d.ts +38 -7
  50. package/dist/services/artifacts/artifact-prerequisites.js +130 -65
  51. package/dist/services/artifacts/artifact-service.js +1 -1
  52. package/dist/services/artifacts/request-artifact-service.d.ts +8 -0
  53. package/dist/services/artifacts/request-artifact-service.js +18 -8
  54. package/dist/services/artifacts/request-artifact-state-helpers.d.ts +57 -0
  55. package/dist/services/artifacts/request-artifact-state-helpers.js +91 -10
  56. package/dist/services/audit-independent/perf-audit-service.d.ts +9 -0
  57. package/dist/services/audit-independent/perf-audit-service.js +27 -5
  58. package/dist/services/audit-independent/security-audit-service.d.ts +12 -2
  59. package/dist/services/audit-independent/security-audit-service.js +28 -6
  60. package/dist/services/capability-guard-runner/contracts/J01.js +2 -1
  61. package/dist/services/capability-guard-runner/contracts/J02.js +3 -3
  62. package/dist/services/capability-guard-runner/contracts/J04.js +4 -2
  63. package/dist/services/capability-guard-runner/contracts/J07.js +2 -1
  64. package/dist/services/code/auto-compact-lifecycle.d.ts +130 -1
  65. package/dist/services/code/auto-compact-lifecycle.js +180 -4
  66. package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
  67. package/dist/services/code/auto-compact-orchestrator.js +166 -34
  68. package/dist/services/code/compact-event-settle.d.ts +122 -0
  69. package/dist/services/code/compact-event-settle.js +219 -0
  70. package/dist/services/code/orchestrator-can-do.d.ts +4 -2
  71. package/dist/services/code/orchestrator-can-do.js +37 -5
  72. package/dist/services/codegraph/codegraph-exclude-reconciler.js +2 -1
  73. package/dist/services/codegraph/codegraph-process-runner.js +3 -2
  74. package/dist/services/compact/request-transition-hook.js +5 -2
  75. package/dist/services/compact-history/compact-history-service.d.ts +75 -0
  76. package/dist/services/compact-history/compact-history-service.js +49 -0
  77. package/dist/services/config/config-restore.d.ts +12 -1
  78. package/dist/services/config/config-restore.js +35 -4
  79. package/dist/services/config/config-rollback.js +6 -1
  80. package/dist/services/config/config-safety.d.ts +52 -0
  81. package/dist/services/config/config-safety.js +75 -1
  82. package/dist/services/context/auto-compact-dispatcher.d.ts +7 -37
  83. package/dist/services/context/auto-compact-dispatcher.js +113 -40
  84. package/dist/services/context/auto-compact-reader.d.ts +68 -28
  85. package/dist/services/context/auto-compact-reader.js +155 -1
  86. package/dist/services/context/auto-compact-types.d.ts +89 -12
  87. package/dist/services/context/auto-compact-types.js +16 -32
  88. package/dist/services/context/harness-context-witness.d.ts +310 -0
  89. package/dist/services/context/harness-context-witness.js +606 -0
  90. package/dist/services/context/harness-window-config.d.ts +412 -0
  91. package/dist/services/context/harness-window-config.js +607 -0
  92. package/dist/services/context/main-session-monitor.d.ts +27 -0
  93. package/dist/services/context/main-session-monitor.js +32 -1
  94. package/dist/services/context/post-compact-reinjection.d.ts +221 -0
  95. package/dist/services/context/post-compact-reinjection.js +491 -0
  96. package/dist/services/dispatch/merge-back-runner.js +5 -5
  97. package/dist/services/dispatch/service-shutdown.js +3 -3
  98. package/dist/services/doc/doc-generator.js +2 -1
  99. package/dist/services/env/shell-probe.js +1 -1
  100. package/dist/services/evidence/evidence-generator.js +86 -49
  101. package/dist/services/final-review/final-review-service.d.ts +9 -0
  102. package/dist/services/final-review/final-review-service.js +36 -12
  103. package/dist/services/fuzzy-matching/fzf-pick-service.js +2 -0
  104. package/dist/services/hooks/auto-compact-hook-install.d.ts +10 -2
  105. package/dist/services/hooks/auto-compact-hook-install.js +8 -0
  106. package/dist/services/ide/adapters/claude-code-adapter.d.ts +107 -3
  107. package/dist/services/ide/adapters/claude-code-adapter.js +154 -7
  108. package/dist/services/ide/ide-registry.d.ts +31 -0
  109. package/dist/services/ide/ide-registry.js +35 -0
  110. package/dist/services/ide/ide-types.d.ts +59 -0
  111. package/dist/services/job/job-state-store.js +7 -0
  112. package/dist/services/lint/detect-eslint.js +2 -2
  113. package/dist/services/lint/eslint-runner.js +3 -1
  114. package/dist/services/loop/evaluator-dispatcher.js +2 -1
  115. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +1 -1
  116. package/dist/services/memory/project-memory-service/store/paths.d.ts +9 -1
  117. package/dist/services/memory/project-memory-service/store/paths.js +15 -6
  118. package/dist/services/polyrepo/polyrepo-dispatcher.js +11 -0
  119. package/dist/services/prd/best-practice-auto-trigger.js +1 -0
  120. package/dist/services/prd/handoff-auto-regen.js +31 -27
  121. package/dist/services/prd/handoff-frontmatter.d.ts +44 -0
  122. package/dist/services/prd/handoff-frontmatter.js +75 -0
  123. package/dist/services/prd/handoff-service.d.ts +41 -2
  124. package/dist/services/prd/handoff-service.js +81 -8
  125. package/dist/services/prd/handoff-types.d.ts +3 -2
  126. package/dist/services/prd/handoff-types.js +3 -2
  127. package/dist/services/qa/qa-business-review-state.js +9 -0
  128. package/dist/services/release/version-precheck-service.d.ts +2 -1
  129. package/dist/services/release/version-precheck-service.js +82 -12
  130. package/dist/services/runtime/vendor-adapter.d.ts +29 -4
  131. package/dist/services/runtime/vendors/claude-code.js +1 -1
  132. package/dist/services/runtime/vendors/codex.js +1 -1
  133. package/dist/services/runtime/vendors/copilot.js +1 -1
  134. package/dist/services/sc/sc-service.js +1 -1
  135. package/dist/services/scan/diff-scope-service.js +2 -2
  136. package/dist/services/scan/file-size-scan.js +2 -2
  137. package/dist/services/scan/karpathy-service.js +2 -2
  138. package/dist/services/scan/orphan-service.js +2 -1
  139. package/dist/services/scan/type-sanity-service.js +2 -2
  140. package/dist/services/session/session-checkpoint-service.js +8 -0
  141. package/dist/services/skill/resume-detector.js +29 -11
  142. package/dist/services/skillhub/tar-runtime.js +1 -0
  143. package/dist/services/skills/hooks-codegate-superpowers.d.ts +14 -0
  144. package/dist/services/skills/hooks-codegate-superpowers.js +99 -3
  145. package/dist/services/skills/hooks-settings-service.d.ts +12 -0
  146. package/dist/services/skills/hooks-settings-service.js +91 -14
  147. package/dist/services/skills/session-start-hook-constants.d.ts +86 -0
  148. package/dist/services/skills/session-start-hook-constants.js +86 -0
  149. package/dist/services/skills/skill-presence-service.js +9 -0
  150. package/dist/services/skills/skill-statusline-service.d.ts +14 -0
  151. package/dist/services/slice/slice-check-service.js +31 -12
  152. package/dist/services/slice/slice-decompose-runners.js +2 -1
  153. package/dist/services/slice/slice-review-state.js +8 -0
  154. package/dist/services/upgrade/upgrade-service.js +1 -0
  155. package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -10
  156. package/dist/services/workflow/pipeline-verify-gate-support.js +212 -93
  157. package/dist/services/workflow/pipeline-verify-service.js +24 -23
  158. package/dist/services/workflow/pipeline-verify-types.d.ts +10 -3
  159. package/dist/services/workflow/workflow-skip-service.js +2 -1
  160. package/dist/services/workspace/claude-settings-template.d.ts +56 -8
  161. package/dist/services/workspace/claude-settings-template.js +98 -20
  162. package/dist/services/workspace/migrate-service.js +1 -1
  163. package/dist/services/workspace/workspace-claude-settings-materializer.js +124 -9
  164. package/dist/services/workspace/workspace-service.js +8 -0
  165. package/dist/services/worktree/host-worktree-reconciler.js +1 -0
  166. package/dist/services/worktree/long-path-cleanup.js +3 -2
  167. package/dist/shared/process.js +1 -1
  168. package/package.json +6 -6
  169. package/scripts/install-skills.mjs +1 -0
  170. package/scripts/watch.mjs +3 -1
  171. package/skills/bee/peaks-perf-audit/SKILL.md +1 -1
  172. package/skills/bee/peaks-prd/SKILL.md +8 -6
  173. package/skills/bee/peaks-qa/SKILL.md +7 -7
  174. package/skills/bee/peaks-qa/references/qa-runbook.md +2 -2
  175. package/skills/bee/peaks-qa/references/qa-transition-gates.md +7 -7
  176. package/skills/bee/peaks-rd/SKILL.md +10 -8
  177. package/skills/bee/peaks-rd/references/artifact-per-request.md +2 -2
  178. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -5
  179. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +13 -13
  180. package/skills/bee/peaks-rd/references/rd-runbook.md +9 -5
  181. package/skills/bee/peaks-rd/references/rd-transition-gates.md +9 -7
  182. package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +6 -6
  183. package/skills/bee/peaks-reviewer/SKILL.md +1 -1
  184. package/skills/bee/peaks-sc/SKILL.md +1 -1
  185. package/skills/bee/peaks-security-audit/SKILL.md +1 -1
  186. package/skills/bee/peaks-txt/SKILL.md +1 -1
  187. package/skills/bee/peaks-ui/SKILL.md +1 -1
  188. package/skills/peaks-audit/SKILL.md +1 -1
  189. package/skills/peaks-code/SKILL.md +3 -3
  190. package/skills/peaks-code/references/a2a-artifact-mapping.md +3 -3
  191. package/skills/peaks-code/references/local-artifact-workspace.md +1 -1
  192. package/skills/peaks-code/references/resume-detection.md +13 -7
  193. package/skills/peaks-code/references/runbook.md +3 -2
  194. package/skills/peaks-code/references/session-overload-signal-index.md +2 -1
  195. package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
  196. package/skills/peaks-code/references/workflow-gates-and-types.md +8 -6
  197. package/skills/peaks-content/SKILL.md +1 -1
  198. package/skills/peaks-doctor/SKILL.md +1 -1
  199. package/skills/peaks-final-review/SKILL.md +1 -1
  200. package/skills/peaks-ide/SKILL.md +1 -1
  201. package/skills/peaks-issue-fix-orchestrator/SKILL.md +1 -1
  202. package/skills/peaks-resume/SKILL.md +1 -1
  203. package/skills/peaks-slice-decompose/SKILL.md +1 -1
  204. package/skills/peaks-solo/SKILL.md +1 -1
  205. package/skills/peaks-sop/SKILL.md +1 -1
  206. package/skills/peaks-status/SKILL.md +1 -1
  207. package/skills/peaks-test/SKILL.md +1 -1
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Harness auto-compact window ownership
3
+ * (slice 2026-09-13-auto-compact-trigger-ownership, rid
4
+ * 2026-09-13-auto-compact-trigger-ownership).
5
+ *
6
+ * The goal (user's words): *when* to trigger auto-compact must be
7
+ * peaks-loop's decision; *how* to compact stays the harness's capability.
8
+ *
9
+ * That only holds if the two sides agree on ONE number. Before this slice
10
+ * they each resolved a window independently: Claude Code used the window it
11
+ * knows for the model, while peaks-loop divided by `resolveContextWindow()`
12
+ * (env → config → model-name heuristic → 200_000 default). A 1M-window model
13
+ * whose id the heuristic does not recognise made peaks-loop's "95%" land at
14
+ * 190K while the harness waited for ~967K — a 5× early trigger, and (before
15
+ * T3) a deadlock, because nothing could lower the ratio.
16
+ *
17
+ * This module owns the OTHER half of the fix: the harness itself accepts a
18
+ * window override (`IdeCompactProfile.autoCompactWindowEnvVar`, declared per
19
+ * adapter — no IDE names here), so peaks-loop writes the very number it
20
+ * computes the ratio against into the harness's own machine-local settings
21
+ * file. Reader and writer then reference one artifact instead of two
22
+ * resolutions that must "remember" to agree.
23
+ *
24
+ * Vendor neutrality: this module knows about a JSON file with an `env` block
25
+ * and nothing else. The settings path and the env-var name are BOTH passed
26
+ * in by the caller, resolved from the IDE adapter's declarations. There is
27
+ * no IDE id, no `.claude` literal, and no registry import in this file.
28
+ */
29
+ /**
30
+ * Peaks-owned opt-out flag, read from the SAME machine-local `env` block the
31
+ * window key is written to.
32
+ *
33
+ * Why it exists: `resetHarnessWindow` must be a real rollback. Without a
34
+ * durable opt-out, the next context probe would simply write the key back
35
+ * and the user's "remove it" would last until the next tool call — a
36
+ * rollback in name only. Absent (or any value other than `off`) = sync is
37
+ * enabled, which keeps every project installed by an earlier release
38
+ * unchanged.
39
+ */
40
+ export declare const HARNESS_WINDOW_SYNC_OPTOUT_KEY = "PEAKS_HARNESS_WINDOW_SYNC";
41
+ /** The one value that disables the sync. */
42
+ export declare const HARNESS_WINDOW_SYNC_OPTOUT_VALUE = "off";
43
+ /**
44
+ * Provenance marker: the window value peaks-loop ITSELF last wrote, recorded
45
+ * in the same `env` block as the window key.
46
+ *
47
+ * Why it must exist. The window key is a SHARED artifact — the harness reads
48
+ * it, the user may set it by hand (it is a documented Claude Code variable),
49
+ * and peaks-loop writes it. Two rules collide on it:
50
+ *
51
+ * - the ratio peaks-loop reports must divide by the key (the single-source
52
+ * rule), so the key outranks the model heuristic; and
53
+ * - a first-time mis-resolution must not become PERMANENT.
54
+ *
55
+ * The second rule needs the late 1M rescue to be able to overrule the key when
56
+ * the observed context proves the key too small. Applied to a value a HUMAN
57
+ * pinned, that same rescue would silently destroy an explicit setting — the
58
+ * user asks for an early compact, a long session outgrows the pin, and
59
+ * peaks-loop rewrites the file to 1M, permanently. Applied to peaks-loop's own
60
+ * earlier output it is simply self-correction.
61
+ *
62
+ * This marker is what tells the two apart: it records what peaks-loop wrote,
63
+ * so a mismatch means the human has taken the key over and the rescue must
64
+ * stand down. It travels in the same write as the value it describes, so the
65
+ * two can never disagree about which write they belong to.
66
+ */
67
+ export declare const HARNESS_WINDOW_WRITTEN_KEY = "PEAKS_HARNESS_WINDOW_WRITTEN";
68
+ /**
69
+ * The harness's OWN accepted band for its auto-compact window (E1, rid
70
+ * 2026-09-13-defects-e).
71
+ *
72
+ * THE BAND IS A PROPERTY OF THE KEY, NOT OF peaks-loop. `autoCompactWindow` —
73
+ * and the `CLAUDE_CODE_AUTO_COMPACT_WINDOW` env var that shadows it — is
74
+ * documented as "how full the context window gets before Claude Code compacts
75
+ * automatically, in tokens from 100000 to 1000000", and a value outside that
76
+ * band is not the window the harness compacts on: below the minimum the value
77
+ * is ignored, above the maximum it is reduced to the model's own context size
78
+ * (`Math.min(native, override)`; no Claude model's window exceeds 1000000, so
79
+ * above the maximum the reduction is certain).
80
+ *
81
+ * Either way the outcome is the failure this whole module exists to prevent:
82
+ * peaks-loop divides its ratio by a number the harness is not using, so
83
+ * "85%" names a point the harness will never fire at. A positive integer is
84
+ * therefore NOT sufficient — the band is part of what the value means.
85
+ *
86
+ * Clamping instead was rejected. A clamped write would put a DIFFERENT number
87
+ * in the file from the one the probe just divided by, restoring the
88
+ * two-resolutions drift inside the single write that is supposed to delete it.
89
+ * Refusing keeps the disagreement in one place, reportable, and reported.
90
+ */
91
+ export declare const HARNESS_WINDOW_MIN_TOKENS = 100000;
92
+ /** See `HARNESS_WINDOW_MIN_TOKENS`. */
93
+ export declare const HARNESS_WINDOW_MAX_TOKENS = 1000000;
94
+ /**
95
+ * True when `tokens` is a window the harness will actually compact on.
96
+ * `null` (absent / unparseable) is NOT in band — there is no window at all.
97
+ */
98
+ export declare function isHarnessWindowInRange(tokens: number | null): boolean;
99
+ /** Where the harness key lives + which key it is. Both caller-supplied. */
100
+ export interface HarnessWindowLocation {
101
+ /** Absolute path of the machine-local settings file. */
102
+ readonly settingsPath: string;
103
+ /** Env var / settings key the harness reads its auto-compact window from. */
104
+ readonly envVar: string;
105
+ /**
106
+ * The project root `settingsPath` was derived from, when the caller knows it.
107
+ *
108
+ * Why the writer wants it: `--project .` with the shell sitting in the user's
109
+ * home directory resolves the "project root" to `$HOME` itself, which puts
110
+ * this file at `$HOME/.claude/settings.local.json` — the user's PERSONAL
111
+ * harness settings, shared by every project they own and outside any repo
112
+ * peaks-loop has business editing. A new terminal starts in `$HOME`, so that
113
+ * combination is the ordinary one, not an exotic one. The writer refuses it
114
+ * (reason `unsafe-project-root`) and says so instead of writing.
115
+ *
116
+ * Optional: callers that legitimately operate on a temp directory or on a
117
+ * path they built themselves simply omit it, and no check is made. The
118
+ * comparison lives in `isUserHomeProjectRoot` (`config-safety.ts`) and is
119
+ * never string equality — a `--project` argument may arrive as `C:/Users/x`
120
+ * where `homedir()` yields `C:\Users\x`.
121
+ */
122
+ readonly projectRoot?: string;
123
+ }
124
+ /** Which artifact a value was read from. */
125
+ export type HarnessWindowValueSource = 'process-env' | 'settings-file';
126
+ export interface HarnessWindowReadResult {
127
+ /** Parsed window in tokens, or null when absent / not a positive integer. */
128
+ readonly tokens: number | null;
129
+ /** The raw on-disk / in-env value, for diagnostics. */
130
+ readonly raw: unknown;
131
+ /** Where the value came from; null when neither side carries it. */
132
+ readonly source: HarnessWindowValueSource | null;
133
+ /**
134
+ * The SETTINGS FILE's window, parsed — the value the harness will carry
135
+ * tomorrow, independent of the frozen process env. `null` when the file is
136
+ * silent or its value is unparseable.
137
+ *
138
+ * This is the T1 number, and it is deliberately separate from `tokens`:
139
+ * `tokens` answers "what is in force in the running session?" (env-first,
140
+ * for the status read), while `fileTokens` answers "what is configured for
141
+ * the harness?" — the one artifact peaks-loop can keep consistent with its
142
+ * own ratio. See `resolveHarnessRatioWindow` in `auto-compact-reader.ts`.
143
+ */
144
+ readonly fileTokens: number | null;
145
+ /**
146
+ * The settings file's raw value, `undefined` when the file carries no such
147
+ * key. Presence-detection only; use `fileTokens` for the number.
148
+ *
149
+ * Needed because "the file says 150000" and "the file says something
150
+ * unparseable" must both be distinguishable from "the file is silent": the
151
+ * first two are values peaks-loop must not overwrite, the third is an empty
152
+ * slot it may fill.
153
+ */
154
+ readonly fileRaw: unknown;
155
+ /** True when `settings.local.json` opts out of peaks-loop managing it. */
156
+ readonly optedOut: boolean;
157
+ /**
158
+ * True when peaks-loop is the current writer of this key — the file's window
159
+ * value still matches the `HARNESS_WINDOW_WRITTEN_KEY` marker peaks-loop
160
+ * wrote with it. False when the key is unset, or when its value differs from
161
+ * the marker, i.e. a human has taken the key over.
162
+ *
163
+ * Deliberately decided from the FILE's value, not from whichever copy
164
+ * (`process-env` / `settings-file`) supplied `tokens` this time. A running
165
+ * session freezes its env at start-up, so after peaks-loop raises the file
166
+ * the env still hands back the superseded number; judging by that copy would
167
+ * report "a human set this" about peaks-loop's own value and switch the late
168
+ * 1M rescue off on the next probe.
169
+ *
170
+ * The caller uses this to decide whether the late 1M rescue may overrule the
171
+ * harness layer: peaks-loop's own output may be corrected, a human's pin may
172
+ * not. See `resolveContextWindowTokens`.
173
+ */
174
+ readonly peakWritten: boolean;
175
+ }
176
+ export type HarnessWindowSyncAction = 'written' | 'unchanged' | 'skipped';
177
+ export interface HarnessWindowSyncResult {
178
+ readonly settingsPath: string;
179
+ /** The key written / read (the adapter-declared `autoCompactWindowEnvVar`). */
180
+ readonly key: string;
181
+ readonly action: HarnessWindowSyncAction;
182
+ /**
183
+ * Why the sync did nothing. Present only when `action === 'skipped'`:
184
+ * - `no-window-resolved` — the caller had no token window to write
185
+ * (percent-only probe sources carry none)
186
+ * - `opted-out` — the user ran the rollback
187
+ * - `unreadable-settings` — the file exists but is not a JSON object we
188
+ * can safely edit
189
+ * - `not-peaks-owned` — the file already carries a value that
190
+ * peaks-loop did not write (a human's pin, or
191
+ * another tool's). peaks-loop never rewrites it
192
+ * and never re-arms its provenance marker for
193
+ * it; the value wins over peaks-loop's own
194
+ * resolution instead. This is the B1 guard —
195
+ * without it a stale process env (or any
196
+ * re-resolution) silently reverts a human's
197
+ * hand-edited window and re-claims ownership.
198
+ * - `unsafe-project-root` — the resolved project root IS the user's home
199
+ * directory, so this write would land in their
200
+ * personal `~/.claude/settings.local.json`
201
+ * (`--project .` from a fresh terminal). The H1
202
+ * guard; `--reset` is deliberately still allowed
203
+ * there, because removing a key an earlier
204
+ * release put in `$HOME` is the user's explicit
205
+ * recovery path.
206
+ */
207
+ readonly reason?: string;
208
+ /** The value in force after the call. */
209
+ readonly tokens: number | null;
210
+ /**
211
+ * The value the SETTINGS FILE held before the call (null when absent — or
212
+ * when the file is unreadable). Deliberately the file's value, not the
213
+ * process env's: the file is what the next session reads, so it is what the
214
+ * caller must quote back to the user ("was 200000") and what decides whether
215
+ * a write is needed at all.
216
+ */
217
+ readonly previousTokens: number | null;
218
+ /**
219
+ * The window the caller ASKED to materialize — the denominator it computed
220
+ * that probe's ratio against (`probe.capacityTokens`). `null` when the
221
+ * caller carried no token window at all.
222
+ *
223
+ * Why it is reported rather than left at the call site: the interesting
224
+ * outcome is a DISAGREEMENT, and a disagreement takes two numbers. A result
225
+ * that carried only "what is in force" left every consumer unable to name
226
+ * what it disagrees WITH — the notice for a refused write could say that
227
+ * nothing was written and never which two numbers were apart. See
228
+ * `describeHarnessWindowSync`.
229
+ */
230
+ readonly requestedTokens: number | null;
231
+ /**
232
+ * The file's RAW value for the key before the call; `undefined` when the
233
+ * file carried no such key.
234
+ *
235
+ * The one case `previousTokens` cannot express: a hand-typed `500k` parses
236
+ * to `null`, exactly like an absent key, yet the two need opposite notices —
237
+ * an absent key is a slot peaks-loop may fill, a typo is a value it must not
238
+ * touch. Only the raw text can be quoted back to the user, so it travels
239
+ * with the result.
240
+ */
241
+ readonly previousRawValue: unknown;
242
+ /**
243
+ * True when the window in the file after the call is peaks-loop's own write
244
+ * (its value matches the provenance marker).
245
+ *
246
+ * Reported because "the file already holds the number we wanted" has two very
247
+ * different meanings and only this flag tells them apart: peaks-loop's own
248
+ * earlier output, which it may raise when a session outgrows it, versus a
249
+ * value a human pinned, which it never will. Without it, `unchanged` reads as
250
+ * "all good" for a key peaks-loop has permanently stopped managing — the
251
+ * state a user upgrading from a pre-marker release is silently in.
252
+ *
253
+ * Always `true` for `action: 'written'`: the write that produced the result
254
+ * is what sets the marker.
255
+ */
256
+ readonly peakWritten: boolean;
257
+ }
258
+ export type HarnessWindowResetResult = {
259
+ readonly settingsPath: string;
260
+ readonly action: 'removed' | 'absent';
261
+ readonly previousTokens: number | null;
262
+ };
263
+ /**
264
+ * The ONE wording of "what the harness-window sync just did", for both
265
+ * commands that sync (`peaks code context-now`, `peaks code auto-compact`).
266
+ *
267
+ * Why it lives here rather than inline at each call site: peaks-loop rewrites
268
+ * a file in the user's own harness settings on every probe, and the user
269
+ * accepted that write on one condition — 要告知 (tell me). Two copies of the
270
+ * sentence would mean a future edit tells half the users. This is the only
271
+ * definition; the CLI renders whatever it returns.
272
+ *
273
+ * The `written` branch is the load-bearing one: it names the key, the value,
274
+ * the file, what the value was before, and the exact rollback command, because
275
+ * the harness itself reports an override only through `/autocompact` — if
276
+ * peaks-loop does not say it here, nobody says it.
277
+ *
278
+ * The `skipped` branch is the one that is easy to get wrong, and did: it used
279
+ * to end at the reason token (`Harness window not managed (not-peaks-owned).`),
280
+ * which is true and tells the reader nothing. A refusal can be the exact
281
+ * moment the two sides came apart — the ratio divided by one number while the
282
+ * file pins another — so it is composed from the reason, the two numbers when
283
+ * they disagree, and the raw value when the file's value is not a number at
284
+ * all. See `harnessWindowConflictClause` / `unreadableWindowValueClause`.
285
+ */
286
+ export declare function describeHarnessWindowSync(result: HarnessWindowSyncResult | null): string;
287
+ /**
288
+ * The same disagreement as `harnessWindowConflictClause`, reduced to one line
289
+ * for the `warnings` channel — the machine-readable half of 要告知, so a
290
+ * consumer reading the JSON envelope (or a human reading stderr) sees it even
291
+ * if it never renders `nextActions`. Returns `null` when there is nothing to
292
+ * warn about, which is the ordinary case.
293
+ */
294
+ export declare function harnessWindowSyncWarning(result: HarnessWindowSyncResult | null): string | null;
295
+ /**
296
+ * Parse a candidate window value. Positive finite integers only — a number,
297
+ * or a numeric string (the env block is JSON, so the value on disk is always
298
+ * a string there). The harness's own documentation is explicit that the
299
+ * variable "accepts only the plain token count" and NOT a `850k` suffix, so
300
+ * anything non-integral is refused here rather than written and silently
301
+ * ignored downstream.
302
+ */
303
+ export declare function parseHarnessWindowTokens(raw: unknown): number | null;
304
+ /**
305
+ * Read the window the harness is currently using for auto-compact.
306
+ *
307
+ * Order: the process env FIRST (that is the value the RUNNING session
308
+ * captured at start-up, so it is what the harness is actually compacting
309
+ * against right now), then the settings file (what the next session will
310
+ * read). When the two disagree, the file is what the sync updates — the
311
+ * process env cannot be changed from inside a running session.
312
+ */
313
+ export declare function readHarnessWindow(input: {
314
+ readonly location: HarnessWindowLocation;
315
+ readonly env?: NodeJS.ProcessEnv | undefined;
316
+ }): HarnessWindowReadResult;
317
+ /**
318
+ * Write `tokens` as the harness's auto-compact window. Idempotent: a re-run
319
+ * with the same value performs no write at all, so repeated calls (the sync
320
+ * runs on every context probe) cannot churn the file or duplicate the entry.
321
+ *
322
+ * Everything else in the file is preserved verbatim — the caller's own `env`
323
+ * entries, every `hooks` entry, and any unknown top-level key. This is the
324
+ * same read-modify-write discipline `auto-compact-hook-install.ts` already
325
+ * applies to this file; there are now two writers, and both must leave the
326
+ * other's rows alone.
327
+ */
328
+ export declare function syncHarnessWindow(input: {
329
+ readonly location: HarnessWindowLocation;
330
+ readonly tokens: number | null;
331
+ readonly env?: NodeJS.ProcessEnv | undefined;
332
+ }): HarnessWindowSyncResult;
333
+ /**
334
+ * Rollback: remove the window key and record the opt-out so the next sync
335
+ * does not put it straight back. Both edits land in the same
336
+ * read-modify-write, so there is no window in which the key is gone but the
337
+ * opt-out is not yet on disk.
338
+ *
339
+ * Idempotent: a second call reports `action: 'absent'` and rewrites nothing
340
+ * unless the opt-out row is missing.
341
+ */
342
+ export declare function resetHarnessWindow(input: {
343
+ readonly location: HarnessWindowLocation;
344
+ readonly env?: NodeJS.ProcessEnv | undefined;
345
+ }): HarnessWindowResetResult;
346
+ /**
347
+ * Record the opt-out WITHOUT removing anything: "stop managing this key",
348
+ * expressible at any moment — including before peaks-loop has ever written it.
349
+ *
350
+ * WHY THIS IS A SEPARATE ENTRY POINT AND NOT A FALLBACK OF `resetHarnessWindow`
351
+ *
352
+ * "Remove what is there" and "never write it again" are two different
353
+ * intentions. `--reset` on a file holding no peaks row is deliberately a no-op
354
+ * (see the file-litter note in `resetHarnessWindow`), so a user whose project
355
+ * peaks-loop has never touched had NO command for the second intention at all:
356
+ * they had to wait for a probe to write the key and then remove it — the order
357
+ * backwards. Folding the opt-out into `--reset` would fix that by making one
358
+ * verb mean "delete" or "don't write", depending on whether the file happened
359
+ * to hold a row, which is a verb whose effect the user cannot predict from its
360
+ * name. Two verbs, two intentions, each predictable.
361
+ *
362
+ * WHAT IT DOES NOT DO
363
+ * - it does not touch the window key: a hand-set value stays exactly as it
364
+ * was (peaks-loop would not have overwritten it anyway — see the B1 guard);
365
+ * - it does not write a provenance marker, so it claims no ownership of a
366
+ * value it did not write.
367
+ * The next probe reports `skipped / opted-out` and writes nothing.
368
+ *
369
+ * THE H1 HOME GUARD APPLIES HERE TOO (E4, rid 2026-09-13-defects-e).
370
+ *
371
+ * This function used to exempt itself, on the argument that `--disable` is an
372
+ * explicit instruction and "the location's own file is the only place the
373
+ * opt-out can be recorded for it to mean anything". The first half is true and
374
+ * was never the problem; the second half does not hold at `$HOME`:
375
+ *
376
+ * - the location is NOT usually explicit. `--project` is optional, and a
377
+ * fresh terminal starts in `$HOME`, so the ordinary invocation resolves the
378
+ * root to the user's home directory without them naming it — the very
379
+ * trigger the H1 guard was written for;
380
+ * - the opt-out has NOTHING to mean there. `syncHarnessWindow` already
381
+ * refuses to write the window at that root (`unsafe-project-root`), so the
382
+ * only thing a recorded opt-out changes is which sentence the refusal uses.
383
+ * Nothing is made expressible; a visible refusal is traded for a quieter one;
384
+ * - what IS added is a durable peaks-loop row in `~/.claude/settings.local.json`
385
+ * — a file outside every repo. `--reset`'s exemption does not transfer to
386
+ * this verb: `--reset` REMOVES (and is a no-op when there is nothing of
387
+ * peaks-loop's to remove), while `--disable` only ever ADDS.
388
+ *
389
+ * So the guard fires here exactly as it does in `syncHarnessWindow`: the same
390
+ * exact-home comparison, before any `mkdir` and before any read-modify-write.
391
+ * `~/my-project` is unaffected, and a user who really does carry a stale
392
+ * peaks-loop window key in `$HOME` still has `--reset`, which is allowed there
393
+ * for the reason its own note gives.
394
+ *
395
+ * Idempotent: a second call reports `already-opted-out` and rewrites nothing.
396
+ */
397
+ export declare function disableHarnessWindowSync(input: {
398
+ readonly location: HarnessWindowLocation;
399
+ }): {
400
+ readonly settingsPath: string;
401
+ readonly action: 'disabled' | 'already-opted-out' | 'unreadable-settings' | 'refused-unsafe-project-root';
402
+ };
403
+ /**
404
+ * Undo the opt-out (the companion of `resetHarnessWindow`) so peaks-loop
405
+ * resumes owning the harness window.
406
+ */
407
+ export declare function reenableHarnessWindowSync(input: {
408
+ readonly location: HarnessWindowLocation;
409
+ }): {
410
+ readonly settingsPath: string;
411
+ readonly action: 'reenabled' | 'absent';
412
+ };