liteagents 2.24.0 → 3.0.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 +121 -0
- package/README.md +27 -11
- package/installer/cli.js +36 -3
- package/installer/installation-engine.js +8 -0
- package/package.json +2 -2
- package/packages/ampcode/AGENT.md +1 -2
- package/packages/ampcode/agents/orchestrator.md +2 -2
- package/packages/ampcode/commands/refactor.md +8 -2
- package/packages/ampcode/commands/remember/stub-check.cjs +197 -0
- package/packages/ampcode/commands/remember/sync-rules.cjs +169 -0
- package/packages/ampcode/commands/remember/version-check.cjs +214 -0
- package/packages/ampcode/commands/remember.md +78 -10
- package/packages/claude/CLAUDE.md +1 -2
- package/packages/claude/agents/orchestrator.md +2 -2
- package/packages/claude/commands/refactor.md +8 -2
- package/packages/claude/commands/remember/stub-check.cjs +197 -0
- package/packages/claude/commands/remember/sync-rules.cjs +169 -0
- package/packages/claude/commands/remember/version-check.cjs +214 -0
- package/packages/claude/commands/remember.md +78 -10
- package/packages/droid/AGENTS.md +1 -2
- package/packages/droid/commands/refactor.md +8 -2
- package/packages/droid/commands/remember/stub-check.cjs +197 -0
- package/packages/droid/commands/remember/sync-rules.cjs +169 -0
- package/packages/droid/commands/remember/version-check.cjs +214 -0
- package/packages/droid/commands/remember.md +78 -10
- package/packages/droid/droids/orchestrator.md +2 -2
- package/packages/opencode/AGENTS.md +1 -2
- package/packages/opencode/agent/orchestrator.md +2 -2
- package/packages/opencode/command/refactor.md +8 -2
- package/packages/opencode/command/remember/stub-check.cjs +197 -0
- package/packages/opencode/command/remember/sync-rules.cjs +169 -0
- package/packages/opencode/command/remember/version-check.cjs +214 -0
- package/packages/opencode/command/remember.md +78 -10
- package/packages/opencode/opencode.jsonc +0 -10
- package/packages/subagentic-manual.md +15 -15
- package/packages/ampcode/agents/context-builder.md +0 -144
- package/packages/claude/agents/context-builder.md +0 -145
- package/packages/droid/droids/context-builder.md +0 -144
- package/packages/opencode/agent/context-builder.md +0 -148
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,127 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [3.0.0] - 2026-09-04
|
|
13
|
+
|
|
14
|
+
### Breaking
|
|
15
|
+
- **The `context-builder` subagent is removed — catalog goes 11 -> 10 agents.**
|
|
16
|
+
`docs-builder` supersedes its Tier 2/3 work (creating/splitting `docs/*.md`,
|
|
17
|
+
validated on real corpora; `docs/index.md` via `index-flat` is the sole index,
|
|
18
|
+
same reason `docs/wiki-index.md` was already deleted). Tier 1 (author
|
|
19
|
+
`CLAUDE.md`) was the only unique part, and its one load-bearing invariant —
|
|
20
|
+
`CLAUDE.md` must `@`-include `.claude/remember/MEMORY.md` and nothing else —
|
|
21
|
+
is now a mechanical gate in `stub-check.cjs`, run every `/remember`; a prose
|
|
22
|
+
rule inside a subagent was always the weaker enforcement. **Migration:** for
|
|
23
|
+
the doc-authoring work `context-builder` did, use `/docs-builder`; for a
|
|
24
|
+
cold-start `CLAUDE.md` on a brand-new project, use Claude Code's own `/init`.
|
|
25
|
+
Orchestrator routing's Brownfield workflow now starts at `system-architect`.
|
|
26
|
+
|
|
27
|
+
### Security
|
|
28
|
+
- **`sync-rules.cjs` and `stub-check.cjs` no longer write through a symlink out
|
|
29
|
+
of the repo they were invoked on.** `/remember` runs across a fleet of sibling
|
|
30
|
+
checkouts, so a relative link only has to reach a neighbor. Three routes were
|
|
31
|
+
reproduced and closed: a dangling link at the write target (followed by
|
|
32
|
+
`writeFileSync` as if the file were merely absent), a symlinked parent
|
|
33
|
+
directory (invisible to a leaf-only `lstat` guard), and a symlinked
|
|
34
|
+
`CLAUDE.md` that `stub-check.cjs` rewrote in place with no rename-away step.
|
|
35
|
+
`escapesRepo()` resolves the deepest existing ancestor with `realpath` and
|
|
36
|
+
confirms the write still lands inside the repo, `lstat`-ing the leaf for the
|
|
37
|
+
dangling case; refusal is loud (an `escapes` action / a skip message), never
|
|
38
|
+
silent. Mirrored to all four kits.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- **`AGENT_RULES.md` is now kept current in every repo, instead of written once and
|
|
42
|
+
forgotten.** `remember/sync-rules.cjs` byte-compares the repo copy against the
|
|
43
|
+
template shipped beside it on every `/remember` run: identical does nothing at all,
|
|
44
|
+
absent copies it in, and a differing body is moved to a single `AGENT_RULES.md.bak`
|
|
45
|
+
before the new one lands. The `.bak` is one file, deliberately — a customised body
|
|
46
|
+
survives exactly one release, so fold your changes in before the next update. This
|
|
47
|
+
replaced a bootstrap-once rule that had left a measured 35 local repos many releases
|
|
48
|
+
behind, after three hand sweeps failed to hold.
|
|
49
|
+
- **`remember/stub-check.cjs` asserts the config stub's SHAPE and repairs it.** v2.19
|
|
50
|
+
demoted `AGENT_RULES.md` from an `@`-include to a plain pointer — an `@`-include
|
|
51
|
+
hot-loads ~300 lines into every session — but nothing enforced it, and 21 of 37
|
|
52
|
+
measured repos had drifted back. It also repairs a MEMORY include that is not
|
|
53
|
+
`@<dir>/remember/MEMORY.md`, since a bare `@MEMORY.md` resolves to a nonexistent
|
|
54
|
+
root file and hot memory then silently never loads. It edits only *inside* the
|
|
55
|
+
marker pairs and only the mechanism; the prose in those blocks stays user-owned.
|
|
56
|
+
It will **not** repoint a MEMORY include at a file that does not exist — a repo
|
|
57
|
+
still on the pre-rename `.claude/memory/` layout has a live file at the old path,
|
|
58
|
+
and breaking a working include to satisfy a naming convention is worse than saying
|
|
59
|
+
so.
|
|
60
|
+
- **`remember/version-check.cjs` nudges when the installed liteagents is behind.**
|
|
61
|
+
One line in `/remember` step 0, advice only — it never installs. Any failure
|
|
62
|
+
(offline, DNS, timeout) is a silent skip, because a memory command that hangs on a
|
|
63
|
+
network call is worse than one that misses a nudge. The installer now stamps
|
|
64
|
+
`liteagents_version` into its manifest so the check does not shell out to
|
|
65
|
+
`npm ls -g` on every run (measured 502ms -> 113ms warm).
|
|
66
|
+
- **The installer says where your backup went.** On a reinstall it now closes with the
|
|
67
|
+
path of every install it moved aside, and notes that an edited `AGENT_RULES.md` is
|
|
68
|
+
preserved in the backup's `remember/` folder. A fresh install's output is unchanged.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
- **`/remember` reports every write verbatim.** Step 8 relays exactly what
|
|
72
|
+
`version-check.cjs`, `sync-rules.cjs` and `stub-check.cjs` printed, and nothing when
|
|
73
|
+
they printed nothing. Paraphrasing could drop the `AGENT_RULES.md.bak` filename,
|
|
74
|
+
which is the one thing someone who just lost their edits needs.
|
|
75
|
+
- **The stub check survives a quiet run.** Step 1's guard stops the run when there are
|
|
76
|
+
no unprocessed stashes and no friction output — which is exactly the state of a
|
|
77
|
+
stale, unattended repo. `stub-check.cjs` now runs before that stop; skipping it on
|
|
78
|
+
quiet runs is how a repo with nothing to remember stayed broken forever.
|
|
79
|
+
- **`/refactor` hands the review back instead of chaining it.** Step 6's "commit, then
|
|
80
|
+
run `/branch-review`" read as a sequence to execute; a field run started the review
|
|
81
|
+
off the owner's "commit" and the owner objected. It now states that this is a
|
|
82
|
+
sentence to say, that both are the user's separate calls, and that "commit"/"yes"/"go"
|
|
83
|
+
authorizes the commit and nothing after it.
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
- Four places stated the memory-include mechanism using the broken bare `@MEMORY.md`
|
|
87
|
+
form, including an anti-pattern table an agent reads before writing the config.
|
|
88
|
+
- `poc/friction-file-referents/README.md` claimed everything needed to audit it was in
|
|
89
|
+
the directory. `score.py` reproduces only the LLM-experiment table (Findings 4-6);
|
|
90
|
+
the mechanical-study numbers (Findings 2-3) come from a run whose script was never
|
|
91
|
+
committed. Now stated precisely.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## [2.24.1] - 2026-09-03
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
- **The file-referent ledger half is SHELVED — no demonstrated problem.** The v2.24.0
|
|
99
|
+
entry below says shipping the ledger side is "gated on a future exact-label-agreement
|
|
100
|
+
measurement," which implied the work was justified and merely queued. That premise was
|
|
101
|
+
never checked, and it is wrong. The harm this channel prevents — a false match
|
|
102
|
+
inflating an entry's count until a `hot` entry hits `recurred_while_hot >= 2` and has
|
|
103
|
+
its rule rewritten — has never occurred: `ag-001` is the only `hot` entry, its
|
|
104
|
+
`recurred_while_hot` is 1 against a threshold of 2, and its two `attempts` are a
|
|
105
|
+
deliberate August rephrasing rather than a false-match rewrite. A POC also established
|
|
106
|
+
the naive ledger design would not have worked: cluster-level unions collide at 1.8%,
|
|
107
|
+
but entry-level unions collide at **38%**, because an entry accumulates paths across
|
|
108
|
+
every session it matches and inevitably collects `README.md` / `CLAUDE.md`. A
|
|
109
|
+
document-frequency filter repairs it (9.5% at df<=2), but repairing a fix for a problem
|
|
110
|
+
that is not occurring is not a reason to ship. **The incoming half is kept** — it costs
|
|
111
|
+
nothing, adds no LLM step, and accumulates evidence for free.
|
|
112
|
+
- **The un-shelve trigger is now checkable rather than a judgement call:** a false match
|
|
113
|
+
observed under a sonnet-class classifier, OR `ag-001` reaching `recurred_while_hot = 2`
|
|
114
|
+
on evidence that is not about validation.
|
|
115
|
+
- **And that trigger must NOT be exact-label agreement.** A fourth POC arm with the
|
|
116
|
+
user's quotes stripped from both sides scored best on every stability measure (0.900
|
|
117
|
+
exact agreement, 3/20 unstable) and is plainly the worst arm: it unanimously dropped
|
|
118
|
+
three clusters (`ag-012`, `ag-007`, `ag-001`) that all nine quote-carrying runs matched
|
|
119
|
+
unanimously, and named antigens after session hashes. It wins by having nothing to go
|
|
120
|
+
on and defaulting to `drop`. High agreement on "I don't know" is not quality — the same
|
|
121
|
+
degenerate shape as the severity axis that was seeded and rated on the same signal. A
|
|
122
|
+
re-attempt needs a human-labelled gold set.
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
- `poc/friction-file-referents/` — the corpus, all four arm prompts, raw labels from 12
|
|
126
|
+
runs, and `score.py`, which reproduces every table above. Kept so a re-attempt starts
|
|
127
|
+
from the numbers. Outside `package.json`'s `files` allowlist; not published.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
10
131
|
## [2.24.0] - 2026-09-03
|
|
11
132
|
|
|
12
133
|
### Added
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
╚══════╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
**AI development toolkit with
|
|
12
|
+
**AI development toolkit with 10 specialized agents and 18 commands per tool**
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<img src="https://img.shields.io/github/package-json/v/hamr0/liteagents?label=version&color=2a4f8c" alt="version (auto from package.json)">
|
|
@@ -45,10 +45,10 @@ liteagents
|
|
|
45
45
|
|
|
46
46
|
### Supported Tools
|
|
47
47
|
|
|
48
|
-
- **Claude Code** -
|
|
49
|
-
- **Opencode** -
|
|
50
|
-
- **Ampcode** -
|
|
51
|
-
- **Droid** -
|
|
48
|
+
- **Claude Code** - 10 subagents + 8 skills + 10 commands (+ optional live-canvas channel plugin)
|
|
49
|
+
- **Opencode** - 10 agent references + 18 commands
|
|
50
|
+
- **Ampcode** - 10 subagents + 18 commands
|
|
51
|
+
- **Droid** - 10 agent references + 18 commands
|
|
52
52
|
|
|
53
53
|
**Key Difference:**
|
|
54
54
|
- **Claude Code**: Full subagent system with orchestrator + skills (auto-triggering)
|
|
@@ -73,7 +73,7 @@ liteagents
|
|
|
73
73
|
|
|
74
74
|
## Hot Memory — project-local learning from your own sessions
|
|
75
75
|
|
|
76
|
-
Liteagents ships a two-command pipeline that turns Claude Code's session logs into project-local memory. No databases, no external services, just markdown files the assistant reads via
|
|
76
|
+
Liteagents ships a two-command pipeline that turns Claude Code's session logs into project-local memory. No databases, no external services, just markdown files the assistant reads via an `@.claude/remember/MEMORY.md` include.
|
|
77
77
|
|
|
78
78
|
```
|
|
79
79
|
/stash → /remember
|
|
@@ -81,9 +81,23 @@ capture analyze + consolidate
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
- **`/stash`** — snapshot the current session's context before compaction or handoff; nudges you to consolidate once a few stashes pile up. The write-up runs on a mid-tier model, dispatched as a background subagent where your tool supports it, so the session isn't blocked on formatting/file I/O
|
|
84
|
-
- **`/remember`** — runs friction analysis automatically (mining JSONL session logs across *all* your projects for frustration signals, failed flows, and abandonment patterns, clustered into antigen candidates), then consolidates stashes + friction antigens into `.claude/remember/MEMORY.md`; auto-injected into `CLAUDE.md` via
|
|
84
|
+
- **`/remember`** — runs friction analysis automatically (mining JSONL session logs across *all* your projects for frustration signals, failed flows, and abandonment patterns, clustered into antigen candidates), then consolidates stashes + friction antigens into `.claude/remember/MEMORY.md`; auto-injected into `CLAUDE.md` via an explicit `@.claude/remember/MEMORY.md` include so every future session benefits. Per-stash extraction runs as concurrent subagent calls on a mid-tier model — no model name hardcoded, so it works with whatever your tool has configured
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
### `AGENT_RULES.md` — the standards doc that primes every session
|
|
87
|
+
|
|
88
|
+
`/remember` installs an `AGENT_RULES.md` coding-standards template into `.claude/remember/` (`.factory/`, `.amp/`, `.opencode/` for the other tools) and references it from `CLAUDE.md` (`AGENT.md` / `AGENTS.md`) as a plain pointer, so the assistant consults it when building something new. It is a static guide — not something the pipeline learns or extracts.
|
|
89
|
+
|
|
90
|
+
**It is kept current on every `/remember` run**, because a shipped standards doc that never updates is a stale one: 35 local repos were measured drifting many releases behind before this existed. Each run compares your copy against the template that came with your installed liteagents:
|
|
91
|
+
|
|
92
|
+
| your copy | what happens |
|
|
93
|
+
|---|---|
|
|
94
|
+
| identical to the template | nothing at all — no write, no output |
|
|
95
|
+
| missing | copied in |
|
|
96
|
+
| **different** | moved to `AGENT_RULES.md.bak`, new template copied in, both reported |
|
|
97
|
+
|
|
98
|
+
So **edits are never destroyed, but they are not preserved in place either.** If you have customised your rules, fold your changes into the new `AGENT_RULES.md` after an update — `AGENT_RULES.md.bak` is a *single* file that the next update overwrites, so a customised body survives one release, not two.
|
|
99
|
+
|
|
100
|
+
To get a newer template in the first place, update the package: `/remember` tells you when your install is behind, and `npm i -g liteagents@latest && liteagents` refreshes it (your previous install is backed up automatically).
|
|
87
101
|
|
|
88
102
|
What you get is a memory that *learns from your own mistakes and interventions*, grows quietly in your repo, and works anywhere Claude Code runs. The friction pass inside `/remember` scans all your projects and gives you a per-repo reliability verdict:
|
|
89
103
|
|
|
@@ -107,7 +121,7 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
107
121
|
|
|
108
122
|
## What's Included
|
|
109
123
|
|
|
110
|
-
###
|
|
124
|
+
### 10 Agents
|
|
111
125
|
|
|
112
126
|
**Workflow Agents (3):**
|
|
113
127
|
- **1-create-prd** - Define scope with structured Product Requirement Documents
|
|
@@ -118,7 +132,6 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
118
132
|
- **orchestrator** - Analyze intent, coordinate workflows, route to optimal agent sequences
|
|
119
133
|
- **code-developer** - Implementation, debugging, refactoring, code best practices
|
|
120
134
|
- **quality-assurance** - Test architecture, quality gates, requirements traceability, risk assessment
|
|
121
|
-
- **context-builder** - Initialize project context, discover documentation, create knowledge bases
|
|
122
135
|
- **feature-planner** - Epics, user stories, prioritization, backlog management, retrospectives
|
|
123
136
|
- **market-researcher** - Market analysis, competitive research, project discovery, brainstorming
|
|
124
137
|
- **system-architect** - System design, technology selection, API design, scalability planning
|
|
@@ -167,6 +180,9 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
167
180
|
| **[remember-README.md](docs/product/remember-README.md)** | How hot memory works — the `/stash` → `/remember` pipeline, the friction sensor, and the antigen ledger |
|
|
168
181
|
| **[docs-builder-README.md](docs/product/docs-builder-README.md)** | How `/docs-builder` works — the reorg/cleanup modes, what it measurably costs, and the ledger that tracks doc drift |
|
|
169
182
|
| **[branch-review-README.md](docs/product/branch-review-README.md)** | How the pre-merge gate works — the three stages, what blocks a merge, and the `/branch-review` → fix ledger → `/refactor` loop |
|
|
183
|
+
| **[live-canvas/README.md](packages/claude/skills/live-canvas/README.md)** | How `/live-canvas` works — the two modes, the click-to-annotate channel, and the Claude Code plugin setup |
|
|
184
|
+
| **[docs/README.md](docs/README.md)** | Entry point for this repo’s own docs — what lives in `product/`, `archive/` and the operation log |
|
|
185
|
+
| **[docs/index.md](docs/index.md)** | The generated index of every doc, with heading ranges. Rebuilt by `/docs-builder` on every reorg or split |
|
|
170
186
|
|
|
171
187
|
---
|
|
172
188
|
|
|
@@ -200,7 +216,7 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
200
216
|
|
|
201
217
|
## Stats
|
|
202
218
|
|
|
203
|
-
- **
|
|
219
|
+
- **10** Specialized Agents
|
|
204
220
|
- **18** Workflow Commands & Skills
|
|
205
221
|
- **4** Supported Tools (Claude, Opencode, Ampcode, Droid)
|
|
206
222
|
- **Apache-2.0** License
|
package/installer/cli.js
CHANGED
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const readline = require('readline');
|
|
16
|
+
const os = require('os');
|
|
16
17
|
|
|
17
18
|
// Single source of truth for version; UPDATE_VERSION.sh bumps only package.json.
|
|
18
19
|
const PACKAGE_JSON = require('../package.json');
|
|
19
20
|
const PACKAGE_VERSION = PACKAGE_JSON.version;
|
|
20
21
|
// Banner counts derived from the description field — same source as README.
|
|
21
|
-
const AGENT_COUNT = (PACKAGE_JSON.description.match(/(\d+)\s+specialized agents/) || [, '
|
|
22
|
+
const AGENT_COUNT = (PACKAGE_JSON.description.match(/(\d+)\s+specialized agents/) || [, '10'])[1];
|
|
22
23
|
const COMMAND_COUNT = (PACKAGE_JSON.description.match(/(\d+)\s+commands/) || [, '18'])[1];
|
|
23
24
|
|
|
24
25
|
// ANSI color codes
|
|
@@ -30,9 +31,35 @@ const colors = {
|
|
|
30
31
|
yellow: '\x1b[33m',
|
|
31
32
|
cyan: '\x1b[36m',
|
|
32
33
|
magenta: '\x1b[35m',
|
|
33
|
-
red: '\x1b[31m'
|
|
34
|
+
red: '\x1b[31m',
|
|
35
|
+
dim: '\x1b[2m'
|
|
34
36
|
};
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Render the closing note about backed-up installs, printed after `Done!`.
|
|
40
|
+
* Returns '' when backupLog is empty so a fresh install's output is
|
|
41
|
+
* unchanged. Never names `commands/` vs `command/` (opencode uses the
|
|
42
|
+
* singular form) — the note points at the shared `remember/` folder inside
|
|
43
|
+
* the backup, which is the same for every tool.
|
|
44
|
+
*
|
|
45
|
+
* @param {Array<{original: string, backup: string}>} backupLog
|
|
46
|
+
* @returns {string} Text to print (includes its own leading newline), or ''.
|
|
47
|
+
*/
|
|
48
|
+
function formatBackupClosingNote(backupLog) {
|
|
49
|
+
if (!backupLog || backupLog.length === 0) {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const lines = [`${colors.yellow}Previous install(s) backed up:${colors.reset}`];
|
|
54
|
+
for (const entry of backupLog) {
|
|
55
|
+
lines.push(` ${entry.backup.replace(os.homedir(), '~')}`);
|
|
56
|
+
}
|
|
57
|
+
lines.push(`${colors.dim}If you had edited AGENT_RULES.md, your copy is preserved in`);
|
|
58
|
+
lines.push(`each backup's remember/ folder.${colors.reset}`);
|
|
59
|
+
|
|
60
|
+
return `\n${lines.join('\n')}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
class InteractiveInstaller {
|
|
37
64
|
constructor() {
|
|
38
65
|
this.rl = readline.createInterface({
|
|
@@ -701,6 +728,11 @@ ${colors.bright}v${PACKAGE_VERSION} | ${AGENT_COUNT} agents + ${COMMAND_COUNT} c
|
|
|
701
728
|
}
|
|
702
729
|
|
|
703
730
|
console.log(`\n${colors.bright}${colors.green}Done!${colors.reset}`);
|
|
731
|
+
|
|
732
|
+
const closingNote = formatBackupClosingNote(installationEngine.backupLog);
|
|
733
|
+
if (closingNote) {
|
|
734
|
+
console.log(closingNote);
|
|
735
|
+
}
|
|
704
736
|
}
|
|
705
737
|
|
|
706
738
|
/**
|
|
@@ -765,4 +797,5 @@ if (require.main === module) {
|
|
|
765
797
|
installer.run().catch(console.error);
|
|
766
798
|
}
|
|
767
799
|
|
|
768
|
-
module.exports = InteractiveInstaller;
|
|
800
|
+
module.exports = InteractiveInstaller;
|
|
801
|
+
module.exports.formatBackupClosingNote = formatBackupClosingNote;
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
|
+
// Same single source of truth as installer/cli.js: UPDATE_VERSION.sh bumps
|
|
11
|
+
// only package.json. Stamped into every manifest so version-check.cjs can read
|
|
12
|
+
// the installed version without shelling out to `npm ls -g` (~500ms per run).
|
|
13
|
+
const LITEAGENTS_VERSION = require('../package.json').version;
|
|
14
|
+
|
|
10
15
|
class InstallationEngine {
|
|
11
16
|
constructor(pathManager, packageManager) {
|
|
12
17
|
this.pathManager = pathManager;
|
|
@@ -448,7 +453,10 @@ class InstallationEngine {
|
|
|
448
453
|
const manifest = {
|
|
449
454
|
...template,
|
|
450
455
|
variant,
|
|
456
|
+
// `version` is the MANIFEST SCHEMA version and always has been; the
|
|
457
|
+
// liteagents release that wrote it is a separate field. Do not merge them.
|
|
451
458
|
version: '1.1.0',
|
|
459
|
+
liteagents_version: LITEAGENTS_VERSION,
|
|
452
460
|
installed_at: new Date().toISOString(),
|
|
453
461
|
variantInfo: {
|
|
454
462
|
name: variantMetadata.name,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liteagents",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI development toolkit with
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "AI development toolkit with 10 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": {
|
|
7
7
|
"liteagents": "./installer/cli.js",
|
|
@@ -6,7 +6,7 @@ Ampcode is a lightweight CLI tool that provides workflow automation commands.
|
|
|
6
6
|
|
|
7
7
|
These subagents are available when using Ampcode CLI.
|
|
8
8
|
|
|
9
|
-
### Subagents (
|
|
9
|
+
### Subagents (10 total)
|
|
10
10
|
|
|
11
11
|
| ID | Title | When To Use |
|
|
12
12
|
|---|---|---|
|
|
@@ -14,7 +14,6 @@ These subagents are available when using Ampcode CLI.
|
|
|
14
14
|
| 2-generate-tasks | 2-Generate Tasks | Detailed Planning - use to break down the PRD into a granular, actionable task list |
|
|
15
15
|
| 3-process-task-list | 3-Process Task List | Iterative Implementation - use to guide the AI to tackle one task at a time, allowing you to review and approve each change |
|
|
16
16
|
| code-developer | Full Stack Developer | Use for code implementation, debugging, refactoring, and development best practices |
|
|
17
|
-
| context-builder | Context Initializer | Use to initialize project context for new/existing projects, discover and organize documentation, create AGENT.md and KNOWLEDGE_BASE.md for optimal token-efficient memory |
|
|
18
17
|
| feature-planner | Product Manager | Use for creating epics and user stories, prioritization, backlog navigation, story refinement, and retrospectives |
|
|
19
18
|
| market-researcher | Business Analyst | Use for market research, brainstorming, competitive analysis, project briefs, and initial project discovery |
|
|
20
19
|
| orchestrator | Master Orchestrator | Use for workflow coordination, multi-agent tasks, role switching guidance, and when unsure which specialist to consult |
|
|
@@ -77,7 +77,7 @@ Predefined multi-agent sequences:
|
|
|
77
77
|
| Workflow | Sequence | When |
|
|
78
78
|
|----------|----------|------|
|
|
79
79
|
| **Greenfield** | market-researcher → feature-planner → 1-create-prd → 2-generate-tasks → 3-process-task-list | New product/feature from scratch |
|
|
80
|
-
| **Brownfield** |
|
|
80
|
+
| **Brownfield** | system-architect → feature-planner | Understand existing codebase |
|
|
81
81
|
| **Feature** | feature-planner → 1-create-prd → 2-generate-tasks → 3-process-task-list | Add feature to existing product |
|
|
82
82
|
| **Bug Fix** | code-developer → quality-assurance | Fix and verify |
|
|
83
83
|
| **Sprint** | feature-planner (*sprint-plan) → 2-generate-tasks | Plan sprint from backlog |
|
|
@@ -96,7 +96,7 @@ Quick routing when user has clear intent:
|
|
|
96
96
|
| review, quality, test | quality-assurance |
|
|
97
97
|
| design, UI, wireframe | ui-designer |
|
|
98
98
|
| architecture, tech, design doc | system-architect |
|
|
99
|
-
| understand,
|
|
99
|
+
| understand, brownfield, existing codebase | system-architect |
|
|
100
100
|
|
|
101
101
|
## Commands
|
|
102
102
|
|
|
@@ -72,8 +72,14 @@ refactor and how to close each item.
|
|
|
72
72
|
behaviour change is not a refactor — leave the bullet, note it in the report.
|
|
73
73
|
5. Run the tests as described below. Then report: **fixed / dropped / left**
|
|
74
74
|
with the reason per left item, and the remaining bullet count.
|
|
75
|
-
6. Say plainly: **commit, then run
|
|
76
|
-
mode is a fixer, not a review,
|
|
75
|
+
6. **Hand it back; do not chain it.** Say plainly: **commit, then run
|
|
76
|
+
`/branch-review`** on this branch — ledger mode is a fixer, not a review,
|
|
77
|
+
and its diff gets the ordinary gate. That is a sentence you *say*, not a
|
|
78
|
+
sequence you *run*. They are two separate calls and both are the user's:
|
|
79
|
+
an answer of "commit", "yes" or "go" authorizes the commit and nothing
|
|
80
|
+
after it. Never start `/branch-review` off the back of it. Observed in the
|
|
81
|
+
field: a run chained the review onto the owner's "commit" and the owner
|
|
82
|
+
objected.
|
|
77
83
|
|
|
78
84
|
## Goals
|
|
79
85
|
- Reduce complexity
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* stub-check.cjs — asserts the two managed blocks in the tool config carry the
|
|
6
|
+
* CURRENT stub shape, and repairs the shape when it is wrong.
|
|
7
|
+
*
|
|
8
|
+
* Shape, not content. The blocks' prose is user-owned — step 5 writes it once
|
|
9
|
+
* and never re-imposes it, because users trim it deliberately. What this script
|
|
10
|
+
* touches is only the mechanism:
|
|
11
|
+
*
|
|
12
|
+
* MEMORY block @<PROJECT_DIR>/remember/MEMORY.md — an @-include.
|
|
13
|
+
* A bare `@MEMORY.md` resolves relative to the file that
|
|
14
|
+
* contains it, so in a repo root it names a file that does
|
|
15
|
+
* not exist and hot memory silently never loads.
|
|
16
|
+
*
|
|
17
|
+
* AGENT_RULES block <PROJECT_DIR>/remember/AGENT_RULES.md — a PLAIN pointer.
|
|
18
|
+
* v2.19 demoted it from an @-include on purpose: it is a
|
|
19
|
+
* standards guide to consult when building something new,
|
|
20
|
+
* not hot context, and @-including it loads ~300 lines into
|
|
21
|
+
* every session. Measured 2026-09-03: 21 of 37 local repos
|
|
22
|
+
* still carried the pre-v2.19 @-include.
|
|
23
|
+
*
|
|
24
|
+
* Two deliberate limits:
|
|
25
|
+
*
|
|
26
|
+
* - It only edits INSIDE a marker pair. A pointer elsewhere in the config is
|
|
27
|
+
* the user's prose and is left alone.
|
|
28
|
+
* - It never repoints the MEMORY include at a file that does not exist. An
|
|
29
|
+
* un-migrated `.amp/memory/` repo has a live MEMORY.md at the old path;
|
|
30
|
+
* rewriting it to the new one would break a working include to satisfy a
|
|
31
|
+
* naming convention. That case is reported, not repaired.
|
|
32
|
+
*
|
|
33
|
+
* Missing marker pairs are not this script's business — step 5 creates them.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const path = require('path');
|
|
38
|
+
|
|
39
|
+
// The ONE pair of lines that differs across packages.
|
|
40
|
+
const PROJECT_DIR = '.amp';
|
|
41
|
+
const CONFIG_FILE = 'AGENT.md';
|
|
42
|
+
|
|
43
|
+
const MEM = { start: '<!-- MEMORY:START -->', end: '<!-- MEMORY:END -->' };
|
|
44
|
+
const RULES = { start: '<!-- AGENT_RULES:START -->', end: '<!-- AGENT_RULES:END -->' };
|
|
45
|
+
|
|
46
|
+
const MEMORY_REL = `${PROJECT_DIR}/remember/MEMORY.md`;
|
|
47
|
+
const RULES_REL = `${PROJECT_DIR}/remember/AGENT_RULES.md`;
|
|
48
|
+
|
|
49
|
+
/** lstat, not existsSync: existsSync follows links, so a DANGLING link reads
|
|
50
|
+
* as absent and gets walked straight past. */
|
|
51
|
+
function lexists(p) {
|
|
52
|
+
try { fs.lstatSync(p); return true; } catch (e) { return false; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* True when writing to `target` would land outside `repo`.
|
|
57
|
+
*
|
|
58
|
+
* There are two ways out and a guard on only one of them is false safety:
|
|
59
|
+
* `target` may itself be a symlink — including a dangling one, which reads as
|
|
60
|
+
* "the file is absent" and is still followed on write — or any parent
|
|
61
|
+
* directory may be a link pointing elsewhere. This runs across a whole fleet
|
|
62
|
+
* of repos, so a relative link only has to reach a sibling checkout.
|
|
63
|
+
*/
|
|
64
|
+
function escapesRepo(repo, target) {
|
|
65
|
+
let root;
|
|
66
|
+
try { root = fs.realpathSync(repo); } catch (e) { return true; }
|
|
67
|
+
|
|
68
|
+
// Walk up to the deepest ancestor that exists; anything below it cannot be
|
|
69
|
+
// a link yet, so only the existing part needs resolving.
|
|
70
|
+
const tail = [];
|
|
71
|
+
let dir = path.dirname(target);
|
|
72
|
+
while (!lexists(dir)) {
|
|
73
|
+
tail.unshift(path.basename(dir));
|
|
74
|
+
const up = path.dirname(dir);
|
|
75
|
+
if (up === dir) return true; // walked off the filesystem root
|
|
76
|
+
dir = up;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let resolved;
|
|
80
|
+
try { resolved = path.join(fs.realpathSync(dir), ...tail, path.basename(target)); }
|
|
81
|
+
catch (e) { return true; } // an ancestor is a dangling link
|
|
82
|
+
|
|
83
|
+
// The last component can be a link even when every directory above it is
|
|
84
|
+
// clean — that is the dangling-file case.
|
|
85
|
+
try { if (fs.lstatSync(resolved).isSymbolicLink()) return true; } catch (e) { /* absent: fine */ }
|
|
86
|
+
|
|
87
|
+
return resolved !== root && !resolved.startsWith(root + path.sep);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Index range of the lines strictly between a marker pair, or null. */
|
|
91
|
+
function blockRange(lines, markers) {
|
|
92
|
+
const s = lines.findIndex((l) => l.trim() === markers.start);
|
|
93
|
+
if (s === -1) return null;
|
|
94
|
+
const e = lines.findIndex((l, i) => i > s && l.trim() === markers.end);
|
|
95
|
+
if (e === -1) return null;
|
|
96
|
+
return { from: s + 1, to: e }; // [from, to)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @returns {{fixes:string[], notes:string[], changed:boolean}}
|
|
101
|
+
* fixes — repairs written to disk
|
|
102
|
+
* notes — wrong shapes deliberately left alone, with the reason
|
|
103
|
+
*/
|
|
104
|
+
function check(repo) {
|
|
105
|
+
const config = path.join(repo, CONFIG_FILE);
|
|
106
|
+
const fixes = [];
|
|
107
|
+
const notes = [];
|
|
108
|
+
|
|
109
|
+
// Checked before the read, not just before the write: a repair decided
|
|
110
|
+
// from a followed link is already the wrong decision.
|
|
111
|
+
if (escapesRepo(repo, config)) {
|
|
112
|
+
return { fixes, notes: [`${CONFIG_FILE} not checked: it leaves the repo via a symlink`], changed: false };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let text;
|
|
116
|
+
try { text = fs.readFileSync(config, 'utf8'); } catch (e) {
|
|
117
|
+
return { fixes, notes, changed: false }; // no config: step 5 will create one
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lines = text.split('\n');
|
|
121
|
+
let changed = false;
|
|
122
|
+
|
|
123
|
+
// ── MEMORY block: must be an @-include naming the explicit path ────────────
|
|
124
|
+
const mem = blockRange(lines, MEM);
|
|
125
|
+
if (mem) {
|
|
126
|
+
const i = lines.findIndex(
|
|
127
|
+
(l, n) => n >= mem.from && n < mem.to && /^@\S*MEMORY\.md\s*$/.test(l.trim()));
|
|
128
|
+
if (i === -1) {
|
|
129
|
+
notes.push(`${CONFIG_FILE}: MEMORY block has no @-include — hot memory does not load`);
|
|
130
|
+
} else {
|
|
131
|
+
const want = `@${MEMORY_REL}`;
|
|
132
|
+
const have = lines[i].trim();
|
|
133
|
+
if (have !== want) {
|
|
134
|
+
if (fs.existsSync(path.join(repo, MEMORY_REL))) {
|
|
135
|
+
lines[i] = want;
|
|
136
|
+
changed = true;
|
|
137
|
+
fixes.push(`${CONFIG_FILE}: MEMORY include repaired, ${have} → ${want}`);
|
|
138
|
+
} else {
|
|
139
|
+
// The old path may be the only one with a file behind it.
|
|
140
|
+
notes.push(
|
|
141
|
+
`${CONFIG_FILE}: MEMORY include is ${have}, not ${want} — left as is, `
|
|
142
|
+
+ `${MEMORY_REL} does not exist yet`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── AGENT_RULES block: must be a PLAIN pointer, never an @-include ─────────
|
|
149
|
+
const rules = blockRange(lines, RULES);
|
|
150
|
+
if (rules) {
|
|
151
|
+
const i = lines.findIndex(
|
|
152
|
+
(l, n) => n >= rules.from && n < rules.to && /^@\S*AGENT_RULES\.md\s*$/.test(l.trim()));
|
|
153
|
+
if (i !== -1) {
|
|
154
|
+
// Demote in place. The path is kept as written — only the @ is dropped,
|
|
155
|
+
// because the @ is the defect and the path may be a deliberate variant.
|
|
156
|
+
const had = lines[i].trim();
|
|
157
|
+
lines[i] = had.slice(1);
|
|
158
|
+
changed = true;
|
|
159
|
+
fixes.push(
|
|
160
|
+
`${CONFIG_FILE}: AGENT_RULES pointer demoted from an @-include (${had} → ${had.slice(1)}) `
|
|
161
|
+
+ `— it is a standards guide, not hot context`);
|
|
162
|
+
} else {
|
|
163
|
+
const hasPointer = lines
|
|
164
|
+
.slice(rules.from, rules.to)
|
|
165
|
+
.some((l) => /AGENT_RULES\.md/.test(l));
|
|
166
|
+
if (!hasPointer) {
|
|
167
|
+
notes.push(`${CONFIG_FILE}: AGENT_RULES block has no path pointer — nothing points at the rules`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (changed) {
|
|
173
|
+
try {
|
|
174
|
+
fs.writeFileSync(config, lines.join('\n'));
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return { fixes: [], notes: [`${CONFIG_FILE} not repaired: ${e.message}`], changed: false };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { fixes, notes, changed };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function main() {
|
|
184
|
+
const repo = process.argv[2] || process.cwd();
|
|
185
|
+
const r = check(repo);
|
|
186
|
+
for (const line of r.fixes) process.stdout.write(`${line}\n`);
|
|
187
|
+
for (const line of r.notes) process.stdout.write(`${line}\n`);
|
|
188
|
+
// Silent when the shape is already current — the common case.
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (require.main === module) {
|
|
192
|
+
// A passenger on /remember, like version-check.cjs and sync-rules.cjs: it
|
|
193
|
+
// never gets to fail the run it rides in.
|
|
194
|
+
try { main(); } catch (e) { /* silent */ }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = { check, PROJECT_DIR, CONFIG_FILE, MEMORY_REL, RULES_REL };
|