peaks-loop 4.0.46 → 4.0.47

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 (130) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
  5. package/dist/cli/commands/code-runtime-commands.js +99 -16
  6. package/dist/cli/commands/compact-command.js +129 -1
  7. package/dist/cli/commands/container-commands.js +3 -3
  8. package/dist/cli/commands/core/skill-command.js +45 -10
  9. package/dist/cli/commands/cron-commands.js +2 -1
  10. package/dist/cli/commands/e2e-verify.js +3 -3
  11. package/dist/cli/commands/governance-classify-contract-commands.js +1 -0
  12. package/dist/cli/commands/hooks-commands.js +10 -1
  13. package/dist/cli/commands/loop-commands.js +1 -0
  14. package/dist/cli/commands/playwright-commands.js +2 -1
  15. package/dist/cli/commands/reinject-command.d.ts +72 -0
  16. package/dist/cli/commands/reinject-command.js +174 -0
  17. package/dist/cli/commands/request-commands.js +6 -3
  18. package/dist/cli/commands/shadcn-commands.js +1 -0
  19. package/dist/cli/commands/test-commands.js +2 -1
  20. package/dist/cli/commands/vm-commands.js +7 -7
  21. package/dist/cli/commands/workspace/init-command.js +24 -2
  22. package/dist/cli/commands/worktree-lease-commands.js +4 -4
  23. package/dist/cli/index.js +49 -2
  24. package/dist/cli/program.js +5 -0
  25. package/dist/hooks/pre-tool-use-sub-agent.js +1 -1
  26. package/dist/services/adapter/adapter-registry.js +1 -1
  27. package/dist/services/artifacts/artifact-service.js +1 -1
  28. package/dist/services/capability-guard-runner/contracts/J01.js +2 -1
  29. package/dist/services/capability-guard-runner/contracts/J02.js +3 -3
  30. package/dist/services/capability-guard-runner/contracts/J04.js +4 -2
  31. package/dist/services/capability-guard-runner/contracts/J07.js +2 -1
  32. package/dist/services/code/auto-compact-lifecycle.d.ts +11 -1
  33. package/dist/services/code/auto-compact-lifecycle.js +11 -4
  34. package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
  35. package/dist/services/code/auto-compact-orchestrator.js +153 -32
  36. package/dist/services/code/orchestrator-can-do.d.ts +4 -2
  37. package/dist/services/code/orchestrator-can-do.js +37 -5
  38. package/dist/services/codegraph/codegraph-exclude-reconciler.js +2 -1
  39. package/dist/services/codegraph/codegraph-process-runner.js +3 -2
  40. package/dist/services/compact/request-transition-hook.js +5 -2
  41. package/dist/services/compact-history/compact-history-service.d.ts +61 -0
  42. package/dist/services/compact-history/compact-history-service.js +49 -0
  43. package/dist/services/config/config-safety.d.ts +52 -0
  44. package/dist/services/config/config-safety.js +75 -1
  45. package/dist/services/context/auto-compact-dispatcher.d.ts +7 -37
  46. package/dist/services/context/auto-compact-dispatcher.js +113 -40
  47. package/dist/services/context/auto-compact-reader.d.ts +68 -28
  48. package/dist/services/context/auto-compact-reader.js +155 -1
  49. package/dist/services/context/auto-compact-types.d.ts +89 -12
  50. package/dist/services/context/auto-compact-types.js +16 -32
  51. package/dist/services/context/harness-window-config.d.ts +412 -0
  52. package/dist/services/context/harness-window-config.js +607 -0
  53. package/dist/services/context/main-session-monitor.d.ts +27 -0
  54. package/dist/services/context/main-session-monitor.js +32 -1
  55. package/dist/services/context/post-compact-reinjection.d.ts +221 -0
  56. package/dist/services/context/post-compact-reinjection.js +491 -0
  57. package/dist/services/dispatch/merge-back-runner.js +5 -5
  58. package/dist/services/dispatch/service-shutdown.js +3 -3
  59. package/dist/services/doc/doc-generator.js +2 -1
  60. package/dist/services/env/shell-probe.js +1 -1
  61. package/dist/services/fuzzy-matching/fzf-pick-service.js +2 -0
  62. package/dist/services/hooks/auto-compact-hook-install.d.ts +10 -2
  63. package/dist/services/hooks/auto-compact-hook-install.js +8 -0
  64. package/dist/services/ide/adapters/claude-code-adapter.d.ts +107 -3
  65. package/dist/services/ide/adapters/claude-code-adapter.js +154 -7
  66. package/dist/services/ide/ide-registry.d.ts +12 -0
  67. package/dist/services/ide/ide-registry.js +14 -0
  68. package/dist/services/ide/ide-types.d.ts +59 -0
  69. package/dist/services/lint/detect-eslint.js +2 -2
  70. package/dist/services/lint/eslint-runner.js +3 -1
  71. package/dist/services/loop/evaluator-dispatcher.js +2 -1
  72. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +1 -1
  73. package/dist/services/memory/project-memory-service/store/paths.d.ts +9 -1
  74. package/dist/services/memory/project-memory-service/store/paths.js +15 -6
  75. package/dist/services/prd/best-practice-auto-trigger.js +1 -0
  76. package/dist/services/release/version-precheck-service.d.ts +2 -1
  77. package/dist/services/release/version-precheck-service.js +82 -12
  78. package/dist/services/runtime/vendor-adapter.d.ts +29 -4
  79. package/dist/services/runtime/vendors/claude-code.js +1 -1
  80. package/dist/services/runtime/vendors/codex.js +1 -1
  81. package/dist/services/runtime/vendors/copilot.js +1 -1
  82. package/dist/services/sc/sc-service.js +1 -1
  83. package/dist/services/scan/diff-scope-service.js +2 -2
  84. package/dist/services/scan/file-size-scan.js +2 -2
  85. package/dist/services/scan/orphan-service.js +2 -1
  86. package/dist/services/scan/type-sanity-service.js +2 -2
  87. package/dist/services/skillhub/tar-runtime.js +1 -0
  88. package/dist/services/skills/hooks-codegate-superpowers.d.ts +8 -0
  89. package/dist/services/skills/hooks-codegate-superpowers.js +40 -3
  90. package/dist/services/skills/hooks-settings-service.d.ts +12 -0
  91. package/dist/services/skills/hooks-settings-service.js +77 -10
  92. package/dist/services/skills/session-start-hook-constants.d.ts +41 -0
  93. package/dist/services/skills/session-start-hook-constants.js +41 -0
  94. package/dist/services/skills/skill-presence-service.js +9 -0
  95. package/dist/services/slice/slice-check-service.js +2 -1
  96. package/dist/services/slice/slice-decompose-runners.js +2 -1
  97. package/dist/services/upgrade/upgrade-service.js +1 -0
  98. package/dist/services/workflow/workflow-skip-service.js +2 -1
  99. package/dist/services/workspace/migrate-service.js +1 -1
  100. package/dist/services/workspace/workspace-claude-settings-materializer.js +51 -7
  101. package/dist/services/workspace/workspace-service.js +8 -0
  102. package/dist/services/worktree/host-worktree-reconciler.js +1 -0
  103. package/dist/services/worktree/long-path-cleanup.js +3 -2
  104. package/dist/shared/process.js +1 -1
  105. package/package.json +5 -5
  106. package/scripts/install-skills.mjs +1 -0
  107. package/scripts/watch.mjs +3 -1
  108. package/skills/bee/peaks-perf-audit/SKILL.md +1 -1
  109. package/skills/bee/peaks-prd/SKILL.md +1 -1
  110. package/skills/bee/peaks-qa/SKILL.md +2 -2
  111. package/skills/bee/peaks-rd/SKILL.md +2 -2
  112. package/skills/bee/peaks-reviewer/SKILL.md +1 -1
  113. package/skills/bee/peaks-sc/SKILL.md +1 -1
  114. package/skills/bee/peaks-security-audit/SKILL.md +1 -1
  115. package/skills/bee/peaks-txt/SKILL.md +1 -1
  116. package/skills/bee/peaks-ui/SKILL.md +1 -1
  117. package/skills/peaks-audit/SKILL.md +1 -1
  118. package/skills/peaks-code/SKILL.md +2 -2
  119. package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
  120. package/skills/peaks-content/SKILL.md +1 -1
  121. package/skills/peaks-doctor/SKILL.md +1 -1
  122. package/skills/peaks-final-review/SKILL.md +1 -1
  123. package/skills/peaks-ide/SKILL.md +1 -1
  124. package/skills/peaks-issue-fix-orchestrator/SKILL.md +1 -1
  125. package/skills/peaks-resume/SKILL.md +1 -1
  126. package/skills/peaks-slice-decompose/SKILL.md +1 -1
  127. package/skills/peaks-solo/SKILL.md +1 -1
  128. package/skills/peaks-sop/SKILL.md +1 -1
  129. package/skills/peaks-status/SKILL.md +1 -1
  130. package/skills/peaks-test/SKILL.md +1 -1
