liteagents 2.18.0 → 2.19.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 +21 -0
- package/package.json +1 -1
- package/packages/ampcode/commands/docs-builder/docs-builder.cjs +27 -7
- package/packages/ampcode/commands/docs-builder.md +7 -1
- package/packages/ampcode/commands/remember.md +26 -10
- package/packages/claude/commands/docs-builder/docs-builder.cjs +27 -7
- package/packages/claude/commands/docs-builder.md +7 -1
- package/packages/claude/commands/remember.md +26 -10
- package/packages/droid/commands/docs-builder/docs-builder.cjs +27 -7
- package/packages/droid/commands/docs-builder.md +7 -1
- package/packages/droid/commands/remember.md +26 -10
- package/packages/opencode/command/docs-builder/docs-builder.cjs +27 -7
- package/packages/opencode/command/docs-builder.md +7 -1
- package/packages/opencode/command/remember.md +26 -10
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
15
|
- Enhanced testing capabilities
|
|
16
16
|
- Performance optimizations
|
|
17
17
|
|
|
18
|
+
## [2.19.0] - 2026-08-26
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **`docs/index.md` rows now list each doc's H2 headings, one per line, with a line range.**
|
|
22
|
+
The index is meant to let an agent find and slice-read a section without opening the doc —
|
|
23
|
+
previously each row carried only an H1, a line count, and a link, so an agent still had to
|
|
24
|
+
open the file to find anything inside it. Each H2 line reuses the exact `headings()` +
|
|
25
|
+
`fenceMask()` boundaries `scan` already writes to `outline.json` (no second parser), so a
|
|
26
|
+
heading inside a fenced code block still never appears. Archive rows stay H1-only — an
|
|
27
|
+
archived doc is frozen history, not a live section to route into.
|
|
28
|
+
- **`/remember` step 7 self-heals `docs/index.md` every run, not just at reorg time.** Any
|
|
29
|
+
drift `due` reports (new/moved/changed/deleted, not only the >=5-doc DUE threshold) now
|
|
30
|
+
also re-runs `index-flat` — script-only, no model call — so the index stays current between
|
|
31
|
+
full `/docs-builder reorg` passes instead of silently drifting until the next one.
|
|
32
|
+
- **`AGENT_RULES.md` demoted from an `@`-include to a plain path pointer.** It was wired into
|
|
33
|
+
CLAUDE.md as `@.claude/remember/AGENT_RULES.md`, which hot-loads the whole file into every
|
|
34
|
+
session even though it's documented as "not hot context" — measured at ~6.5k tokens/session
|
|
35
|
+
of standards prose loaded despite the file's own claim otherwise. `MEMORY.md` stays
|
|
36
|
+
`@`-referenced (it is hot); `AGENT_RULES.md` is now a plain path line, read only when
|
|
37
|
+
designing or building something new.
|
|
38
|
+
|
|
18
39
|
## [2.18.0] - 2026-08-26
|
|
19
40
|
|
|
20
41
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liteagents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "AI development toolkit with 11 specialized agents and 18 commands 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": {
|
|
@@ -728,14 +728,33 @@ function plan(outlineF, labelsF) {
|
|
|
728
728
|
// tripwire, never a prune, never a collapse, never a delete.
|
|
729
729
|
const ARCHIVE_WARN_ROWS = 100; // stated default, not measured — see docs-builder-v3-spec.md
|
|
730
730
|
|
|
731
|
-
|
|
731
|
+
// `includeH2` is false for archive rows: an archived doc is frozen history, not a live
|
|
732
|
+
// section a reader is being routed into, so its row stays H1 + line count + link only.
|
|
733
|
+
function indexRow(rel, dest, includeH2) {
|
|
732
734
|
const text = read(rel);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
+
const lines = text.split('\n');
|
|
736
|
+
// Same headings()+fenceMask() path scan() uses -- no second parser -- so an H2 inside a
|
|
737
|
+
// ``` fence is masked out here exactly as it is there.
|
|
738
|
+
const mask = fenceMask(lines);
|
|
739
|
+
const { h1, heads } = headings(lines, mask);
|
|
735
740
|
// Read from INSIDE index.md, so the link must resolve relative to index.md's own
|
|
736
741
|
// directory, not the repo root.
|
|
737
742
|
const relLink = path.relative(path.dirname(dest), repoPath(rel)).split(path.sep).join('/');
|
|
738
|
-
|
|
743
|
+
let row = `- [${h1 || path.basename(rel)}](${relLink}) — ${lines.length} lines\n`;
|
|
744
|
+
if (!includeH2) return row;
|
|
745
|
+
// One line per H2, each with its own line range: from the `## ` heading's own 1-based
|
|
746
|
+
// line through the line before the next heading of level <= 2 (H1 or H2), or EOF for the
|
|
747
|
+
// last one -- the SAME boundary scan() uses to write s/e into outline.json, re-derived
|
|
748
|
+
// here from the same heads array rather than duplicated as a second computation. Lets an
|
|
749
|
+
// agent jump straight to (and slice-read) a section without opening the doc at all.
|
|
750
|
+
const boundaries = heads.filter(h => h.lvl <= 2);
|
|
751
|
+
boundaries.forEach((h, i) => {
|
|
752
|
+
if (h.lvl !== 2) return;
|
|
753
|
+
const start = h.line;
|
|
754
|
+
const end = i + 1 < boundaries.length ? boundaries[i + 1].line - 1 : lines.length;
|
|
755
|
+
row += ` - ${h.text} (L${start}–${end})\n`;
|
|
756
|
+
});
|
|
757
|
+
return row;
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
function renderSection(title, rows) {
|
|
@@ -767,9 +786,10 @@ function indexFlat() {
|
|
|
767
786
|
const outRel = process.env.OUT || 'docs/index.md';
|
|
768
787
|
const dest = repoPath(outRel);
|
|
769
788
|
const productRows = [...productFiles, ...pageFiles].sort()
|
|
770
|
-
.map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
771
|
-
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
772
|
-
|
|
789
|
+
.map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
790
|
+
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
791
|
+
// Archive rows are deliberately H1-only -- frozen history, not a live section to route into.
|
|
792
|
+
const archiveRows = archiveFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, false) }));
|
|
773
793
|
|
|
774
794
|
let s = '# Index\n\n';
|
|
775
795
|
// Unconditional — not gated on row count, unlike ARCHIVE_WARN_ROWS below: a reader should
|
|
@@ -747,7 +747,13 @@ Writes **one** `docs/index.md` covering the whole corpus, in three sections: `##
|
|
|
747
747
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
748
748
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
749
749
|
under `docs/logs/`), and `## Archive` (one row per file under `docs/archive/`). Each row is
|
|
750
|
-
an H1 title, a line count, and a link
|
|
750
|
+
an H1 title, a line count, and a link, plus one indented line per H2 heading (in document
|
|
751
|
+
order) so an agent can find and slice-read a section without opening the doc — each H2
|
|
752
|
+
line carries its own `(Lstart–end)` line range, reusing the SAME `headings()`+`fenceMask()`
|
|
753
|
+
boundaries `scan` already writes to `outline.json` (no second parser). Omitted when the doc
|
|
754
|
+
has no H2s. `## Archive` rows are H1-only, never H2 lines — an archived doc is frozen
|
|
755
|
+
history, not a live section to route into. No theme grouping, no `labels.json`, no model
|
|
756
|
+
call.
|
|
751
757
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
752
758
|
pipeline (nothing else writes an index at all).
|
|
753
759
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -346,12 +346,13 @@ Reads all raw material (`.amp/stash/*.md` + `.amp/remember/friction/antigen_clus
|
|
|
346
346
|
inline duplication is needed
|
|
347
347
|
- If `.amp/remember/AGENT_RULES.md` exists (bootstrapped in step 1), compose a second,
|
|
348
348
|
independent section between `<!-- AGENT_RULES:START -->` and `<!-- AGENT_RULES:END -->`
|
|
349
|
-
markers
|
|
349
|
+
markers. Unlike MEMORY.md above, this is a **plain path pointer, never `@`-referenced**
|
|
350
|
+
— an `@`-reference hot-loads the whole file into every session, and this is a standards
|
|
351
|
+
guide to consult when designing/building something new, not hot context:
|
|
350
352
|
```
|
|
351
353
|
<!-- AGENT_RULES:START -->
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
@.amp/remember/AGENT_RULES.md
|
|
354
|
+
Standards guide (read when designing/building something new, not hot context):
|
|
355
|
+
.amp/remember/AGENT_RULES.md
|
|
355
356
|
<!-- AGENT_RULES:END -->
|
|
356
357
|
```
|
|
357
358
|
- Each marker pair is independent: if AGENT.md already has a given pair, replace the
|
|
@@ -411,11 +412,12 @@ Reads all raw material (`.amp/stash/*.md` + `.amp/remember/friction/antigen_clus
|
|
|
411
412
|
6. **Update processed manifest**
|
|
412
413
|
- Append paths of newly processed stashes to `.amp/remember/.processed`
|
|
413
414
|
|
|
414
|
-
7. **Docs reconcile check
|
|
415
|
+
7. **Docs reconcile check + auto re-index** (best-effort, crash-isolated like step 0)
|
|
415
416
|
|
|
416
|
-
`/remember` never reconciles
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
`/remember` never reconciles doc CONTENT, never writes frontmatter, never edits a page —
|
|
418
|
+
the only write here is the generated `docs/index.md` itself, via the same deterministic
|
|
419
|
+
`index-flat` script `/docs-builder` already uses, never a model call. Wrapped so any
|
|
420
|
+
failure here can never block the memory write that already happened in steps 3-6.
|
|
419
421
|
|
|
420
422
|
- **Locate `docs-builder.cjs`** — bundled next to this command at
|
|
421
423
|
`docs-builder/docs-builder.cjs` (same convention as `remember/friction.cjs`). Call it by
|
|
@@ -444,7 +446,19 @@ Reads all raw material (`.amp/stash/*.md` + `.amp/remember/friction/antigen_clus
|
|
|
444
446
|
- If `due` prints "no ledger yet" (no `docs/.docs-builder/ledger.json` to compare against),
|
|
445
447
|
do NOT relay it — print the same `/docs-builder reorg` line as the no-`docs/.docs-builder/`
|
|
446
448
|
case above, for the same reason: `ledger` would stamp an unsorted pile as correct.
|
|
447
|
-
-
|
|
449
|
+
- **Auto re-index — script only, no model, in addition to the DUE advisory below, not a
|
|
450
|
+
replacement for it.** If `due`'s output was NOT `docs unchanged since <sha>. NOT due.`
|
|
451
|
+
(i.e. it printed a row table -- any new/moved/moved+changed/changed/deleted doc, whether
|
|
452
|
+
or not the >=5 threshold below was crossed), the index has drifted and self-heals right
|
|
453
|
+
here, unconditionally:
|
|
454
|
+
```bash
|
|
455
|
+
node <docs-builder.cjs> index-flat
|
|
456
|
+
```
|
|
457
|
+
Same script `/docs-builder reorg` already calls, run standalone — no model call, no
|
|
458
|
+
interview, nothing moves. Note in the step-8 report that `docs/index.md` (and
|
|
459
|
+
`docs/log.md`, if `index-flat` touched it) were regenerated, so they are included
|
|
460
|
+
alongside whatever step 3-6 already changed when this run is committed.
|
|
461
|
+
- If DUE (the row count crossed the >=5 threshold), ALSO end with one line:
|
|
448
462
|
```
|
|
449
463
|
docs: 7 changed since 991f72d3 — run /docs-builder reorg
|
|
450
464
|
```
|
|
@@ -489,12 +503,14 @@ Reads all raw material (`.amp/stash/*.md` + `.amp/remember/friction/antigen_clus
|
|
|
489
503
|
ledger: ag-002 "literal scoped ask" ESCALATED → Fact; 2 phrasings failed. Hook or accept?
|
|
490
504
|
```
|
|
491
505
|
- If AGENT_RULES.md was bootstrapped this run, say so (one line)
|
|
506
|
+
- If step 7 ran the auto re-index, say so and name the regenerated files
|
|
507
|
+
(`docs/index.md`, plus `docs/log.md` if touched) so they are staged with this run
|
|
492
508
|
- Confirm MEMORY.md and AGENT.md updated
|
|
493
509
|
|
|
494
510
|
**File locations (all project-local — two dirs: `/stash` owns `.amp/stash/`, `/remember` owns `.amp/remember/`)**
|
|
495
511
|
- Stash files: `.amp/stash/*.md`
|
|
496
512
|
- Memory file: `.amp/remember/MEMORY.md` (single source of truth, referenced as `@.amp/remember/MEMORY.md`)
|
|
497
|
-
- Rules template: `.amp/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced
|
|
513
|
+
- Rules template: `.amp/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced by a plain path pointer, not `@`-referenced — see step 5)
|
|
498
514
|
- Antigen ledger: `.amp/remember/ledger.json` (per-rule evidence trail: class, status, attempts/rejected-buffer, recurrence-while-hot)
|
|
499
515
|
- Consolidation report: `.amp/remember/report.md` (latest step-8 report, overwritten each run)
|
|
500
516
|
- Processed manifest: `.amp/remember/.processed`
|
|
@@ -728,14 +728,33 @@ function plan(outlineF, labelsF) {
|
|
|
728
728
|
// tripwire, never a prune, never a collapse, never a delete.
|
|
729
729
|
const ARCHIVE_WARN_ROWS = 100; // stated default, not measured — see docs-builder-v3-spec.md
|
|
730
730
|
|
|
731
|
-
|
|
731
|
+
// `includeH2` is false for archive rows: an archived doc is frozen history, not a live
|
|
732
|
+
// section a reader is being routed into, so its row stays H1 + line count + link only.
|
|
733
|
+
function indexRow(rel, dest, includeH2) {
|
|
732
734
|
const text = read(rel);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
+
const lines = text.split('\n');
|
|
736
|
+
// Same headings()+fenceMask() path scan() uses -- no second parser -- so an H2 inside a
|
|
737
|
+
// ``` fence is masked out here exactly as it is there.
|
|
738
|
+
const mask = fenceMask(lines);
|
|
739
|
+
const { h1, heads } = headings(lines, mask);
|
|
735
740
|
// Read from INSIDE index.md, so the link must resolve relative to index.md's own
|
|
736
741
|
// directory, not the repo root.
|
|
737
742
|
const relLink = path.relative(path.dirname(dest), repoPath(rel)).split(path.sep).join('/');
|
|
738
|
-
|
|
743
|
+
let row = `- [${h1 || path.basename(rel)}](${relLink}) — ${lines.length} lines\n`;
|
|
744
|
+
if (!includeH2) return row;
|
|
745
|
+
// One line per H2, each with its own line range: from the `## ` heading's own 1-based
|
|
746
|
+
// line through the line before the next heading of level <= 2 (H1 or H2), or EOF for the
|
|
747
|
+
// last one -- the SAME boundary scan() uses to write s/e into outline.json, re-derived
|
|
748
|
+
// here from the same heads array rather than duplicated as a second computation. Lets an
|
|
749
|
+
// agent jump straight to (and slice-read) a section without opening the doc at all.
|
|
750
|
+
const boundaries = heads.filter(h => h.lvl <= 2);
|
|
751
|
+
boundaries.forEach((h, i) => {
|
|
752
|
+
if (h.lvl !== 2) return;
|
|
753
|
+
const start = h.line;
|
|
754
|
+
const end = i + 1 < boundaries.length ? boundaries[i + 1].line - 1 : lines.length;
|
|
755
|
+
row += ` - ${h.text} (L${start}–${end})\n`;
|
|
756
|
+
});
|
|
757
|
+
return row;
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
function renderSection(title, rows) {
|
|
@@ -767,9 +786,10 @@ function indexFlat() {
|
|
|
767
786
|
const outRel = process.env.OUT || 'docs/index.md';
|
|
768
787
|
const dest = repoPath(outRel);
|
|
769
788
|
const productRows = [...productFiles, ...pageFiles].sort()
|
|
770
|
-
.map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
771
|
-
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
772
|
-
|
|
789
|
+
.map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
790
|
+
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
791
|
+
// Archive rows are deliberately H1-only -- frozen history, not a live section to route into.
|
|
792
|
+
const archiveRows = archiveFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, false) }));
|
|
773
793
|
|
|
774
794
|
let s = '# Index\n\n';
|
|
775
795
|
// Unconditional — not gated on row count, unlike ARCHIVE_WARN_ROWS below: a reader should
|
|
@@ -747,7 +747,13 @@ Writes **one** `docs/index.md` covering the whole corpus, in three sections: `##
|
|
|
747
747
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
748
748
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
749
749
|
under `docs/logs/`), and `## Archive` (one row per file under `docs/archive/`). Each row is
|
|
750
|
-
an H1 title, a line count, and a link
|
|
750
|
+
an H1 title, a line count, and a link, plus one indented line per H2 heading (in document
|
|
751
|
+
order) so an agent can find and slice-read a section without opening the doc — each H2
|
|
752
|
+
line carries its own `(Lstart–end)` line range, reusing the SAME `headings()`+`fenceMask()`
|
|
753
|
+
boundaries `scan` already writes to `outline.json` (no second parser). Omitted when the doc
|
|
754
|
+
has no H2s. `## Archive` rows are H1-only, never H2 lines — an archived doc is frozen
|
|
755
|
+
history, not a live section to route into. No theme grouping, no `labels.json`, no model
|
|
756
|
+
call.
|
|
751
757
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
752
758
|
pipeline (nothing else writes an index at all).
|
|
753
759
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -346,12 +346,13 @@ Reads all raw material (`.claude/stash/*.md` + `.claude/remember/friction/antige
|
|
|
346
346
|
inline duplication is needed
|
|
347
347
|
- If `.claude/remember/AGENT_RULES.md` exists (bootstrapped in step 1), compose a second,
|
|
348
348
|
independent section between `<!-- AGENT_RULES:START -->` and `<!-- AGENT_RULES:END -->`
|
|
349
|
-
markers
|
|
349
|
+
markers. Unlike MEMORY.md above, this is a **plain path pointer, never `@`-referenced**
|
|
350
|
+
— an `@`-reference hot-loads the whole file into every session, and this is a standards
|
|
351
|
+
guide to consult when designing/building something new, not hot context:
|
|
350
352
|
```
|
|
351
353
|
<!-- AGENT_RULES:START -->
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
@.claude/remember/AGENT_RULES.md
|
|
354
|
+
Standards guide (read when designing/building something new, not hot context):
|
|
355
|
+
.claude/remember/AGENT_RULES.md
|
|
355
356
|
<!-- AGENT_RULES:END -->
|
|
356
357
|
```
|
|
357
358
|
- Each marker pair is independent: if CLAUDE.md already has a given pair, replace the
|
|
@@ -411,11 +412,12 @@ Reads all raw material (`.claude/stash/*.md` + `.claude/remember/friction/antige
|
|
|
411
412
|
6. **Update processed manifest**
|
|
412
413
|
- Append paths of newly processed stashes to `.claude/remember/.processed`
|
|
413
414
|
|
|
414
|
-
7. **Docs reconcile check
|
|
415
|
+
7. **Docs reconcile check + auto re-index** (best-effort, crash-isolated like step 0)
|
|
415
416
|
|
|
416
|
-
`/remember` never reconciles
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
`/remember` never reconciles doc CONTENT, never writes frontmatter, never edits a page —
|
|
418
|
+
the only write here is the generated `docs/index.md` itself, via the same deterministic
|
|
419
|
+
`index-flat` script `/docs-builder` already uses, never a model call. Wrapped so any
|
|
420
|
+
failure here can never block the memory write that already happened in steps 3-6.
|
|
419
421
|
|
|
420
422
|
- **Locate `docs-builder.cjs`** — bundled next to this command at
|
|
421
423
|
`docs-builder/docs-builder.cjs` (same convention as `remember/friction.cjs`). Call it by
|
|
@@ -444,7 +446,19 @@ Reads all raw material (`.claude/stash/*.md` + `.claude/remember/friction/antige
|
|
|
444
446
|
- If `due` prints "no ledger yet" (no `docs/.docs-builder/ledger.json` to compare against),
|
|
445
447
|
do NOT relay it — print the same `/docs-builder reorg` line as the no-`docs/.docs-builder/`
|
|
446
448
|
case above, for the same reason: `ledger` would stamp an unsorted pile as correct.
|
|
447
|
-
-
|
|
449
|
+
- **Auto re-index — script only, no model, in addition to the DUE advisory below, not a
|
|
450
|
+
replacement for it.** If `due`'s output was NOT `docs unchanged since <sha>. NOT due.`
|
|
451
|
+
(i.e. it printed a row table -- any new/moved/moved+changed/changed/deleted doc, whether
|
|
452
|
+
or not the >=5 threshold below was crossed), the index has drifted and self-heals right
|
|
453
|
+
here, unconditionally:
|
|
454
|
+
```bash
|
|
455
|
+
node <docs-builder.cjs> index-flat
|
|
456
|
+
```
|
|
457
|
+
Same script `/docs-builder reorg` already calls, run standalone — no model call, no
|
|
458
|
+
interview, nothing moves. Note in the step-8 report that `docs/index.md` (and
|
|
459
|
+
`docs/log.md`, if `index-flat` touched it) were regenerated, so they are included
|
|
460
|
+
alongside whatever step 3-6 already changed when this run is committed.
|
|
461
|
+
- If DUE (the row count crossed the >=5 threshold), ALSO end with one line:
|
|
448
462
|
```
|
|
449
463
|
docs: 7 changed since 991f72d3 — run /docs-builder reorg
|
|
450
464
|
```
|
|
@@ -489,12 +503,14 @@ Reads all raw material (`.claude/stash/*.md` + `.claude/remember/friction/antige
|
|
|
489
503
|
ledger: ag-002 "literal scoped ask" ESCALATED → Fact; 2 phrasings failed. Hook or accept?
|
|
490
504
|
```
|
|
491
505
|
- If AGENT_RULES.md was bootstrapped this run, say so (one line)
|
|
506
|
+
- If step 7 ran the auto re-index, say so and name the regenerated files
|
|
507
|
+
(`docs/index.md`, plus `docs/log.md` if touched) so they are staged with this run
|
|
492
508
|
- Confirm MEMORY.md and CLAUDE.md updated
|
|
493
509
|
|
|
494
510
|
**File locations (all project-local — two dirs: `/stash` owns `.claude/stash/`, `/remember` owns `.claude/remember/`)**
|
|
495
511
|
- Stash files: `.claude/stash/*.md`
|
|
496
512
|
- Memory file: `.claude/remember/MEMORY.md` (single source of truth, referenced as `@.claude/remember/MEMORY.md`)
|
|
497
|
-
- Rules template: `.claude/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced
|
|
513
|
+
- Rules template: `.claude/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced by a plain path pointer, not `@`-referenced — see step 5)
|
|
498
514
|
- Antigen ledger: `.claude/remember/ledger.json` (per-rule evidence trail: class, status, attempts/rejected-buffer, recurrence-while-hot)
|
|
499
515
|
- Consolidation report: `.claude/remember/report.md` (latest step-8 report, overwritten each run)
|
|
500
516
|
- Processed manifest: `.claude/remember/.processed`
|
|
@@ -728,14 +728,33 @@ function plan(outlineF, labelsF) {
|
|
|
728
728
|
// tripwire, never a prune, never a collapse, never a delete.
|
|
729
729
|
const ARCHIVE_WARN_ROWS = 100; // stated default, not measured — see docs-builder-v3-spec.md
|
|
730
730
|
|
|
731
|
-
|
|
731
|
+
// `includeH2` is false for archive rows: an archived doc is frozen history, not a live
|
|
732
|
+
// section a reader is being routed into, so its row stays H1 + line count + link only.
|
|
733
|
+
function indexRow(rel, dest, includeH2) {
|
|
732
734
|
const text = read(rel);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
+
const lines = text.split('\n');
|
|
736
|
+
// Same headings()+fenceMask() path scan() uses -- no second parser -- so an H2 inside a
|
|
737
|
+
// ``` fence is masked out here exactly as it is there.
|
|
738
|
+
const mask = fenceMask(lines);
|
|
739
|
+
const { h1, heads } = headings(lines, mask);
|
|
735
740
|
// Read from INSIDE index.md, so the link must resolve relative to index.md's own
|
|
736
741
|
// directory, not the repo root.
|
|
737
742
|
const relLink = path.relative(path.dirname(dest), repoPath(rel)).split(path.sep).join('/');
|
|
738
|
-
|
|
743
|
+
let row = `- [${h1 || path.basename(rel)}](${relLink}) — ${lines.length} lines\n`;
|
|
744
|
+
if (!includeH2) return row;
|
|
745
|
+
// One line per H2, each with its own line range: from the `## ` heading's own 1-based
|
|
746
|
+
// line through the line before the next heading of level <= 2 (H1 or H2), or EOF for the
|
|
747
|
+
// last one -- the SAME boundary scan() uses to write s/e into outline.json, re-derived
|
|
748
|
+
// here from the same heads array rather than duplicated as a second computation. Lets an
|
|
749
|
+
// agent jump straight to (and slice-read) a section without opening the doc at all.
|
|
750
|
+
const boundaries = heads.filter(h => h.lvl <= 2);
|
|
751
|
+
boundaries.forEach((h, i) => {
|
|
752
|
+
if (h.lvl !== 2) return;
|
|
753
|
+
const start = h.line;
|
|
754
|
+
const end = i + 1 < boundaries.length ? boundaries[i + 1].line - 1 : lines.length;
|
|
755
|
+
row += ` - ${h.text} (L${start}–${end})\n`;
|
|
756
|
+
});
|
|
757
|
+
return row;
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
function renderSection(title, rows) {
|
|
@@ -767,9 +786,10 @@ function indexFlat() {
|
|
|
767
786
|
const outRel = process.env.OUT || 'docs/index.md';
|
|
768
787
|
const dest = repoPath(outRel);
|
|
769
788
|
const productRows = [...productFiles, ...pageFiles].sort()
|
|
770
|
-
.map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
771
|
-
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
772
|
-
|
|
789
|
+
.map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
790
|
+
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
791
|
+
// Archive rows are deliberately H1-only -- frozen history, not a live section to route into.
|
|
792
|
+
const archiveRows = archiveFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, false) }));
|
|
773
793
|
|
|
774
794
|
let s = '# Index\n\n';
|
|
775
795
|
// Unconditional — not gated on row count, unlike ARCHIVE_WARN_ROWS below: a reader should
|
|
@@ -747,7 +747,13 @@ Writes **one** `docs/index.md` covering the whole corpus, in three sections: `##
|
|
|
747
747
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
748
748
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
749
749
|
under `docs/logs/`), and `## Archive` (one row per file under `docs/archive/`). Each row is
|
|
750
|
-
an H1 title, a line count, and a link
|
|
750
|
+
an H1 title, a line count, and a link, plus one indented line per H2 heading (in document
|
|
751
|
+
order) so an agent can find and slice-read a section without opening the doc — each H2
|
|
752
|
+
line carries its own `(Lstart–end)` line range, reusing the SAME `headings()`+`fenceMask()`
|
|
753
|
+
boundaries `scan` already writes to `outline.json` (no second parser). Omitted when the doc
|
|
754
|
+
has no H2s. `## Archive` rows are H1-only, never H2 lines — an archived doc is frozen
|
|
755
|
+
history, not a live section to route into. No theme grouping, no `labels.json`, no model
|
|
756
|
+
call.
|
|
751
757
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
752
758
|
pipeline (nothing else writes an index at all).
|
|
753
759
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -346,12 +346,13 @@ Reads all raw material (`.factory/stash/*.md` + `.factory/remember/friction/anti
|
|
|
346
346
|
inline duplication is needed
|
|
347
347
|
- If `.factory/remember/AGENT_RULES.md` exists (bootstrapped in step 1), compose a second,
|
|
348
348
|
independent section between `<!-- AGENT_RULES:START -->` and `<!-- AGENT_RULES:END -->`
|
|
349
|
-
markers
|
|
349
|
+
markers. Unlike MEMORY.md above, this is a **plain path pointer, never `@`-referenced**
|
|
350
|
+
— an `@`-reference hot-loads the whole file into every session, and this is a standards
|
|
351
|
+
guide to consult when designing/building something new, not hot context:
|
|
350
352
|
```
|
|
351
353
|
<!-- AGENT_RULES:START -->
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
@.factory/remember/AGENT_RULES.md
|
|
354
|
+
Standards guide (read when designing/building something new, not hot context):
|
|
355
|
+
.factory/remember/AGENT_RULES.md
|
|
355
356
|
<!-- AGENT_RULES:END -->
|
|
356
357
|
```
|
|
357
358
|
- Each marker pair is independent: if AGENTS.md already has a given pair, replace the
|
|
@@ -411,11 +412,12 @@ Reads all raw material (`.factory/stash/*.md` + `.factory/remember/friction/anti
|
|
|
411
412
|
6. **Update processed manifest**
|
|
412
413
|
- Append paths of newly processed stashes to `.factory/remember/.processed`
|
|
413
414
|
|
|
414
|
-
7. **Docs reconcile check
|
|
415
|
+
7. **Docs reconcile check + auto re-index** (best-effort, crash-isolated like step 0)
|
|
415
416
|
|
|
416
|
-
`/remember` never reconciles
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
`/remember` never reconciles doc CONTENT, never writes frontmatter, never edits a page —
|
|
418
|
+
the only write here is the generated `docs/index.md` itself, via the same deterministic
|
|
419
|
+
`index-flat` script `/docs-builder` already uses, never a model call. Wrapped so any
|
|
420
|
+
failure here can never block the memory write that already happened in steps 3-6.
|
|
419
421
|
|
|
420
422
|
- **Locate `docs-builder.cjs`** — bundled next to this command at
|
|
421
423
|
`docs-builder/docs-builder.cjs` (same convention as `remember/friction.cjs`). Call it by
|
|
@@ -444,7 +446,19 @@ Reads all raw material (`.factory/stash/*.md` + `.factory/remember/friction/anti
|
|
|
444
446
|
- If `due` prints "no ledger yet" (no `docs/.docs-builder/ledger.json` to compare against),
|
|
445
447
|
do NOT relay it — print the same `/docs-builder reorg` line as the no-`docs/.docs-builder/`
|
|
446
448
|
case above, for the same reason: `ledger` would stamp an unsorted pile as correct.
|
|
447
|
-
-
|
|
449
|
+
- **Auto re-index — script only, no model, in addition to the DUE advisory below, not a
|
|
450
|
+
replacement for it.** If `due`'s output was NOT `docs unchanged since <sha>. NOT due.`
|
|
451
|
+
(i.e. it printed a row table -- any new/moved/moved+changed/changed/deleted doc, whether
|
|
452
|
+
or not the >=5 threshold below was crossed), the index has drifted and self-heals right
|
|
453
|
+
here, unconditionally:
|
|
454
|
+
```bash
|
|
455
|
+
node <docs-builder.cjs> index-flat
|
|
456
|
+
```
|
|
457
|
+
Same script `/docs-builder reorg` already calls, run standalone — no model call, no
|
|
458
|
+
interview, nothing moves. Note in the step-8 report that `docs/index.md` (and
|
|
459
|
+
`docs/log.md`, if `index-flat` touched it) were regenerated, so they are included
|
|
460
|
+
alongside whatever step 3-6 already changed when this run is committed.
|
|
461
|
+
- If DUE (the row count crossed the >=5 threshold), ALSO end with one line:
|
|
448
462
|
```
|
|
449
463
|
docs: 7 changed since 991f72d3 — run /docs-builder reorg
|
|
450
464
|
```
|
|
@@ -489,12 +503,14 @@ Reads all raw material (`.factory/stash/*.md` + `.factory/remember/friction/anti
|
|
|
489
503
|
ledger: ag-002 "literal scoped ask" ESCALATED → Fact; 2 phrasings failed. Hook or accept?
|
|
490
504
|
```
|
|
491
505
|
- If AGENT_RULES.md was bootstrapped this run, say so (one line)
|
|
506
|
+
- If step 7 ran the auto re-index, say so and name the regenerated files
|
|
507
|
+
(`docs/index.md`, plus `docs/log.md` if touched) so they are staged with this run
|
|
492
508
|
- Confirm MEMORY.md and AGENTS.md updated
|
|
493
509
|
|
|
494
510
|
**File locations (all project-local — two dirs: `/stash` owns `.factory/stash/`, `/remember` owns `.factory/remember/`)**
|
|
495
511
|
- Stash files: `.factory/stash/*.md`
|
|
496
512
|
- Memory file: `.factory/remember/MEMORY.md` (single source of truth, referenced as `@.factory/remember/MEMORY.md`)
|
|
497
|
-
- Rules template: `.factory/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced
|
|
513
|
+
- Rules template: `.factory/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced by a plain path pointer, not `@`-referenced — see step 5)
|
|
498
514
|
- Antigen ledger: `.factory/remember/ledger.json` (per-rule evidence trail: class, status, attempts/rejected-buffer, recurrence-while-hot)
|
|
499
515
|
- Consolidation report: `.factory/remember/report.md` (latest step-8 report, overwritten each run)
|
|
500
516
|
- Processed manifest: `.factory/remember/.processed`
|
|
@@ -728,14 +728,33 @@ function plan(outlineF, labelsF) {
|
|
|
728
728
|
// tripwire, never a prune, never a collapse, never a delete.
|
|
729
729
|
const ARCHIVE_WARN_ROWS = 100; // stated default, not measured — see docs-builder-v3-spec.md
|
|
730
730
|
|
|
731
|
-
|
|
731
|
+
// `includeH2` is false for archive rows: an archived doc is frozen history, not a live
|
|
732
|
+
// section a reader is being routed into, so its row stays H1 + line count + link only.
|
|
733
|
+
function indexRow(rel, dest, includeH2) {
|
|
732
734
|
const text = read(rel);
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
+
const lines = text.split('\n');
|
|
736
|
+
// Same headings()+fenceMask() path scan() uses -- no second parser -- so an H2 inside a
|
|
737
|
+
// ``` fence is masked out here exactly as it is there.
|
|
738
|
+
const mask = fenceMask(lines);
|
|
739
|
+
const { h1, heads } = headings(lines, mask);
|
|
735
740
|
// Read from INSIDE index.md, so the link must resolve relative to index.md's own
|
|
736
741
|
// directory, not the repo root.
|
|
737
742
|
const relLink = path.relative(path.dirname(dest), repoPath(rel)).split(path.sep).join('/');
|
|
738
|
-
|
|
743
|
+
let row = `- [${h1 || path.basename(rel)}](${relLink}) — ${lines.length} lines\n`;
|
|
744
|
+
if (!includeH2) return row;
|
|
745
|
+
// One line per H2, each with its own line range: from the `## ` heading's own 1-based
|
|
746
|
+
// line through the line before the next heading of level <= 2 (H1 or H2), or EOF for the
|
|
747
|
+
// last one -- the SAME boundary scan() uses to write s/e into outline.json, re-derived
|
|
748
|
+
// here from the same heads array rather than duplicated as a second computation. Lets an
|
|
749
|
+
// agent jump straight to (and slice-read) a section without opening the doc at all.
|
|
750
|
+
const boundaries = heads.filter(h => h.lvl <= 2);
|
|
751
|
+
boundaries.forEach((h, i) => {
|
|
752
|
+
if (h.lvl !== 2) return;
|
|
753
|
+
const start = h.line;
|
|
754
|
+
const end = i + 1 < boundaries.length ? boundaries[i + 1].line - 1 : lines.length;
|
|
755
|
+
row += ` - ${h.text} (L${start}–${end})\n`;
|
|
756
|
+
});
|
|
757
|
+
return row;
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
function renderSection(title, rows) {
|
|
@@ -767,9 +786,10 @@ function indexFlat() {
|
|
|
767
786
|
const outRel = process.env.OUT || 'docs/index.md';
|
|
768
787
|
const dest = repoPath(outRel);
|
|
769
788
|
const productRows = [...productFiles, ...pageFiles].sort()
|
|
770
|
-
.map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
771
|
-
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest) }));
|
|
772
|
-
|
|
789
|
+
.map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
790
|
+
const logsRows = logsFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, true) }));
|
|
791
|
+
// Archive rows are deliberately H1-only -- frozen history, not a live section to route into.
|
|
792
|
+
const archiveRows = archiveFiles.sort().map(f => ({ file: f, row: indexRow(f, dest, false) }));
|
|
773
793
|
|
|
774
794
|
let s = '# Index\n\n';
|
|
775
795
|
// Unconditional — not gated on row count, unlike ARCHIVE_WARN_ROWS below: a reader should
|
|
@@ -747,7 +747,13 @@ Writes **one** `docs/index.md` covering the whole corpus, in three sections: `##
|
|
|
747
747
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
748
748
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
749
749
|
under `docs/logs/`), and `## Archive` (one row per file under `docs/archive/`). Each row is
|
|
750
|
-
an H1 title, a line count, and a link
|
|
750
|
+
an H1 title, a line count, and a link, plus one indented line per H2 heading (in document
|
|
751
|
+
order) so an agent can find and slice-read a section without opening the doc — each H2
|
|
752
|
+
line carries its own `(Lstart–end)` line range, reusing the SAME `headings()`+`fenceMask()`
|
|
753
|
+
boundaries `scan` already writes to `outline.json` (no second parser). Omitted when the doc
|
|
754
|
+
has no H2s. `## Archive` rows are H1-only, never H2 lines — an archived doc is frozen
|
|
755
|
+
history, not a live section to route into. No theme grouping, no `labels.json`, no model
|
|
756
|
+
call.
|
|
751
757
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
752
758
|
pipeline (nothing else writes an index at all).
|
|
753
759
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -346,12 +346,13 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
|
|
|
346
346
|
inline duplication is needed
|
|
347
347
|
- If `.opencode/remember/AGENT_RULES.md` exists (bootstrapped in step 1), compose a second,
|
|
348
348
|
independent section between `<!-- AGENT_RULES:START -->` and `<!-- AGENT_RULES:END -->`
|
|
349
|
-
markers
|
|
349
|
+
markers. Unlike MEMORY.md above, this is a **plain path pointer, never `@`-referenced**
|
|
350
|
+
— an `@`-reference hot-loads the whole file into every session, and this is a standards
|
|
351
|
+
guide to consult when designing/building something new, not hot context:
|
|
350
352
|
```
|
|
351
353
|
<!-- AGENT_RULES:START -->
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
@.opencode/remember/AGENT_RULES.md
|
|
354
|
+
Standards guide (read when designing/building something new, not hot context):
|
|
355
|
+
.opencode/remember/AGENT_RULES.md
|
|
355
356
|
<!-- AGENT_RULES:END -->
|
|
356
357
|
```
|
|
357
358
|
- Each marker pair is independent: if AGENTS.md already has a given pair, replace the
|
|
@@ -411,11 +412,12 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
|
|
|
411
412
|
6. **Update processed manifest**
|
|
412
413
|
- Append paths of newly processed stashes to `.opencode/remember/.processed`
|
|
413
414
|
|
|
414
|
-
7. **Docs reconcile check
|
|
415
|
+
7. **Docs reconcile check + auto re-index** (best-effort, crash-isolated like step 0)
|
|
415
416
|
|
|
416
|
-
`/remember` never reconciles
|
|
417
|
-
|
|
418
|
-
|
|
417
|
+
`/remember` never reconciles doc CONTENT, never writes frontmatter, never edits a page —
|
|
418
|
+
the only write here is the generated `docs/index.md` itself, via the same deterministic
|
|
419
|
+
`index-flat` script `/docs-builder` already uses, never a model call. Wrapped so any
|
|
420
|
+
failure here can never block the memory write that already happened in steps 3-6.
|
|
419
421
|
|
|
420
422
|
- **Locate `docs-builder.cjs`** — bundled next to this command at
|
|
421
423
|
`docs-builder/docs-builder.cjs` (same convention as `remember/friction.cjs`). Call it by
|
|
@@ -444,7 +446,19 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
|
|
|
444
446
|
- If `due` prints "no ledger yet" (no `docs/.docs-builder/ledger.json` to compare against),
|
|
445
447
|
do NOT relay it — print the same `/docs-builder reorg` line as the no-`docs/.docs-builder/`
|
|
446
448
|
case above, for the same reason: `ledger` would stamp an unsorted pile as correct.
|
|
447
|
-
-
|
|
449
|
+
- **Auto re-index — script only, no model, in addition to the DUE advisory below, not a
|
|
450
|
+
replacement for it.** If `due`'s output was NOT `docs unchanged since <sha>. NOT due.`
|
|
451
|
+
(i.e. it printed a row table -- any new/moved/moved+changed/changed/deleted doc, whether
|
|
452
|
+
or not the >=5 threshold below was crossed), the index has drifted and self-heals right
|
|
453
|
+
here, unconditionally:
|
|
454
|
+
```bash
|
|
455
|
+
node <docs-builder.cjs> index-flat
|
|
456
|
+
```
|
|
457
|
+
Same script `/docs-builder reorg` already calls, run standalone — no model call, no
|
|
458
|
+
interview, nothing moves. Note in the step-8 report that `docs/index.md` (and
|
|
459
|
+
`docs/log.md`, if `index-flat` touched it) were regenerated, so they are included
|
|
460
|
+
alongside whatever step 3-6 already changed when this run is committed.
|
|
461
|
+
- If DUE (the row count crossed the >=5 threshold), ALSO end with one line:
|
|
448
462
|
```
|
|
449
463
|
docs: 7 changed since 991f72d3 — run /docs-builder reorg
|
|
450
464
|
```
|
|
@@ -489,12 +503,14 @@ Reads all raw material (`.opencode/stash/*.md` + `.opencode/remember/friction/an
|
|
|
489
503
|
ledger: ag-002 "literal scoped ask" ESCALATED → Fact; 2 phrasings failed. Hook or accept?
|
|
490
504
|
```
|
|
491
505
|
- If AGENT_RULES.md was bootstrapped this run, say so (one line)
|
|
506
|
+
- If step 7 ran the auto re-index, say so and name the regenerated files
|
|
507
|
+
(`docs/index.md`, plus `docs/log.md` if touched) so they are staged with this run
|
|
492
508
|
- Confirm MEMORY.md and AGENTS.md updated
|
|
493
509
|
|
|
494
510
|
**File locations (all project-local — two dirs: `/stash` owns `.opencode/stash/`, `/remember` owns `.opencode/remember/`)**
|
|
495
511
|
- Stash files: `.opencode/stash/*.md`
|
|
496
512
|
- Memory file: `.opencode/remember/MEMORY.md` (single source of truth, referenced as `@.opencode/remember/MEMORY.md`)
|
|
497
|
-
- Rules template: `.opencode/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced
|
|
513
|
+
- Rules template: `.opencode/remember/AGENT_RULES.md` (bootstrapped once from the bundled package template on first `/remember` run, never overwritten again — user-owned after that; referenced by a plain path pointer, not `@`-referenced — see step 5)
|
|
498
514
|
- Antigen ledger: `.opencode/remember/ledger.json` (per-rule evidence trail: class, status, attempts/rejected-buffer, recurrence-while-hot)
|
|
499
515
|
- Consolidation report: `.opencode/remember/report.md` (latest step-8 report, overwritten each run)
|
|
500
516
|
- Processed manifest: `.opencode/remember/.processed`
|