framein 0.0.4 → 0.0.5

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 CHANGED
@@ -1,44 +1,79 @@
1
- # Framein
2
-
3
- **Keep one work frame across Claude, Codex, and Gemini.**
4
-
5
- Start with one agent, challenge it with another, switch when needed, and close the work with
6
- validation.
7
-
8
- Framein is a local work-state layer for AI coding agents. Keep using Claude, Codex, Gemini,
9
- slash-command frameworks, skill packs, role-based workflows, or your own agent setup. Framein keeps
10
- the work underneath them stable: a task contract, decision trail, risk state, validation results, and
11
- a compact capsule the next model can read.
12
-
13
- ```text
14
- start in Claude -> challenge with Codex -> switch when needed -> validate before ship
15
- ```
16
-
17
- Status: **public pre-release** (`v0.0.4`). Runtime dependencies: **zero**. Required Node:
1
+ <p align="center">
2
+ <img src="docs/assets/framein-readme-header.svg" alt="Framein" width="760">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>One local work frame beneath the coding agents you already use.</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ Start with one agent. Challenge with another. Switch when needed. Ship with evidence.
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/framein"><img src="https://img.shields.io/npm/v/framein" alt="npm version"></a>
15
+ <img src="https://img.shields.io/badge/tests-249%20passing-brightgreen" alt="249 tests passing">
16
+ <img src="https://img.shields.io/badge/runtime-zero%20deps-blue" alt="zero runtime dependencies">
17
+ <img src="https://img.shields.io/badge/node-%3E%3D22.5-339933" alt="Node 22.5+">
18
+ <img src="https://img.shields.io/badge/license-MIT-lightgrey" alt="MIT license">
19
+ </p>
20
+
21
+ <p align="center">
22
+ <a href="https://www.framein.dev">Website</a> |
23
+ <a href="https://www.framein.dev/why">Developer note</a> |
24
+ <a href="docs/MANUAL.md">Manual</a> |
25
+ <a href="docs/INSTALL.md">Install guide</a> |
26
+ <a href="docs/FAQ.md">FAQ</a> |
27
+ <a href="SECURITY.md">Security</a>
28
+ </p>
29
+
30
+ Framein is a local work-state layer beneath the coding agents and harnesses you already use. Keep
31
+ using Claude Code, Codex, Gemini, Pi, OpenCode, slash-command frameworks, skill packs, role-based
32
+ workflows, or your own setup. Framein keeps the work underneath them stable: a task contract,
33
+ decision trail, risk state, validation results, and a compact capsule the next model can read.
34
+
35
+ ```text
36
+ start in Claude -> challenge with Codex -> switch when needed -> validate before ship
37
+ ```
38
+
39
+ Status: **public pre-release** (`v0.0.5`). Runtime dependencies: **zero**. Required Node:
18
40
  **22.5.0+**.