@@ -22,6 +22,7 @@
22
22
  */
23
23
  import { CONTEXT_CAPACITY_DEFAULT_BYTES, THRESHOLD_SOFT_WARN_RATIO, THRESHOLD_NEAR_LIMIT_RATIO, THRESHOLD_EMERGENCY_RATIO, evaluateThresholdTier } from './threshold.js';
24
24
  import { detectIdeFromEnv } from './ide-detect.js';
25
+ import { tryGetAdapter } from '../ide/ide-registry.js';
25
26
  export const MAIN_SESSION_THRESHOLD_RATIOS = {
26
27
  ok: 0,
27
28
  'soft-warn': THRESHOLD_SOFT_WARN_RATIO,
@@ -71,6 +72,36 @@ export function evaluateMainSessionThreshold(promptSize, capacityBytes = CONTEXT
71
72
  warnings: inner.warnings
72
73
  };
73
74
  }
75
+ /**
76
+ * Which compact pathway serves this IDE, read from the ADAPTER'S OWN
77
+ * declaration (`IdeCompactProfile.compactPathway`) rather than from the
78
+ * IDE's name.
79
+ *
80
+ * Slice 2026-09-12-auto-compact-vendor-neutrality: this decision used to
81
+ * be the inline `ide === 'claude-code' ? 'ide-native' : 'llm-self-compress'`
82
+ * — an IDE identity branch in a module that is otherwise pure, reading the
83
+ * IDE's NAME where the adapter registry holds the CAPABILITY. An IDE that
84
+ * registered `compactPathway: 'ide-native'` was still told to
85
+ * self-compress.
86
+ *
87
+ * Pure + synchronous: `tryGetAdapter` is a Map lookup with no IO, so this
88
+ * module stays IO-free (same seam discipline as the `env?` parameter).
89
+ * `tryGetAdapter` (not `getAdapter`) because the input is an `IdeKind`,
90
+ * which includes ids — `opencode` — with no registered adapter; throwing
91
+ * from a suggestion-formatting path would be a regression.
92
+ *
93
+ * Only `'ide-native'` is in-band. Every other declared pathway
94
+ * (`shell-exec`, `llm-self-compress`, `noop`) reports the
95
+ * always-available `'llm-self-compress'` fallback, because the trigger
96
+ * union can only carry those two values. That under-reports a `noop`
97
+ * adapter — which the dispatcher would refuse outright — and callers
98
+ * therefore MUST NOT read this as a promise that a compact will happen;
99
+ * the dispatcher's `ok` remains the authority.
100
+ */
101
+ export function compactPathwayForIde(ide) {
102
+ const declared = tryGetAdapter(ide)?.compact?.compactPathway;
103
+ return declared === 'ide-native' ? 'ide-native' : 'llm-self-compress';
104
+ }
74
105
  export function pickMainSessionTrigger(opts) {
75
106
  const ide = opts.ide ?? detectIdeFromEnv(opts.env);
76
107
  const evaluation = evaluateMainSessionThreshold(opts.promptSize, opts.capacityBytes);
@@ -91,7 +122,7 @@ export function pickMainSessionTrigger(opts) {
91
122
  return { kind: 'defer', reason: 'unsupported-ide' };
92
123
  }
93
124
  const code = evaluation.tier === 'emergency' ? 'PROMPT_EMERGENCY' : 'CONTEXT_NEAR_LIMIT';
94
- const path = ide === 'claude-code' ? 'ide-native' : 'llm-self-compress';
125
+ const path = compactPathwayForIde(ide);
95
126
  return {
96
127
  kind: 'compact',
97
128
  ide,
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Post-compact engineering-state re-injection
3
+ * (rid `2026-09-13-a2-post-compact-reinject`, session
4
+ * `2026-09-12-session-e37ef0`).
5
+ *
6
+ * THE PROBLEM THIS SOLVES
7
+ *
8
+ * `94bacb4a` settled WHO decides when to compact: peaks-loop owns the window,
9
+ * the harness performs the compaction. This module owns what happens AFTER
10
+ * it. Compaction summarizes a conversation, and a summarizer has no notion of
11
+ * a slice, a request, a gate or an acceptance criterion — so the engineering
12
+ * state is exactly the part it is most likely to drop. The user's word for
13
+ * the symptom is 飘逸 (drift): the session keeps talking but has lost its
14
+ * place.
15
+ *
16
+ * This repository already has the case on file:
17
+ * `.peaks/memory/per-turn-obligations-belong-in-per-turn-output.md` —
18
+ * text that lives in the message history (a SKILL.md body, a dispatch prompt)
19
+ * does not survive a compaction, so a rule that only exists there stops
20
+ * applying. The fix has to be re-injection, not a better summary.
21
+ *
22
+ * WHY A BUDGET IS THE DESIGN AND NOT A DETAIL
23
+ *
24
+ * The harness's own supported mechanism is a `SessionStart` hook whose stdout
25
+ * it adds to the context. So the card competes for the very space the
26
+ * compaction just freed. Re-injecting a lot is WORSE than re-injecting
27
+ * nothing: it re-fills the window, the ratio climbs back, and the next
28
+ * compaction arrives sooner — the cost is paid on every cycle. Hence a hard,
29
+ * declared ceiling (`POST_COMPACT_REINJECTION_BYTE_BUDGET`) and a
30
+ * deterministic rule for what is dropped (see `renderReinjectionCard`).
31
+ *
32
+ * POINT, DO NOT INLINE
33
+ *
34
+ * Two different things can be put in the card:
35
+ * - a FACT whose absence changes behaviour on the very next turn (the
36
+ * runtime rules, the next slice, the identity of the session), and
37
+ * - a BODY of material the model can go and read (a request artifact, a
38
+ * job state file, a memory file).
39
+ * Inlining the second is the worse trade at every size: it costs bytes on
40
+ * every compaction cycle forever, it goes stale the moment it is written, and
41
+ * the model cannot tell a stale copy from a live one. A path costs ~60 bytes,
42
+ * is always current, and the model is going to read the file anyway before it
43
+ * edits anything. So the card inlines only the first kind and spends one
44
+ * whole block on pointers to the second.
45
+ *
46
+ * VENDOR NEUTRALITY
47
+ *
48
+ * Nothing here knows an IDE, a settings file or a hook event. The card is a
49
+ * string; the hook entry that carries it lives with the other peaks hook
50
+ * entries (`session-start-hook-constants.ts`) and the settings plumbing is
51
+ * `applyHookInstall`. The only input that touches the outside world is
52
+ * `projectRoot`.
53
+ *
54
+ * FAIL-SOFT IS A HARD REQUIREMENT, AND IT HAS A SHARP EDGE HERE
55
+ *
56
+ * The hook's stdout IS context. So an error message printed on stdout is not
57
+ * a visible failure — it is a failure injected straight into the model's
58
+ * context as if peaks-loop had meant it. `buildPostCompactReinjectionCard`
59
+ * therefore never throws (every source is individually guarded and reports
60
+ * into `unresolved`), and the CLI wrapper that renders it prints NOTHING and
61
+ * exits 0 when the card cannot be built.
62
+ */
63
+ /**
64
+ * Hard ceiling on the card's size, in UTF-8 bytes.
65
+ *
66
+ * WHY 3072. Three numbers decided it, not taste:
67
+ *
68
+ * 1. It must be small against the freed space. A compaction releases tens
69
+ * of thousands of tokens. 3072 bytes is under ~1200 tokens of English —
70
+ * under 0.6% of a 200K window and under 0.12% of a 1M one. At that size
71
+ * the card cannot plausibly re-fill what the compaction freed, whatever
72
+ * the window, which is the property that makes the re-injection safe to
73
+ * run on every cycle.
74
+ * 2. It must be big enough for the whole content contract. Measured against
75
+ * this repository the full card — identity, pointers, rules, next action
76
+ * and progress — lands around 1.2-1.7 KiB. 3072 leaves roughly 2x
77
+ * headroom for long session ids, a long job id and a long absolute
78
+ * project root before the drop rule has to fire at all.
79
+ * 3. It must be a BYTE count rather than a token or percentage count. Bytes
80
+ * are computable on the hook path with `Buffer.byteLength` and no
81
+ * tokenizer, no window resolution and no dependency; and unlike a
82
+ * share-of-window budget, a fixed byte cap cannot silently GROW on a
83
+ * large-window model — which would reintroduce the very "re-fill the
84
+ * freed space" failure the cap exists to prevent. A percentage budget
85
+ * was considered and rejected for that reason.
86
+ */
87
+ export declare const POST_COMPACT_REINJECTION_BYTE_BUDGET = 3072;
88
+ /**
89
+ * Runtime rules that live ONLY in the message history, so a compaction
90
+ * destroys them.
91
+ *
92
+ * Why a repo-owned constant and not something parsed out of the dispatch
93
+ * record: the dispatch record is free-form prose written per slice, and a
94
+ * parser over it would silently stop matching the day someone rephrases a
95
+ * heading — a rule that vanishes without a failure. These six are peaks-loop's
96
+ * OWN operating rules for an agent working in a peaks-loop session; keeping
97
+ * them here makes adding one a one-line change with a test behind it.
98
+ *
99
+ * The membership test for an entry is: "if the model forgets this, does it
100
+ * act WRONGLY on the next turn?" Session-general advice that already lives in
101
+ * CLAUDE.md / the system prompt is deliberately absent — the system prompt is
102
+ * not part of the summarized history, so re-injecting it would spend budget
103
+ * to say something the model can already see.
104
+ */
105
+ export declare const AGENT_RUNTIME_RULES: ReadonlyArray<string>;
106
+ /**
107
+ * The block ranks, in survival order. The drop rule reads THIS list, so it is
108
+ * the single place the priority is stated.
109
+ *
110
+ * The ordering principle is value-per-byte, highest first:
111
+ * 0 IDENTITY — which session; ~1 line, and every other line depends on it
112
+ * 1 WHERE TO READ — the pointers; cheap, always current, and the only way the
113
+ * model reaches the bulky material it must not have inlined
114
+ * 2 RULES — behaviour-changing, and unavailable anywhere else in context
115
+ * 3 NEXT — one line; without it the next turn has no target
116
+ * 4 CURRENT WORK — request / job / change identity and progress
117
+ * RULES outranks CURRENT WORK deliberately: the identity of the slice is
118
+ * recoverable by Reading the artifact the card points at, whereas a rule the
119
+ * model has forgotten will be broken before it thinks to go looking.
120
+ */
121
+ export declare const REINJECTION_BLOCK_RANKS: readonly [0, 1, 2, 3, 4];
122
+ export declare const REINJECTION_BLOCK_HEADINGS: Readonly<Record<number, string>>;
123
+ /** Short names for the omitted-marker. See `ReinjectionBlock.label`. */
124
+ export declare const REINJECTION_BLOCK_LABELS: Readonly<Record<number, string>>;
125
+ export type ReinjectionBlock = {
126
+ readonly rank: number;
127
+ /** Full heading line, rendered into the card. */
128
+ readonly heading: string;
129
+ /**
130
+ * Short name used by the omitted-marker. Distinct from `heading` on purpose:
131
+ * the marker is itself part of the budget, and a marker built from the full
132
+ * headings ("RULES THAT ARE NOT IN CONTEXT ANY MORE") costs several times
133
+ * what the useful information in it is worth.
134
+ */
135
+ readonly label: string;
136
+ readonly lines: readonly string[];
137
+ };
138
+ export type ReinjectionCard = {
139
+ readonly text: string;
140
+ readonly bytes: number;
141
+ readonly budgetBytes: number;
142
+ readonly emittedRanks: readonly number[];
143
+ readonly droppedRanks: readonly number[];
144
+ };
145
+ /**
146
+ * Assemble the card under a byte budget.
147
+ *
148
+ * THE RULE, stated so it can be checked rather than interpreted:
149
+ *
150
+ * 1. Blocks are rendered whole. Ranks are read in ascending order.
151
+ * 2. A block is emitted iff its bytes still fit in the remaining budget.
152
+ * Otherwise it is SKIPPED and the walk CONTINUES — greedy by rank, not
153
+ * stop-at-first-miss. This is the only one of the two candidate rules
154
+ * that keeps the survival order meaningful: with stop-at-first-miss, one
155
+ * oversized low-priority block would evict every block after it,
156
+ * including the hard rules. Skipping cannot reorder anything, because
157
+ * ranks are still walked in order.
158
+ * 3. Blocks are joined by a blank line, which is counted as part of the
159
+ * block being added (a separator is never emitted for a block that did
160
+ * not fit).
161
+ * 4. If anything was dropped and the remaining budget admits it, one final
162
+ * line names the dropped ranks. It is ALL-OR-NOTHING: it is emitted only
163
+ * if it fits entirely, so no string is ever truncated mid-way and the
164
+ * byte accounting stays exact.
165
+ *
166
+ * Deterministic in the strict sense: the output is a pure function of
167
+ * (blocks, budgetBytes). No clock, no filesystem, no "best effort".
168
+ *
169
+ * `budgetBytes` is injectable so the rule above is testable at any size; it
170
+ * defaults to the declared ceiling and no production caller overrides it.
171
+ */
172
+ export declare function renderReinjectionCard(input: {
173
+ readonly blocks: ReadonlyArray<ReinjectionBlock>;
174
+ readonly budgetBytes?: number | undefined;
175
+ }): ReinjectionCard;
176
+ /** Facts the card is built from. Every field is independently optional. */
177
+ export type PostCompactReinjectionFacts = {
178
+ readonly projectRoot: string;
179
+ readonly sessionId: string | null;
180
+ readonly jobId: string | null;
181
+ readonly isJob: boolean;
182
+ readonly progress: {
183
+ readonly done: number;
184
+ readonly total: number;
185
+ readonly currentSlice: string;
186
+ } | null;
187
+ readonly latestRequest: string | null;
188
+ /** Which sources could not be read. Rendered nowhere in the card; reported. */
189
+ readonly unresolved: readonly string[];
190
+ };
191
+ /**
192
+ * Read every available fact. Never throws: each source is guarded separately
193
+ * and a failure is recorded in `unresolved` rather than propagated, because
194
+ * the caller is a session-start hook.
195
+ */
196
+ export declare function resolvePostCompactReinjectionFacts(input: {
197
+ readonly projectRoot: string;
198
+ readonly sessionId?: string | null | undefined;
199
+ }): PostCompactReinjectionFacts;
200
+ export type PostCompactReinjectionCard = {
201
+ /** True when a card was produced. False only if rendering itself failed. */
202
+ readonly ok: boolean;
203
+ readonly text: string;
204
+ readonly bytes: number;
205
+ readonly budgetBytes: number;
206
+ readonly sessionId: string | null;
207
+ readonly emittedRanks: readonly number[];
208
+ readonly droppedRanks: readonly number[];
209
+ readonly unresolved: readonly string[];
210
+ };
211
+ /**
212
+ * Build the card for a project. This is the whole production surface: read
213
+ * the facts, render them under the budget, return. NEVER throws — a thrown
214
+ * error here would surface on session start, which is the one place the
215
+ * absence of this feature must not be able to break anything.
216
+ */
217
+ export declare function buildPostCompactReinjectionCard(input: {
218
+ readonly projectRoot: string;
219
+ readonly sessionId?: string | null | undefined;
220
+ readonly budgetBytes?: number | undefined;
221
+ }): PostCompactReinjectionCard;