liteagents 3.6.0 → 3.7.0
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/CHANGELOG.md +68 -0
- package/package.json +1 -1
- package/packages/ampcode/skills/branch-review/SKILL.md +20 -3
- package/packages/ampcode/skills/docs-builder/SKILL.md +168 -70
- package/packages/ampcode/skills/docs-builder/docs-builder.cjs +325 -70
- package/packages/ampcode/skills/release/SKILL.md +38 -8
- package/packages/ampcode/skills/security/SKILL.md +6 -3
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package-lock.json +3 -3
- package/packages/claude/skills/branch-review/SKILL.md +20 -3
- package/packages/claude/skills/docs-builder/SKILL.md +168 -69
- package/packages/claude/skills/docs-builder/docs-builder.cjs +325 -70
- package/packages/claude/skills/release/SKILL.md +38 -8
- package/packages/claude/skills/security/SKILL.md +6 -3
- package/packages/droid/commands/branch-review.md +20 -3
- package/packages/droid/commands/docs-builder/docs-builder.cjs +325 -70
- package/packages/droid/commands/docs-builder.md +168 -70
- package/packages/droid/commands/release.md +38 -8
- package/packages/droid/commands/security.md +6 -3
- package/packages/opencode/command/branch-review.md +20 -3
- package/packages/opencode/command/docs-builder/docs-builder.cjs +325 -70
- package/packages/opencode/command/docs-builder.md +168 -70
- package/packages/opencode/command/release.md +38 -8
- package/packages/opencode/command/security.md +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [3.7.0] - 2026-09-11
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`docs-builder` sorts a repo that has no `docs/` yet.** With no folder named and no
|
|
14
|
+
`docs/` directory, `discover` / `reorg` scan the repo's loose `.md` files instead of
|
|
15
|
+
stopping. Protected entry-point files (`README.md`, `CLAUDE.md`, `CHANGELOG.md`,
|
|
16
|
+
`AGENTS.md`, ...) never move.
|
|
17
|
+
- **`reorg <dir>` / `discover <dir>`** re-check a single bucket that got messy.
|
|
18
|
+
- **`docs-builder` asks before committing.** After a run, the skill shows exactly which
|
|
19
|
+
files the run touched and asks; it refuses to commit on `main` / `master`.
|
|
20
|
+
- `discover` warns that `ROOT=` is ignored and says to pass the folder as an argument.
|
|
21
|
+
- **`docs-builder` gains a fourth bucket, `wiki/`**, for generic non-product-specific
|
|
22
|
+
knowledge (conventions, how-tos, standards, reference) — `product/`, `wiki/`, `logs/`,
|
|
23
|
+
`archive/` is now the full layout everywhere (SKILL.md, README, `index-flat`).
|
|
24
|
+
- **`docs-builder` classifies from headings, not just filenames.** A doc with no
|
|
25
|
+
filename signal now also gets a weaker, case-insensitive prior from its own H1 + first
|
|
26
|
+
3 H2s.
|
|
27
|
+
- **`logs/` is the one bucket that nests**, one level, grouped by the file's own first
|
|
28
|
+
path segment under `docs/` (a special subfolder like `docs/fwd/` stays one group
|
|
29
|
+
however deep a file sits inside it, unless that segment is itself a bucket name); loose
|
|
30
|
+
files stay flat. `index-flat`'s `## Logs` section groups rows the same way.
|
|
31
|
+
|
|
32
|
+
### Security
|
|
33
|
+
- `hono` bumped 4.13.0 → 4.13.7 (indirect dependency, Dependabot).
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- **`docs-builder`'s commit recipe mishandled non-ASCII renames, silently swept up the
|
|
37
|
+
operator's own uncommitted edits, and broke under `REPO=<subdir>`.** A moved file with a
|
|
38
|
+
non-ASCII name (e.g. `café.md`) lost its rename because `ls-tree` C-quoted the path,
|
|
39
|
+
dropping it from `commit-files.txt`; now uses `ls-tree -z`. A pre-run dirty snapshot now
|
|
40
|
+
names any listed file that already carried the user's own uncommitted edits
|
|
41
|
+
(`commit-dirty.txt`), and `SKILL.md` surfaces that in the commit question instead of
|
|
42
|
+
silently including them. `REPO=<subdir>` no longer writes lists to a doubled path or
|
|
43
|
+
exits 128 — lists are written under `ARTIFACTS` directly and the recipe uses `git -C`
|
|
44
|
+
when `REPO` isn't the cwd.
|
|
45
|
+
- **`docs-builder`'s dirty-file warning no longer flags the tool's own log/index.** `reorg`
|
|
46
|
+
appends to `docs/log.md` and rebuilds `docs/index.md` as part of its own run, so the
|
|
47
|
+
following `apply-reorg` warned that those tool-owned files carried "your own uncommitted
|
|
48
|
+
edits." Both are now excluded from the dirty-file check; every other listed file is still
|
|
49
|
+
checked.
|
|
50
|
+
- **`docs-builder`'s link rewriter now touches only `.md` files, and `reorg <dir>` /
|
|
51
|
+
`discover <dir>` re-check an already-classified bucket instead of leaving it a false
|
|
52
|
+
SKIP.** Previously the rewriter could open signed JSON job specs or byte-signed scripts
|
|
53
|
+
outside `docs/`, and the commit advisory could stage unrelated files alongside the run's
|
|
54
|
+
own changes; the advisory now names exactly the staged renames, unstaged rewrites, and
|
|
55
|
+
distinct outside-`docs/` locations touched by that run.
|
|
56
|
+
- **`/branch-review` and `/security` effort level no longer cuts which checks run** — only
|
|
57
|
+
how many findings are reported. Stage 1's fail-first check is one red run per changed
|
|
58
|
+
test file, never a sample; a check that can't run is written `NOT RUN: <reason>` on a
|
|
59
|
+
new `checks:` line, visible but non-blocking (`/release` still reads only `coverage:`).
|
|
60
|
+
Stage 2's secrets scan always covers all history, never narrowed to the review range.
|
|
61
|
+
- **`docs-builder`'s default scan scope no longer sweeps the whole repo.** With no
|
|
62
|
+
directory named, `discover` / `reorg` now scan only root-level `.md` files
|
|
63
|
+
(non-recursive) plus everything under `docs/` (recursive); `discover <dir>` /
|
|
64
|
+
`reorg <dir>` are unchanged. `PROTECTED_NAMES` is now matched case-insensitively at
|
|
65
|
+
every call site, so files like `readme.md` or `Claude.md` are protected too, not just
|
|
66
|
+
their exact-case forms. `docs/product`, `docs/wiki`, and `docs/logs` are now
|
|
67
|
+
re-checked on every bare run (only `docs/archive` stays frozen) — this also fixed an
|
|
68
|
+
ordering bug where a resident row could be bumped off its own bucket by an unrelated
|
|
69
|
+
same-basename row visited earlier.
|
|
70
|
+
- **`/release`'s docs sweep is now required and can't be skipped or sampled.** A prior
|
|
71
|
+
run had built a `CHANGELOG` from commit subjects alone (missing changes only named in
|
|
72
|
+
commit bodies) and called a stale README line "already stale before this branch"
|
|
73
|
+
without checking. Phase 2 is now three required passes — list every change from commit
|
|
74
|
+
bodies, map each to a CHANGELOG heading, and grep docs for every replaced string,
|
|
75
|
+
proving any "pre-existing" claim against the merge-base — with one evidence row per
|
|
76
|
+
doc; a "no change" with no evidence now fails the phase.
|
|
77
|
+
|
|
10
78
|
## [3.6.0] - 2026-09-05
|
|
11
79
|
|
|
12
80
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liteagents",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "AI development toolkit with 10 specialized agents and 13 capabilities including live-canvas UI design with click-to-annotate feedback. Simple one-question installer for Claude, Opencode, Ampcode, and Droid.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -140,6 +140,13 @@ governs **stage 1 only**:
|
|
|
140
140
|
- **high / max** — broader coverage; uncertain findings are allowed, but each
|
|
141
141
|
must be labelled uncertain.
|
|
142
142
|
|
|
143
|
+
**No shortcuts.** The level decides how many findings you report, never which
|
|
144
|
+
checks you run. Every check this file calls required runs at every level —
|
|
145
|
+
never cut or sample one "given the effort level", the branch size, or time. If
|
|
146
|
+
a check truly cannot run, write `NOT RUN: <reason>` for it on the `checks:`
|
|
147
|
+
line of the report and the review record. That is a visible gap, not a
|
|
148
|
+
blocker and not a pass.
|
|
149
|
+
|
|
143
150
|
**Stage 2 (security) always runs full, at every level.** A shallow security
|
|
144
151
|
pass is worse than none — it reads as coverage while missing the class of bug
|
|
145
152
|
that costs the most.
|
|
@@ -189,7 +196,10 @@ carries the reproduction.
|
|
|
189
196
|
location outside the repo; the tree must still be clean at exit. A test that
|
|
190
197
|
passes against both the buggy and the fixed source is a tautology and proves
|
|
191
198
|
nothing. Flag every one you find, and say so explicitly when the tests are
|
|
192
|
-
the branch's only evidence for its claims.
|
|
199
|
+
the branch's only evidence for its claims. **Required, every test file the
|
|
200
|
+
diff adds or changes — one red run per file is enough; checking a sample of
|
|
201
|
+
the files is a skip.** Count them as `fail-first N/M files` on the
|
|
202
|
+
`checks:` line.
|
|
193
203
|
- **Maintainability.** Complexity, naming, duplication — only when material.
|
|
194
204
|
|
|
195
205
|
## Stage 2 — Security (always full)
|
|
@@ -204,7 +214,10 @@ If `security.md` cannot be found, run what you can from the list above and
|
|
|
204
214
|
|
|
205
215
|
This stage is repo- and history-scoped, not diff-scoped: a key committed forty
|
|
206
216
|
commits ago, an unbounded route the diff never touched, or a missing row
|
|
207
|
-
policy on a table the new code now reads are all in scope.
|
|
217
|
+
policy on a table the new code now reads are all in scope. **The review range
|
|
218
|
+
never narrows this stage** — even when you were handed `main..HEAD`, the
|
|
219
|
+
secrets scan covers every commit on every branch (`security.md` item 1 has
|
|
220
|
+
the command).
|
|
208
221
|
|
|
209
222
|
## Stage 3 — Verify (adversarial)
|
|
210
223
|
Findings are claims, not facts. **Try to break each one, not to confirm it** —
|
|
@@ -292,7 +305,10 @@ uncertain).
|
|
|
292
305
|
|
|
293
306
|
Then a coverage line: stage 1 at level `<level>`, stage 2 full, stage 3 —
|
|
294
307
|
each `ran ✓/✗` with its evidence. A stage you did not actually run is a **✗**, never an
|
|
295
|
-
assumed pass.
|
|
308
|
+
assumed pass. Then a `checks:` line for the two checks most often cut short:
|
|
309
|
+
`fail-first N/M files` and `secrets-history all-branches` (or `NOT RUN:
|
|
310
|
+
<reason>` for either). An N below M, or a NOT RUN, is reported as-is — it
|
|
311
|
+
does not block.
|
|
296
312
|
|
|
297
313
|
**Write the review record** to `.amp/remember/last-review.md`, overwriting
|
|
298
314
|
it. `/release` reads this file; a SHA that lives only in a chat message is
|
|
@@ -311,6 +327,7 @@ level: <low | medium | high | max>
|
|
|
311
327
|
verdict: <ready | blocked>
|
|
312
328
|
date: <YYYY-MM-DD>
|
|
313
329
|
coverage: stage1 <ran|NOT RUN>, stage2 <ran|NOT RUN>, stage3 <ran|NOT RUN>
|
|
330
|
+
checks: fail-first <N/M files|NOT RUN: reason>, secrets-history <all-branches|NOT RUN: reason>
|
|
314
331
|
blockers:
|
|
315
332
|
- <file:line> · <one-sentence claim, no scenario, no suggested fix>
|
|
316
333
|
```
|
|
@@ -5,7 +5,6 @@ argument-hint: [reorg | cleanup <file.md> | search <query words...> — empty as
|
|
|
5
5
|
allowed-tools: Read, Write, Edit, Grep, Glob, Task, AskUserQuestion, Bash(node:*), Bash(git:*), Bash(rg:*)
|
|
6
6
|
disable-model-invocation: true
|
|
7
7
|
---
|
|
8
|
-
|
|
9
8
|
# docs-builder
|
|
10
9
|
|
|
11
10
|
Keep project docs **current, complete and findable**, and split a file when it outgrows
|
|
@@ -51,8 +50,13 @@ Every command below is `node $DB …`; everything the script writes (`docs/.docs
|
|
|
51
50
|
JSON state, `docs/index.md`, the ledger, the log, the config pointer) lands under the target
|
|
52
51
|
repo. `REPO=` is optional and only needed when not running from the repo root.
|
|
53
52
|
|
|
54
|
-
**With an argument** (`reorg`, `cleanup <file>`, or `search <query words...>`) — run that
|
|
55
|
-
directly, no question asked.
|
|
53
|
+
**With an argument** (`reorg [dir]`, `cleanup <file>`, or `search <query words...>`) — run that
|
|
54
|
+
mode directly, no question asked. `reorg <dir>` re-checks every doc already inside `<dir>` —
|
|
55
|
+
useful to scope a check to one directory outside the default scan. A **bare** `reorg`/`discover`
|
|
56
|
+
(no argument) already re-checks `docs/product/`, `docs/wiki/` and `docs/logs/` on every run —
|
|
57
|
+
only `docs/archive/` stays frozen and out of scope. Scan scope with no argument is otherwise
|
|
58
|
+
narrow by design: root-level `.md` files (top level only, not recursive) plus everything under
|
|
59
|
+
`docs/` (recursive) — nothing else in the repo is ever listed or moved.
|
|
56
60
|
|
|
57
61
|
**Bare `/docs-builder`, no argument — ALWAYS ask, never auto-detect.** Run `due` first and
|
|
58
62
|
put its one-line verdict in the question text so the choice is informed. Then use
|
|
@@ -60,9 +64,9 @@ put its one-line verdict in the question text so the choice is informed. Then us
|
|
|
60
64
|
|
|
61
65
|
> **Question: What should docs-builder do?**
|
|
62
66
|
>
|
|
63
|
-
> - **First run** — sort
|
|
64
|
-
> too big into pages and index them. Use
|
|
65
|
-
> docs-builder has never run here.
|
|
67
|
+
> - **First run** — sort root-level `.md` files and everything under `docs/` into
|
|
68
|
+
> product/wiki/logs/archive, then split anything too big into pages and index them. Use
|
|
69
|
+
> when docs are a pile of loose files, or docs-builder has never run here.
|
|
66
70
|
> - **Docs drift** — docs moved on since the last run: report what changed, rebuild the
|
|
67
71
|
> index, re-run lint. Nothing is restructured and nothing is split.
|
|
68
72
|
|
|
@@ -89,8 +93,9 @@ read-only — no model cost, no interview, nothing moves.
|
|
|
89
93
|
bucket). `bucket` itself starts **empty** on every row.
|
|
90
94
|
2. **The classification interview.** Feed the model the WHOLE plan table (file, h1, snip,
|
|
91
95
|
lines, suggested+reason) in one call and have it fill `bucket` for every row —
|
|
92
|
-
`product`/`logs`/`archive` — with a one-line reason: honour that a SHOUTED
|
|
93
|
-
status is near-conclusive for `archive` and that `suggested` is a prior, not
|
|
96
|
+
`product`/`wiki`/`logs`/`archive` — with a one-line reason: honour that a SHOUTED
|
|
97
|
+
self-declared status is near-conclusive for `archive` and that `suggested` is a prior, not
|
|
98
|
+
an authority.
|
|
94
99
|
The model writes its answers straight into `reorg-plan.json`. Then show the user the full
|
|
95
100
|
resulting table via `AskUserQuestion` (approve all / correct specific rows / abort) — a
|
|
96
101
|
correction changes the plan file before anything moves.
|
|
@@ -102,12 +107,10 @@ read-only — no model cost, no interview, nothing moves.
|
|
|
102
107
|
to split** (any, all, none). Only then run `cleanup <file>` (Mode 1) on each chosen file —
|
|
103
108
|
`cleanup` itself prints the estimated split cost for that one file, then a mechanical
|
|
104
109
|
shape report, then stops for its own interview (Mode 1, step 1b) before anything else runs.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
ledger, and without the stamp `due` stays NOT due, the picker's verdict stays uninformed,
|
|
110
|
-
and `/remember`'s docs nudge never fires.
|
|
110
|
+
Add `docs/.docs-builder/` to `.gitignore` if it is not already ignored: it is machine state,
|
|
111
|
+
regenerated every run, and the ledger stamp is per-clone by design — it must never ride into
|
|
112
|
+
history on a later `git add -A`. Then follow "Finishing a run" (below `apply-reorg`, Mode 0
|
|
113
|
+
step 3) to commit and stamp the ledger.
|
|
111
114
|
|
|
112
115
|
The two stops are deliberate and different. Step 2 guards *correctness* — the interview and
|
|
113
116
|
the user's approval, before a single file moves. Step 3's follow-up guards *cost* — splitting
|
|
@@ -118,7 +121,7 @@ when they pick "First run". Never split N files in one shot on an unseen list.
|
|
|
118
121
|
first, if a ledger stamp exists, then it runs `discover`. If any row's `bucket` is still
|
|
119
122
|
empty (true on a genuine first run, or when new files appeared since the last classification),
|
|
120
123
|
`reorg` **stops right there** and prints what to do next — it never silently proceeds past an
|
|
121
|
-
unclassified plan.
|
|
124
|
+
unclassified plan. Otherwise follow "Finishing a run" once it's done. Once
|
|
122
125
|
the plan is fully classified (an already-sorted corpus's re-run
|
|
123
126
|
carries its prior classifications forward automatically — see "Discover is idempotent"
|
|
124
127
|
below), `reorg` continues straight through `apply-reorg` → `lint`, no further stop, so
|
|
@@ -131,7 +134,7 @@ already sorted: nothing new to classify, so the interview gate never fires.
|
|
|
131
134
|
|
|
132
135
|
| Mode | Menu option | Does | Destructive |
|
|
133
136
|
|---|---|---|---|
|
|
134
|
-
| `/docs-builder reorg` (discover, classification interview, confirm, then apply-reorg) | *First run*, steps 1-3 | classify a WHOLE corpus into product/logs/archive | no (moves are `git mv`, plan classified and reviewed first) |
|
|
137
|
+
| `/docs-builder reorg` (discover, classification interview, confirm, then apply-reorg) | *First run*, steps 1-3 | classify a WHOLE corpus into product/wiki/logs/archive | no (moves are `git mv`, plan classified and reviewed first) |
|
|
135
138
|
| `/docs-builder cleanup <file>` | *First run*, step 3's split question | measure ONE named oversized doc (cost, scan, heading shape) → **stops for the interview** | no (measure-only; original preserved) |
|
|
136
139
|
| `/docs-builder reorg` (bare `docs-builder.cjs reorg`) | *Docs drift* | due's drift summary (if a ledger stamp exists) + discover → (stops here if anything is still unclassified) → apply-reorg → lint, whole corpus | no |
|
|
137
140
|
| `/docs-builder search <query words...>` | *(none — explicit-argument mode only, never offered in the bare picker)* | BM25-rank sections of `docs/.docs-builder/outline.json` against the query, read-only | no |
|
|
@@ -155,16 +158,31 @@ docs/
|
|
|
155
158
|
README.md entry point, referenced from CLAUDE.md
|
|
156
159
|
index.md GENERATED by index-flat/apply-reorg/cleanup-apply. never hand-edited.
|
|
157
160
|
READER-FACING. The WHOLE-CORPUS map — the only file with a completeness
|
|
158
|
-
guarantee. ## Product, ## Logs, ## Archive.
|
|
161
|
+
guarantee. ## Product, ## Logs (grouped by subdir), ## Archive.
|
|
159
162
|
log.md append-only: ## [DATE] operation | description — written by
|
|
160
163
|
`archive`, `apply-reorg`, `validate`, and `reorg`; NOT written by
|
|
161
164
|
read-only commands (`due`, `search`, `discover`).
|
|
162
|
-
product/
|
|
163
|
-
(`git mv`);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
product/ docs ABOUT THE PRODUCT ITSELF — specs, PRDs, designs, guides for this
|
|
166
|
+
product. FLAT, no subdirs. `apply-reorg` MOVES files here (`git mv`);
|
|
167
|
+
content is never rewritten. Re-checked on EVERY reorg (files sitting here
|
|
168
|
+
are re-classified along with everything else — agents litter buckets
|
|
169
|
+
over time).
|
|
170
|
+
wiki/ GENERIC knowledge for the whole repo, not product-specific — conventions,
|
|
171
|
+
how-tos, standards, reference. FLAT, no subdirs. Also where Mode 1
|
|
172
|
+
(`cleanup`)'s split page writers put synthesised pages. Re-checked every
|
|
173
|
+
reorg, same as product/.
|
|
174
|
+
logs/ ONE-TIME, specific, timely knowledge tied to one section/scenario/case —
|
|
175
|
+
POCs, experiments, investigations, incident/session write-ups, reports. The
|
|
176
|
+
ONLY bucket that may nest, ONE level: `docs/logs/<group>/*.md` — the group
|
|
177
|
+
is the FIRST path segment under `docs/` (a special subfolder is one
|
|
178
|
+
self-explanatory group, e.g. every POC under `docs/fwd/` stays together as
|
|
179
|
+
`fwd`, however deep a file actually sits inside it), UNLESS that segment is
|
|
180
|
+
itself a bucket name (`product`/`wiki`/`archive` → flat; `logs` → the group
|
|
181
|
+
is the SECOND segment instead). A file with no first segment — loose at the
|
|
182
|
+
repo root, or directly under `docs/` — stays flat. Re-checked every reorg,
|
|
183
|
+
same as product/wiki/.
|
|
184
|
+
archive/ what got cleaned up: self-declared dead. FROZEN — never re-checked, never
|
|
185
|
+
walked by a bare `discover`/`reorg` at all. Originals are BYTE-FROZEN:
|
|
168
186
|
nothing under here is ever a rewrite target, so a doc lands byte-identical
|
|
169
187
|
to what it carried in (a clean R100 rename) and stays that way. Links
|
|
170
188
|
elsewhere POINTING AT it are still repaired. History via `git mv`.
|
|
@@ -202,14 +220,19 @@ moved the problem (it then clobbered `outline.json` across concurrent splits ins
|
|
|
202
220
|
**the themed index was removed outright, 2026-08-24**. One index, rebuilt on every reorg and
|
|
203
221
|
after every split, is the whole design.
|
|
204
222
|
|
|
205
|
-
**Never moved — enforced in code, not just documented** (`PROTECTED_NAMES` / `
|
|
223
|
+
**Never moved — enforced in code, not just documented** (`PROTECTED_NAMES` / `isProtectedName` /
|
|
224
|
+
`walkMd`):
|
|
206
225
|
|
|
207
|
-
- **Files, at any depth:** `README.md`, `index.md`, `log.md`,
|
|
208
|
-
`
|
|
209
|
-
|
|
226
|
+
- **Files, at any depth, matched CASE-INSENSITIVELY:** `README.md`, `index.md`, `log.md`,
|
|
227
|
+
`CHANGELOG.md`, `LICENSE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`,
|
|
228
|
+
`CLAUDE.md`, `AGENTS.md`, `AGENT.md` — so `readme.md`, `Claude.md`, `changelog.md`,
|
|
229
|
+
`agents.md`, etc. are protected too, not just their exact-case forms. Bare `LICENSE`/`NOTICE`
|
|
230
|
+
have no `.md` extension, so the walker never sees them.
|
|
210
231
|
- **Directories:** every dot-dir (`.git/`, `.github/`, `.claude/`, `.factory/`, `.opencode/`,
|
|
211
|
-
`.amp/`, `.docs-builder/`) plus `node_modules
|
|
212
|
-
|
|
232
|
+
`.amp/`, `.docs-builder/`) plus `node_modules/`. `archive/` is skipped unconditionally by a
|
|
233
|
+
bare `discover`/`reorg` (frozen, never re-checked); `product/`, `wiki/` and `logs/` are
|
|
234
|
+
entered and re-checked on that same bare run — only an explicit `discover <dir>` naming one
|
|
235
|
+
of the reserved names directly bypasses this at all four.
|
|
213
236
|
|
|
214
237
|
---
|
|
215
238
|
|
|
@@ -229,11 +252,31 @@ purpose, not silently dropped.
|
|
|
229
252
|
### 1. Discover (script) — enriches and PROPOSES, never classifies, never moves
|
|
230
253
|
|
|
231
254
|
```bash
|
|
232
|
-
node $DB discover #
|
|
255
|
+
node $DB discover # root-level .md files (top level only) + everything under docs/
|
|
256
|
+
node $DB discover <dir> # scopes to exactly <dir> instead — e.g. a directory outside docs/
|
|
233
257
|
```
|
|
234
258
|
|
|
235
|
-
|
|
236
|
-
|
|
259
|
+
**Scan scope with no argument is deliberately narrow:** (a) `.md` files sitting directly at
|
|
260
|
+
the repo root (top level, not recursive) and (b) everything under `docs/` (recursive, entering
|
|
261
|
+
`product/`, `wiki/` and `logs/` — only `docs/archive/` stays frozen and skipped). Every other
|
|
262
|
+
`.md` file anywhere else in the repo is out of scope entirely — never listed, never moved,
|
|
263
|
+
whether or not `docs/` exists. `PROTECTED_NAMES` still applies at the repo root (case-
|
|
264
|
+
insensitively — see Layout above), so `README.md`, `CLAUDE.md`, `CHANGELOG.md`, `AGENTS.md`,
|
|
265
|
+
`AGENT.md`, `LICENSE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `index.md`
|
|
266
|
+
and `log.md` are never planned or moved. `ROOT=` is NOT read by this script — setting it
|
|
267
|
+
prints a `WARN`; pass the folder as the argument.
|
|
268
|
+
|
|
269
|
+
**Because `product/`, `wiki/` and `logs/` are re-checked every bare run,** a file already
|
|
270
|
+
sitting in one of them is a REAL row too, not folded away — its `suggested` bucket defaults to
|
|
271
|
+
wherever it already sits (a stronger signal, like a filename token or a SHOUTED status word,
|
|
272
|
+
can still override that). It still needs `bucket` filled by the interview like any other row
|
|
273
|
+
(even if the answer is "yes, stays put") — that settles after one classify+apply pass and
|
|
274
|
+
carries forward from then on, same as everything else. An explicit `discover <dir>` still
|
|
275
|
+
scopes to exactly that one directory, unaffected — useful for anything outside the default
|
|
276
|
+
root+docs/ coverage.
|
|
277
|
+
|
|
278
|
+
Recursively finds every in-scope `*.md` (skipping `.docs-builder/` and the protected files, at
|
|
279
|
+
any depth in scope), and for each one writes a row with:
|
|
237
280
|
|
|
238
281
|
- `h1` and a short `snip` (first ~200 chars of body, fence-masked) — reused straight from the
|
|
239
282
|
same `headings()`/`snippet()`/`fenceMask()` parsers `scan` uses, no second extraction path.
|
|
@@ -242,14 +285,22 @@ Recursively finds every `*.md` under the root (skipping `wiki/`, `logs/`, `archi
|
|
|
242
285
|
*sorted* — it is no longer a bucket.
|
|
243
286
|
- `suggested` + `reason` — a mechanical PRIOR, never a verdict:
|
|
244
287
|
|
|
288
|
+
Checked in this order — the first rule that matches wins:
|
|
289
|
+
|
|
245
290
|
| suggested | rule |
|
|
246
291
|
|---|---|
|
|
247
292
|
| `archive` | path already under `archive/old/reports/phases`, **or** the doc's own opening declares a SHOUTED status word (`CLOSED`, `DEPRECATED`, `SUPERSEDED`, `WITHDRAWN`, `RETRACTED`, `REFUTED`, `ARCHIVAL`, `ARCHIVED`), **or** the filename matches an archive-shaped prefix (`REPORT`, `STATUS`, `SUMMARY`, `FIX_`, `PHASE_`, `SPRINT_`, `DRAFT`, `WIP`, `OLD`, `TEMP` followed by `-` or `_`) |
|
|
248
293
|
| `logs` | filename carries an experiment-record token — `PREREG`, `LEARNINGS`, `REPORT`, `RESULTS`, `POSTMORTEM`, `RETRO` (case-sensitive, word-boundary, checked ONLY after the archive rules above, so a `REPORT-old.md` still reads as archive, not logs) |
|
|
249
|
-
|
|
|
294
|
+
| *(residency)* | already resident under `docs/product/`, `docs/wiki/` or `docs/logs/` — its own current bucket is its prior (a stronger signal above can still override, e.g. a product-resident doc now shouting `DEPRECATED`) |
|
|
295
|
+
| `archive`/`logs`/`wiki` | a WEAKER, case-insensitive secondary prior read from the doc's own **H1 + first 3 H2s** — e.g. "Postmortem"/"Retrospective"/"Investigation" → `logs`; "deprecated"/"retired"/"superseded" → `archive`; "Conventions"/"How-to"/"Style Guide"/"Glossary" → `wiki`. Deliberately narrow: bare "guide"/"reference" were tried and DROPPED after false-positiving on an ordinary doc plainly titled "Guide". |
|
|
296
|
+
| `product` | has an H1, no archive/logs/wiki signal — the default when nothing else applies |
|
|
250
297
|
| `product` | no H1, but an **include stub** — its whole non-blank content (≤3 lines) is nothing but include directives (mkdocs `--8<--`, `{% include %}`, `{{ .. }}`, `<!-- include -->`) and/or markdown links | a live pointer, not an unknown doc — real-world miss: uv's `docs/reference/contributing.md` |
|
|
251
298
|
| `product` | no H1 at all, and not an include stub — no strong signal either way; the interview decides, same as any other row |
|
|
252
299
|
|
|
300
|
+
`suggested` never proposes `wiki` from a residency check alone reaching further than its own
|
|
301
|
+
match — the heuristic is only ever a prior; the interview decides the real `bucket`, including
|
|
302
|
+
routing a doc INTO `wiki` from anywhere.
|
|
303
|
+
|
|
253
304
|
- `bucket` — **empty on any row discover has not classified before** (see carry-forward
|
|
254
305
|
below; a re-run keeps a bucket the interview already set). This is the field the
|
|
255
306
|
classification interview (step 2) fills, and the ONLY field `apply-reorg` reads to decide
|
|
@@ -308,7 +359,7 @@ was the silent move, not the judgement.
|
|
|
308
359
|
|
|
309
360
|
1. Read `docs/.docs-builder/reorg-plan.json`. Feed the model the WHOLE table — `file`, `h1`,
|
|
310
361
|
`snip`, `lines`, `suggested`+`reason` — **in one call**, and have it fill `bucket` for
|
|
311
|
-
every row (`product`/`logs`/`archive`) with a one-line reason. `suggested` is a PRIOR the
|
|
362
|
+
every row (`product`/`wiki`/`logs`/`archive`) with a one-line reason. `suggested` is a PRIOR the
|
|
312
363
|
model is shown, never an authority over it — but a SHOUTED self-declared status
|
|
313
364
|
(`**Status: CLOSED**`) is near-conclusive for `archive` regardless of what the mechanical
|
|
314
365
|
prior says.
|
|
@@ -348,9 +399,15 @@ message, not a crash — so nothing can move on an unreviewed plan. A plan from
|
|
|
348
399
|
version (`bucket: 'oversized'` or `'review'`, both gone from the schema) is refused too, with
|
|
349
400
|
a pointer to re-run `discover`.
|
|
350
401
|
|
|
351
|
-
- `product` → verified `git mv` to `docs/product/<basename>`
|
|
352
|
-
- `
|
|
353
|
-
- `
|
|
402
|
+
- `product` → verified `git mv` to `docs/product/<basename>` (always flat)
|
|
403
|
+
- `wiki` → verified `git mv` to `docs/wiki/<basename>` (always flat)
|
|
404
|
+
- `logs` → verified `git mv` to `docs/logs/<basename>` (loose) **or**
|
|
405
|
+
`docs/logs/<group>/<basename>` — the ONE bucket that may nest, ONE level, where the group is
|
|
406
|
+
the file's FIRST path segment under `docs/` (unless that segment is itself a bucket name —
|
|
407
|
+
see the Layout section above). A deeper path inside the same special subfolder joins the
|
|
408
|
+
SAME group (no ratchet on a re-check either).
|
|
409
|
+
- `archive` → verified `git mv` to `docs/archive/<basename>` (always flat, and frozen — never
|
|
410
|
+
re-checked again once there)
|
|
354
411
|
- **Oversized files move too — size decides splittable, not sorted.** No bucket is exempt.
|
|
355
412
|
After the move, every oversized row is printed as a follow-up list at its NEW path, one
|
|
356
413
|
`cleanup <path> (N lines)` line per file — run `cleanup` (Mode 1, below) on each, by hand,
|
|
@@ -369,20 +426,10 @@ a pointer to re-run `discover`.
|
|
|
369
426
|
static — it never varies with row count, so a re-run rewrites identical bytes. Idempotent:
|
|
370
427
|
an existing block is replaced in place, never duplicated; other content is left alone.
|
|
371
428
|
The target is `CONFIG=` (default `CLAUDE.md`); this package uses `CONFIG=CLAUDE.md`.
|
|
372
|
-
- **The moves land STAGED in your git index —
|
|
373
|
-
rename immediately (that is what preserves history), so when `apply-reorg` returns the
|
|
374
|
-
is holding N staged renames
|
|
375
|
-
|
|
376
|
-
a closing advisory naming the counts and a copy-pasteable recipe. **Run that recipe
|
|
377
|
-
VERBATIM. Do NOT hand-edit it, and do NOT stage by hand instead.** If it looks incomplete
|
|
378
|
-
or names a path that errors, that is a BUG in the recipe — stop and report it to the user;
|
|
379
|
-
do not silently repair it and move on. OBSERVED, real (privcloud first field run): the
|
|
380
|
-
recipe omitted `docs/log.md`, the operator quietly added it by hand, and the bug only
|
|
381
|
-
surfaced because they were later asked for near-misses — a silent repair is a lost bug
|
|
382
|
-
report. Do NOT scope the commit to `docs` alone either: the renames are staged, but the
|
|
383
|
-
inbound-link rewrites are UNSTAGED and reach outside `docs/` (`src/`, `scripts/`, `tests/`,
|
|
384
|
-
`README.md`). Both belong in ONE commit, or you ship moved files whose links were never
|
|
385
|
-
repaired. The tool never auto-commits, by design.
|
|
429
|
+
- **The moves land STAGED in your git index — nothing is committed for you.** `git mv` stages
|
|
430
|
+
each rename immediately (that is what preserves history), so when `apply-reorg` returns the
|
|
431
|
+
repo is holding N staged renames plus unstaged link rewrites. `apply-reorg` prints a closing
|
|
432
|
+
commit advisory; see "Finishing a run" below for the actual commit flow.
|
|
386
433
|
- A basename collision (two files, same name, different original folders) is
|
|
387
434
|
disambiguated (`-2`, `-3`, …); a collision with a **file that already exists at the
|
|
388
435
|
destination** is skipped, logged, and does not stop the rest of the run.
|
|
@@ -393,7 +440,9 @@ a pointer to re-run `discover`.
|
|
|
393
440
|
touches — is never removed. Only directories THIS run emptied are candidates; a dir that
|
|
394
441
|
happened to already be empty before this run started is not this tool's to remove.
|
|
395
442
|
- **After every move, `apply-reorg` re-scans the whole corpus** — `docs/product/`,
|
|
396
|
-
`docs/logs/`, and `docs/archive/` all
|
|
443
|
+
`docs/logs/`, and `docs/archive/` all (`docs/wiki/` is excluded, same as any other
|
|
444
|
+
`PAGES` dir — it holds synthesised pages, not source docs to reorg) — straight into
|
|
445
|
+
`outline.json`, the database `search`
|
|
397
446
|
reads. Not a hint, not opt-in: it runs every time, even when nothing moved this run (e.g.
|
|
398
447
|
re-running on a corpus already sorted from a previous pass). Measured bug this closes: on a
|
|
399
448
|
real 37-doc corpus, `outline.json` used to hold records for only the 12 files a split had
|
|
@@ -402,6 +451,47 @@ a pointer to re-run `discover`.
|
|
|
402
451
|
content, so a pre-move scan would just be redone), and reuses the same `scan` used
|
|
403
452
|
everywhere else in this pipeline — no second scanner, no second outline format.
|
|
404
453
|
|
|
454
|
+
### Finishing a run — the commit flow, run by hand every time
|
|
455
|
+
|
|
456
|
+
`apply-reorg`/`archive`/`cleanup-apply` never auto-commit, by design. Each run's staged renames
|
|
457
|
+
and unstaged link rewrites (in `.md` files only, but anywhere — e.g. the root `README.md`)
|
|
458
|
+
belong in ONE commit, including the
|
|
459
|
+
`CLAUDE.md` docs-pointer block `apply-reorg` writes — it is in `commit-files.txt` along with
|
|
460
|
+
everything else. Run these steps literally, in order:
|
|
461
|
+
|
|
462
|
+
1. `git branch --show-current`. If it prints `main` or `master`, do **NOT** commit — tell the
|
|
463
|
+
user the files are ready and to switch to a branch first.
|
|
464
|
+
2. `cat docs/.docs-builder/commit-files.txt` to see exactly what this run touched, and
|
|
465
|
+
`cat docs/.docs-builder/commit-dirty.txt` to see which of those files already carried the
|
|
466
|
+
operator's own uncommitted edits BEFORE this run — pathspec can't split hunks, so committing
|
|
467
|
+
the file commits that edit too. Then ask with `AskUserQuestion`, header `Commit`: if
|
|
468
|
+
`commit-dirty.txt` lists anything, **"Commit these N files now? Note: these files also
|
|
469
|
+
carry your own uncommitted edits, which will be committed too: …"** (name them); otherwise
|
|
470
|
+
**"Commit these N files now?"** — **Commit** (run the printed recipe) / **Leave uncommitted**
|
|
471
|
+
(say what is pending; nothing this run did gets undone).
|
|
472
|
+
3. On **Commit**, run the printed recipe line EXACTLY as printed:
|
|
473
|
+
```
|
|
474
|
+
git add --pathspec-from-file=docs/.docs-builder/commit-add.txt && git commit -m "docs: reorg" --pathspec-from-file=docs/.docs-builder/commit-files.txt
|
|
475
|
+
```
|
|
476
|
+
(when `REPO` isn't the shell's cwd, both commands are printed as `git -C '<REPO>' …` instead
|
|
477
|
+
— run that form, unmodified). Do not hand-edit it, do not stage by hand instead, and do not
|
|
478
|
+
scope it to `docs/` alone — a `.md` outside `docs/` (e.g. `README.md`) can carry a repaired
|
|
479
|
+
link. If it errors or names a path that doesn't exist, that is a BUG: stop and report it to
|
|
480
|
+
the user; do not silently hand-repair and move on.
|
|
481
|
+
OBSERVED, real (privcloud first field run, pre-dating the pathspec-file recipe): a
|
|
482
|
+
hand-rolled recipe once omitted `docs/log.md` and the operator quietly added it by hand —
|
|
483
|
+
the bug only surfaced later, when asked for near-misses. A silent repair is a lost bug
|
|
484
|
+
report.
|
|
485
|
+
4. After a successful commit, run `node $DB ledger` to stamp the consolidation — nothing in
|
|
486
|
+
`discover`/`apply-reorg`/`archive`/`cleanup-apply` stamps it for you, and without the stamp
|
|
487
|
+
`due` stays NOT due, the picker's verdict stays uninformed, and `/remember`'s docs nudge
|
|
488
|
+
never fires.
|
|
489
|
+
|
|
490
|
+
Add `docs/.docs-builder/` to `.gitignore` before the first commit if it is not already
|
|
491
|
+
ignored — it is machine state, regenerated every run, and the ledger stamp is per-clone by
|
|
492
|
+
design. Never use `git add -A` / `git add -u` / `git commit -a` for this: either would ALSO
|
|
493
|
+
absorb any unrelated in-flight work in the tree — OBSERVED TWICE, in two different repos.
|
|
494
|
+
|
|
405
495
|
**After each move it repairs the paths that move just broke** — the whole point of doing this
|
|
406
496
|
in a script. Both movers (`apply-reorg` and `archive`) go through ONE function, `moveDoc`, so
|
|
407
497
|
the follow-up list cannot be added to one and missed by the other; that exact miss shipped
|
|
@@ -413,8 +503,12 @@ reported as a file that needs re-moving. Two follow-ups:
|
|
|
413
503
|
(`records[].file`, and the `<file> :: ` prefix inside every key). This is the same
|
|
414
504
|
function `archive` calls; `apply-reorg` used to bypass it, which silently invalidated
|
|
415
505
|
every key of every file it moved. Both now reach it through `moveDoc`.
|
|
416
|
-
2. **Inbound links** — every git-tracked `.md
|
|
417
|
-
|
|
506
|
+
2. **Inbound links** — every git-tracked `.md` file, anywhere in the repo, that points at the
|
|
507
|
+
old path (repo-rooted, e.g. `docs/GUIDE.md`) is rewritten to the new one. Only `.md` files
|
|
508
|
+
are ever opened, read, or edited — this is a DOCS tool, not a repo-wide text rewriter.
|
|
509
|
+
FIELD BUG, real (bareloop, 2026-09-10): the previous version also scanned
|
|
510
|
+
`.js`/`.cjs`/`.mjs`/`.json`/`.yml`, and rewrote 6 signed JSON job specs (breaking their
|
|
511
|
+
hashes), a byte-signed `.mjs` close script, and a code comment that tripped a commit gate.
|
|
418
512
|
In `.md` files specifically, a RELATIVE link is also caught: inside actual markdown link
|
|
419
513
|
syntax only (`[text](../concepts/x.md)` or a reference-style `[label]: ./tools.md`), never
|
|
420
514
|
bare prose, the target is resolved against the SCANNING file's own directory, and — if it
|
|
@@ -449,13 +543,14 @@ predicate, `isRewriteExempt`, at one call site. `docs/.docs-builder/` is exclude
|
|
|
449
543
|
|
|
450
544
|
**A known, deliberate trade-off: this is a literal exact-path match over raw file bytes, not
|
|
451
545
|
fence-aware or context-aware.** It rewrites every exact, word-bounded occurrence of the old path
|
|
452
|
-
in every git-tracked `.md
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
546
|
+
in every git-tracked `.md` file (except the two exemptions above) — inside a code fence, inside
|
|
547
|
+
a sentence describing history ("this used to live at docs/OLD.md"), anywhere. A prose mention of
|
|
548
|
+
where a file *used to be* WILL be rewritten to say where it is now, changing what the sentence
|
|
549
|
+
says. This is intentional, not an oversight: a dead link is worse than a reworded sentence, the
|
|
550
|
+
match is exact rather than inferred (unlike the dangling-reference *lint*, which infers and was
|
|
551
|
+
cut outright at 1/27 precision), and every rewrite is printed per file so it is visible, never
|
|
552
|
+
silent. Only `.md` files are ever in scope, so this trade-off never touches non-doc files —
|
|
553
|
+
see "Inbound links" above.
|
|
459
554
|
|
|
460
555
|
---
|
|
461
556
|
|
|
@@ -746,14 +841,16 @@ node $DB index-flat
|
|
|
746
841
|
Writes **one** `docs/index.md` covering the whole corpus, in three sections: `## Product`
|
|
747
842
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
748
843
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
749
|
-
under `docs/logs
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
844
|
+
under `docs/logs/`, **grouped by its one-level subdir** — a loose `docs/logs/x.md` row is
|
|
845
|
+
listed ungrouped first, then each subdir gets its own `### <subdir>/` header with its files
|
|
846
|
+
under it, subdirs sorted alphabetically), and `## Archive` (one row per file under
|
|
847
|
+
`docs/archive/`). Each row (product or logs, grouped or not) is an H1 title, a line count, and
|
|
848
|
+
a link, plus one indented line per H2 heading (in document order) so an agent can find and
|
|
849
|
+
slice-read a section without opening the doc — each H2 line carries its own `(Lstart–end)`
|
|
850
|
+
line range, reusing the SAME `headings()`+`fenceMask()` boundaries `scan` already writes to
|
|
851
|
+
`outline.json` (no second parser). Omitted when the doc has no H2s. `## Archive` rows are
|
|
852
|
+
H1-only, never H2 lines — an archived doc is frozen history, not a live section to route into.
|
|
853
|
+
No theme grouping, no `labels.json`, no model call.
|
|
757
854
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
758
855
|
pipeline (nothing else writes an index at all).
|
|
759
856
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -774,7 +871,8 @@ v3 folds the old `reconcile` and `due` commands into one: "first run" (nothing s
|
|
|
774
871
|
state, and two separate commands only made users guess which one to run.
|
|
775
872
|
|
|
776
873
|
```bash
|
|
777
|
-
node $DB reorg
|
|
874
|
+
node $DB reorg # root-level .md files (top level only) + everything under docs/
|
|
875
|
+
node $DB reorg <dir> # re-checks every doc already inside <dir> — passes <dir> to discover
|
|
778
876
|
```
|
|
779
877
|
|
|
780
878
|
If a ledger stamp exists (see "Knowing when reorg is due" below), its `due`-style drift
|