19
-
20
- [Website](https://www.framein.dev) · [Manual](docs/MANUAL.md) · [Install notes](docs/INSTALL.md) · [Code signing policy](docs/CODE_SIGNING.md) · [Security](SECURITY.md)
21
-
22
- ## Why Framein?
23
-
24
- Good PRDs, plans, ADRs, and skill packs help any model do better work. That is useful, and Framein is
25
- designed to coexist with it.
26
-
27
- The pain Framein targets starts when the work has to survive beyond one model or one clean session:
28
-
29
- - Your lead model gets stuck and repeats the same approach.
30
- - You want a different model to challenge the plan, diff, or risk.
31
- - You need to switch lead model because of quota, model fit, or a dead end.
32
- - The agent says the task is done before build and tests ran.
33
- - The next session gets a chat summary instead of the actual contract, validation state, failed attempts, and decisions.
34
-
35
- Framein does not replace the coding agent or pretend to be a full multi-agent cockpit. It keeps one
36
- local work frame under the agents you already use.
37
-
38
- ## Quick Start
39
-
40
- The public npm package is the intended install path, but `framein` is not published to npm yet.
41
- Until that publish step is complete, install from the public source repository:
41
+
42
+ Links:
43
+ - English: [Website](https://www.framein.dev) · [Developer note](https://www.framein.dev/why) · [Manual](docs/MANUAL.md) · [Install guide](docs/INSTALL.md) · [FAQ](docs/FAQ.md) · [Security](SECURITY.md)
44
+ - Korean: [웹사이트](https://www.framein.dev/ko) · [개발자 노트](https://www.framein.dev/ko/why) · [매뉴얼](docs/MANUAL.ko.md) · [설치 가이드](docs/INSTALL.ko.md)
45
+
46
+ ## Why Framein?
47
+
48
+ Good PRDs, plans, ADRs, and skill packs help any model do better work. That is useful, and Framein is
49
+ designed to coexist with it.
50
+
51
+ The pain Framein targets starts when the work has to survive beyond one model or one clean session:
52
+
53
+ - Your lead model gets stuck and repeats the same approach.
54
+ - You want a different model to challenge the plan, diff, or risk.
55
+ - You need to switch lead model because of quota, model fit, or a dead end.
56
+ - The agent says the task is done before build and tests ran.
57
+ - The next session gets a chat summary instead of the actual contract, validation state, failed attempts, and decisions.
58
+
59
+ Framein does not replace the coding agent or pretend to be a full multi-agent cockpit. It keeps one
60
+ local work frame under the agents you already use.
61
+
62
+ ## Quick Start
63
+
64
+ npm is the supported cross-platform install path today:
65
+
66
+ ```bash
67
+ npm install -g framein
68
+ framein --version
69
+ ```
70
+
71
+ Standalone executables are planned as an additional convenience path, mainly for users who do not
72
+ want to install Node/npm or who want to avoid Windows npm shim and PowerShell execution-policy
73
+ friction. They are not required to use Framein today. See
74
+ [Install notes](docs/INSTALL.md#6-standalone-executables).
75
+
76
+ If you want to test a local checkout instead:
42
77
 
43
78
  ```bash
44
79
  git clone https://github.com/framein-dev/framein.git
@@ -46,204 +81,205 @@ cd framein
46
81
  npm install
47
82
  npm run build
48
83
  npm install -g .
49
- framein --version
50
84
  ```
51
85
 
52
- After npm publication, the install path becomes:
86
+ Initialize a project:
53
87
 
54
88
  ```bash
55
- npm install -g framein
89
+ cd your-project
90
+ framein init
91
+ framein integrations install all --write
92
+ ```
93
+
94
+ Run the work loop:
95
+
96
+ ```bash
97
+ framein start "add Google OAuth, keep email login"
98
+ framein verify
99
+
100
+ # When another model should review or continue:
101
+ framein challenge "review the OAuth callback plan" --run
102
+ framein capsule codex
103
+
104
+ framein ship
105
+ ```
106
+
107
+ Use `challenge` when another model should review a claim or plan. In a live run, the reviewer returns
108
+ a structured verdict, the lead gets one bounded response, and Framein prints a decision brief for the
109
+ user to accept or reject. Use `capsule <agent>` when a different model should continue from the same
110
+ local facts. After the current CLI exits, Framein launches the next agent with a short handoff prompt;
111
+ the new agent still pulls facts with `framein capsule`. `verify` is a rehearsal; `ship` is the
112
+ enforced gate and exits non-zero when hard validation fails.
113
+
114
+ ## What You See
115
+
116
+ ```text
117
+ $ framein start "add Google OAuth, keep email login"
118
+ task contract
119
+ goal add Google OAuth, keep email login
120
+ preserve existing email login
121
+
122
+ $ framein challenge "OAuth callback stores state in session" --run
123
+ reviewer codex
124
+ verdict challenge
125
+ required add nonce/state validation
126
+ lead accepts required change
127
+ next framein decide accept "add nonce/state validation"
128
+
129
+ $ framein capsule gemini
130
+ next lead prepared from facts:
131
+ contract · diff · tests · decisions
132
+ exit the current agent; gemini opens and pulls the capsule first
133
+
134
+ $ framein ship
135
+ build ok · tests passed
136
+ risk high: auth/ touched
137
+ status ready with human gate
56
138
  ```
57
139
 
58
- Initialize a project:
59
-
60
- ```bash
61
- cd your-project
62
- framein init
63
- framein integrations install all --write
64
- ```
65
-
66
- Run the work loop:
67
-
68
- ```bash
69
- framein start "add Google OAuth, keep email login"
70
- framein verify
71
-
72
- # When another model should review or continue:
73
- framein challenge "review the OAuth callback plan" --run
74
- framein capsule codex
75
-
76
- framein ship
77
- ```
78
-
79
- Use `challenge` when another model should review a claim or plan. Use `capsule <agent>` when a
80
- different model should continue from the same local facts. `verify` is a rehearsal; `ship` is the
81
- enforced gate and exits non-zero when hard validation fails.
82
-
83
- ## What You See
84
-
85
- ```text
86
- $ framein start "add Google OAuth, keep email login"
87
- task contract
88
- goal add Google OAuth, keep email login
89
- preserve existing email login
90
-
91
- $ framein challenge "OAuth callback stores state in session" --run
92
- reviewer codex
93
- verdict change required
94
- required add nonce/state validation
95
-
96
- $ framein capsule gemini
97
- next lead prepared from facts:
98
- contract · diff · tests · decisions
99
-
100
- $ framein ship
101
- build ok · tests 42/42
102
- risk high: auth/ touched
103
- status ready with human gate
104
- ```
105
-
106
- The important part is not the text UI. It is that every command writes to the same local work frame,
107
- so terminal commands, native agent wrappers, MCP tools, and the next model all read the same facts.
108
-
109
- ## Core Commands
110
-
111
- | Need | Command | What it does |
112
- |---|---|---|
113
- | Define done | `framein start "<goal>"` | Creates a Task Contract: goal, acceptance, protected areas, non-goals |
114
- | Edit the contract | `framein task show` / `framein task amend ...` | Reviews or updates the definition of done |
115
- | Get second opinion | `framein challenge "<proposal>" --run` | Asks a different reviewer role for a bounded objection |
116
- | Switch model/session | `framein capsule [agent]` | Prepares the next lead from contract, diff, validation, ADRs, and ledger |
117
- | Run validation | `framein verify` | Runs configured build/test checks and records the result |
118
- | Check risk | `framein risk` | Flags sensitive blast radius from changed files |
119
- | Decide ship readiness | `framein ship` | Enforced validation and risk gate for commit/deploy readiness |
120
- | Recover from loops | `framein rescue` | Detects repeated failures or thrash and offers options |
121
- | Save a green point | `framein checkpoint <label>` | Records the current commit as last known good |
122
-
123
- Full reference: [`docs/MANUAL.md`](docs/MANUAL.md).
124
-
125
- ## Native Agent Surface
126
-
127
- Framein installs logic-less wrappers into the tools agents already understand:
128
-
129
- | Host | Surface | Example |
130
- |---|---|---|
131
- | Claude / Gemini | slash commands | `/fr:verify`, `/fr:ship`, `/fr:risk` |
132
- | Codex | project skills | `$fr-verify`, `$fr-ship`, `$fr-capsule` |
133
- | Terminal / CI | CLI + JSON | `framein ship --json` |
134
- | MCP-capable clients | local stdio MCP server | `framein mcp serve` |
135
-
136
- The generated agent commands expose the same agent-facing verbs across hosts:
137
-
138
- | Intent | Claude / Gemini | Codex skill |
139
- |---|---|---|
140
- | Start or reset the task contract | `/fr:start` | `$fr-start` |
141
- | Run build/test validation | `/fr:verify` | `$fr-verify` |
142
- | Check commit/deploy readiness | `/fr:ship` | `$fr-ship` |
143
- | Detect a repair loop | `/fr:rescue` | `$fr-rescue` |
144
- | Read current Framein state | `/fr:status` | `$fr-status` |
145
- | Ask an independent model to review | `/fr:challenge` | `$fr-challenge` |
146
- | Check changed-file risk | `/fr:risk` | `$fr-risk` |
147
- | Show or amend the task contract | `/fr:task` | `$fr-task` |
148
- | Prepare a model switch | `/fr:capsule` | `$fr-capsule` |
149
- | Resolve a reviewer debate | `/fr:decide` | `$fr-decide` |
150
-
151
- The wrappers do not contain product logic. They call the same local `framein` engine, so a command
152
- invoked from an agent, a terminal, or CI reads and writes the same contract, validation results, risk, and ledger.
153
-
154
- Windows note: generated wrappers use `framein.cmd` to avoid PowerShell execution-policy failures from
155
- the npm `.ps1` shim inside agent shells.
156
-
157
- ## How It Works
158
-
159
- ```text
160
- framein.store.json (git-friendly snapshot) <-> .frame/store.db (local cache)
161
- |
162
- v
163
- Task Contract · ADRs · memory · write locks · ledger · validation results
164
- |
165
- v
166
- managed block projection
167
- |
168
- +--> CLAUDE.md
169
- +--> AGENTS.md
170
- +--> GEMINI.md
171
- ```
172
-
173
- Important behavior:
174
-
175
- - `framein init` creates `.frame/store.db`, projects managed blocks, and ensures `.frame/` is ignored.
176
- - `framein export` writes `framein.store.json` when you want a git-canonical text snapshot.
177
- - Managed blocks are byte-identical across native context files.
178
- - User-authored text outside managed markers is preserved.
179
- - ADRs are append-only; corrections use superseding records.
180
- - Write locks are atomic conditional upserts with TTL.
181
- - Runtime dependencies stay at zero.
182
-
183
- ## Trust Boundary
184
-
185
- Framein is local-first:
186
-
187
- - No provider credentials are collected.
188
- - No remote credential relay or subscription pooling.
189
- - Claude, Codex, and Gemini keep their official CLI authentication.
190
- - Existing MCP servers and skills are detected/recommended, not proxied or cross-executed.
191
- - No terminal I/O (TTY) screen-scraping.
192
- - `framein trust` previews permission-bypass flags; it does not silently enable them.
193
- - Destructive recovery uses explicit flags, for example `framein rewind --force`.
194
- - Deployment remains a human gate.
195
-
196
- ## Current Status
197
-
198
- Solid in the current pre-release:
199
-
200
- - Store, import/export, managed-block projection
201
- - Task Contract, Verification Gate, Risk Gate, Rescue, Capsule, Challenge/Decide
202
- - Logic-less `/fr:*` and `$fr-*` wrappers
140
+ The important part is not the text UI. It is that every command writes to the same local work frame,
141
+ so terminal commands, native agent wrappers, MCP tools, and the next model all read the same facts.
142
+
143
+ ## Core Commands
144
+
145
+ | Need | Command | What it does |
146
+ |---|---|---|
147
+ | Define done | `framein start "<goal>"` | Creates a Task Contract: goal, acceptance, protected areas, non-goals |
148
+ | Edit the contract | `framein task show` / `framein task amend ...` | Reviews or updates the definition of done |
149
+ | Get second opinion | `framein challenge "<proposal>" --run` | Asks a different reviewer role for a structured verdict, one lead response, and a decision brief |
150
+ | Switch model/session | `framein capsule [agent]` | Prepares the next lead from contract, diff, validation, ADRs, and ledger |
151
+ | Run validation | `framein verify` | Runs configured build/test checks and records the result |
152
+ | Check risk | `framein risk` | Flags sensitive blast radius from changed files |
153
+ | Decide ship readiness | `framein ship` | Enforced validation and risk gate for commit/deploy readiness |
154
+ | Recover from loops | `framein rescue` | Detects repeated failures or thrash and offers options |
155
+ | Save a green point | `framein checkpoint <label>` | Records the current commit as last known good |
156
+
157
+ Full reference: [`docs/MANUAL.md`](docs/MANUAL.md).
158
+
159
+ ## Native Agent Surface
160
+
161
+ Framein installs logic-less wrappers into the tools agents already understand:
162
+
163
+ | Host | Surface | Example |
164
+ |---|---|---|
165
+ | Claude / Gemini | slash commands | `/fr:verify`, `/fr:ship`, `/fr:risk` |
166
+ | Codex | project skills | `$fr-verify`, `$fr-ship`, `$fr-capsule` |
167
+ | Terminal / CI | CLI + JSON | `framein ship --json` |
168
+ | MCP-capable clients | local stdio MCP server | `framein mcp serve` |
169
+
170
+ The generated agent commands expose the same agent-facing verbs across hosts:
171
+
172
+ | Intent | Claude / Gemini | Codex skill |
173
+ |---|---|---|
174
+ | Start or reset the task contract | `/fr:start` | `$fr-start` |
175
+ | Run build/test validation | `/fr:verify` | `$fr-verify` |
176
+ | Check commit/deploy readiness | `/fr:ship` | `$fr-ship` |
177
+ | Detect a repair loop | `/fr:rescue` | `$fr-rescue` |
178
+ | Read current Framein state | `/fr:status` | `$fr-status` |
179
+ | Ask an independent model to review | `/fr:challenge` | `$fr-challenge` |
180
+ | Check changed-file risk | `/fr:risk` | `$fr-risk` |
181
+ | Show or amend the task contract | `/fr:task` | `$fr-task` |
182
+ | Prepare a model switch | `/fr:capsule` | `$fr-capsule` |
183
+ | Resolve a reviewer debate | `/fr:decide` | `$fr-decide` |
184
+
185
+ The wrappers do not contain product logic. They call the same local `framein` engine, so a command
186
+ invoked from an agent, a terminal, or CI reads and writes the same contract, validation results, risk, and ledger.
187
+ Agent-native `challenge` wrappers add `--run --by <host>` internally, so users call `/fr:challenge`
188
+ or `$fr-challenge` without manually typing those flags. Codex repo skills are generated under
189
+ `.agents/skills/fr-<verb>/SKILL.md`.
190
+
191
+ Windows note: generated wrappers use `framein.cmd` to avoid PowerShell execution-policy failures from
192
+ the npm `.ps1` shim inside agent shells.
193
+
194
+ ## How It Works
195
+
196
+ ```text
197
+ framein.store.json (git-friendly snapshot) <-> .frame/store.db (local cache)
198
+ |
199
+ v
200
+ Task Contract · ADRs · memory · write locks · ledger · validation results
201
+ |
202
+ v
203
+ managed block projection
204
+ |
205
+ +--> CLAUDE.md
206
+ +--> AGENTS.md
207
+ +--> GEMINI.md
208
+ ```
209
+
210
+ Important behavior:
211
+
212
+ - `framein init` creates `.frame/store.db`, projects managed blocks, and ensures `.frame/` is ignored.
213
+ - `framein export` writes `framein.store.json` when you want a git-canonical text snapshot.
214
+ - Managed blocks are byte-identical across native context files.
215
+ - User-authored text outside managed markers is preserved.
216
+ - ADRs are append-only; corrections use superseding records.
217
+ - Write locks are atomic conditional upserts with TTL.
218
+ - Runtime dependencies stay at zero.
219
+
220
+ ## Trust Boundary
221
+
222
+ Framein is local-first:
223
+
224
+ - No provider credentials are collected.
225
+ - No remote credential relay or subscription pooling.
226
+ - Claude, Codex, and Gemini keep their official CLI authentication.
227
+ - Existing MCP servers and skills are detected/recommended, not proxied or cross-executed.
228
+ - No terminal I/O (TTY) screen-scraping.
229
+ - `framein trust` previews permission-bypass flags; it does not silently enable them.
230
+ - Destructive recovery uses explicit flags, for example `framein rewind --force`.
231
+ - Deployment remains a human gate.
232
+
233
+ ## Current Status
234
+
235
+ Solid in the current pre-release:
236
+
237
+ - Store, import/export, managed-block projection
238
+ - Task Contract, Verification Gate, Risk Gate, Rescue, Capsule, Challenge/Decide
239
+ - Logic-less `/fr:*` and `$fr-*` wrappers
203
240
  - MCP stdio server and registration helpers
204
241
  - Headless delegation to real CLIs where available
205
242
  - Windows author environment live-verified
206
- - `244` automated tests passing
243
+ - `249` automated tests passing as of 2026-06-28
207
244
 
208
245
  Still being validated:
209
246
 
210
- - public npm publication and post-publish install verification
211
247
  - signed standalone executable release hardening for Windows and macOS
212
248
  - multi-developer workflows
213
249
  - interactive lobby paths such as `/lead`, `/go`, and inline command palette
214
-
215
- ## Development
216
-
217
- ```bash
218
- npm install
219
- npm run build
220
- npm test
221
- ```
222
-
223
- Tests compile first and run from `dist/` through Node's built-in test runner.
224
-
225
- Useful focused commands:
226
-
227
- ```bash
228
- node --no-warnings --test dist/store.test.js
229
- node --no-warnings --test --test-name-pattern="supersede" dist/**/*.test.js
230
- node --no-warnings dist/cli.js <cmd>
231
- ```
232
-
233
- Node **22.5.0+** is required because Framein uses built-in `node:sqlite`.
234
-
235
- ## Documentation
236
-
237
- - Manual: [`docs/MANUAL.md`](docs/MANUAL.md)
238
- - Korean manual backup: [`docs/MANUAL.ko.md`](docs/MANUAL.ko.md)
239
- - Install troubleshooting: [`docs/INSTALL.md`](docs/INSTALL.md) / [`docs/INSTALL.ko.md`](docs/INSTALL.ko.md)
240
- - Code signing policy: [`docs/CODE_SIGNING.md`](docs/CODE_SIGNING.md)
241
- - Website: [framein.dev](https://www.framein.dev)
242
-
243
- ## License
244
-
245
- MIT. Framein by [Frameout](https://frameout.co.kr).
246
-
247
- Please keep the copyright and license notice when redistributing substantial
248
- portions of Framein. See [`NOTICE`](NOTICE) for suggested attribution and brand
249
- usage notes.
250
+
251
+ ## Development
252
+
253
+ ```bash
254
+ npm install
255
+ npm run build
256
+ npm test
257
+ ```
258
+
259
+ Tests compile first and run from `dist/` through Node's built-in test runner.
260
+
261
+ Useful focused commands:
262
+
263
+ ```bash
264
+ node --no-warnings --test dist/store.test.js
265
+ node --no-warnings --test --test-name-pattern="supersede" dist/**/*.test.js
266
+ node --no-warnings dist/cli.js <cmd>
267
+ ```
268
+
269
+ Node **22.5.0+** is required because Framein uses built-in `node:sqlite`.
270
+
271
+ ## Documentation
272
+
273
+ - Manual: [`docs/MANUAL.md`](docs/MANUAL.md)
274
+ - FAQ: [`docs/FAQ.md`](docs/FAQ.md)
275
+ - Korean manual backup: [`docs/MANUAL.ko.md`](docs/MANUAL.ko.md)
276
+ - Install troubleshooting: [`docs/INSTALL.md`](docs/INSTALL.md) / [`docs/INSTALL.ko.md`](docs/INSTALL.ko.md)
277
+ - Website: [framein.dev](https://www.framein.dev)
278
+
279
+ ## License
280
+
281
+ MIT. Framein by [Frameout](https://frameout.co.kr).
282
+
283
+ Please keep the copyright and license notice when redistributing substantial
284
+ portions of Framein. See [`NOTICE`](NOTICE) for suggested attribution and brand
285
+ usage notes.
package/dist/adr.js CHANGED
@@ -1,17 +1,17 @@
1
- // ADR digest: a compact index embedded into the projected native files.
2
- // Full ADRs live in the store (queried live via MCP); files carry only a digest.
3
- export function buildAdrDigest(adrs, opts = {}) {
4
- if (adrs.length === 0)
5
- return '_No decisions recorded yet._';
6
- const max = opts.max ?? 10;
7
- // Derived (append-only): an ADR is superseded only if a LATER one references it.
8
- const supersededIds = new Set(adrs.filter((a) => a.supersedes != null && a.id > a.supersedes).map((a) => a.supersedes));
9
- const recent = [...adrs].sort((a, b) => b.id - a.id).slice(0, max);
10
- const lines = recent.map((a) => {
11
- const status = supersededIds.has(a.id) ? 'superseded' : a.status;
12
- return `- [ADR-${a.id}] ${a.title} (${status})`;
13
- });
14
- const overflow = adrs.length > recent.length
15
- ? `\n- …and ${adrs.length - recent.length} earlier decision(s)` : '';
16
- return `${adrs.length} decision(s) recorded. Latest:\n${lines.join('\n')}${overflow}`;
17
- }
1
+ // ADR digest: a compact index embedded into the projected native files.
2
+ // Full ADRs live in the store (queried live via MCP); files carry only a digest.
3
+ export function buildAdrDigest(adrs, opts = {}) {
4
+ if (adrs.length === 0)
5
+ return '_No decisions recorded yet._';
6
+ const max = opts.max ?? 10;
7
+ // Derived (append-only): an ADR is superseded only if a LATER one references it.
8
+ const supersededIds = new Set(adrs.filter((a) => a.supersedes != null && a.id > a.supersedes).map((a) => a.supersedes));
9
+ const recent = [...adrs].sort((a, b) => b.id - a.id).slice(0, max);
10
+ const lines = recent.map((a) => {
11
+ const status = supersededIds.has(a.id) ? 'superseded' : a.status;
12
+ return `- [ADR-${a.id}] ${a.title} (${status})`;
13
+ });
14
+ const overflow = adrs.length > recent.length
15
+ ? `\n- …and ${adrs.length - recent.length} earlier decision(s)` : '';
16
+ return `${adrs.length} decision(s) recorded. Latest:\n${lines.join('\n')}${overflow}`;
17
+ }
package/dist/anomaly.js CHANGED
@@ -1,39 +1,39 @@
1
- // Audit cadence (ADR-0005, F-AUDIT-3): detect "thrash" signals from the task ledger so a
2
- // reviewer can be pulled in only when an agent is going in circles — not on every turn.
3
- // Pure function over ledger entries; thresholds are tunable (PRD §11.8).
4
- export function detectThrash(entries, opts = {}) {
5
- const editThreshold = opts.repeatedEdits ?? 3;
6
- const failThreshold = opts.repeatedFailures ?? 2;
7
- const noProgress = opts.noProgressTurns ?? 5;
8
- const signals = [];
9
- const editCounts = new Map();
10
- const failCounts = new Map();
11
- for (const e of entries) {
12
- if (e.kind === 'edit' && e.target)
13
- editCounts.set(e.target, (editCounts.get(e.target) ?? 0) + 1);
14
- if (e.kind === 'test-fail' && e.target)
15
- failCounts.set(e.target, (failCounts.get(e.target) ?? 0) + 1);
16
- }
17
- for (const [target, count] of editCounts) {
18
- if (count >= editThreshold)
19
- signals.push({ kind: 'repeated-edits', target, count, message: `'${target}' edited ${count}× — possible thrash loop` });
20
- }
21
- for (const [target, count] of failCounts) {
22
- if (count >= failThreshold)
23
- signals.push({ kind: 'repeated-failure', target, count, message: `'${target}' failed ${count}× — stuck on the same test` });
24
- }
25
- // turns accumulated since the last real progress (edit/commit). Other events (ask,
26
- // test-fail) are neither progress nor turns — they're skipped, not counted.
27
- let trailingTurns = 0;
28
- for (let i = entries.length - 1; i >= 0; i--) {
29
- const k = entries[i].kind;
30
- if (k === 'edit' || k === 'commit')
31
- break;
32
- if (k === 'turn')
33
- trailingTurns++;
34
- }
35
- if (trailingTurns >= noProgress) {
36
- signals.push({ kind: 'no-progress', count: trailingTurns, message: `${trailingTurns} turns without an edit/commit — may be going in circles` });
37
- }
38
- return signals;
39
- }
1
+ // Audit cadence (ADR-0005, F-AUDIT-3): detect "thrash" signals from the task ledger so a
2
+ // reviewer can be pulled in only when an agent is going in circles — not on every turn.
3
+ // Pure function over ledger entries; thresholds are tunable (PRD §11.8).
4
+ export function detectThrash(entries, opts = {}) {
5
+ const editThreshold = opts.repeatedEdits ?? 3;
6
+ const failThreshold = opts.repeatedFailures ?? 2;
7
+ const noProgress = opts.noProgressTurns ?? 5;
8
+ const signals = [];
9
+ const editCounts = new Map();
10
+ const failCounts = new Map();
11
+ for (const e of entries) {
12
+ if (e.kind === 'edit' && e.target)
13
+ editCounts.set(e.target, (editCounts.get(e.target) ?? 0) + 1);
14
+ if (e.kind === 'test-fail' && e.target)
15
+ failCounts.set(e.target, (failCounts.get(e.target) ?? 0) + 1);
16
+ }
17
+ for (const [target, count] of editCounts) {
18
+ if (count >= editThreshold)
19
+ signals.push({ kind: 'repeated-edits', target, count, message: `'${target}' edited ${count}× — possible thrash loop` });
20
+ }
21
+ for (const [target, count] of failCounts) {
22
+ if (count >= failThreshold)
23
+ signals.push({ kind: 'repeated-failure', target, count, message: `'${target}' failed ${count}× — stuck on the same test` });
24
+ }
25
+ // turns accumulated since the last real progress (edit/commit). Other events (ask,
26
+ // test-fail) are neither progress nor turns — they're skipped, not counted.
27
+ let trailingTurns = 0;
28
+ for (let i = entries.length - 1; i >= 0; i--) {
29
+ const k = entries[i].kind;
30
+ if (k === 'edit' || k === 'commit')
31
+ break;
32
+ if (k === 'turn')
33
+ trailingTurns++;
34
+ }
35
+ if (trailingTurns >= noProgress) {
36
+ signals.push({ kind: 'no-progress', count: trailingTurns, message: `${trailingTurns} turns without an edit/commit — may be going in circles` });
37
+ }
38
+ return signals;
39
+ }