agent-templates 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADOPTING.md +5 -1
- package/package.json +1 -1
- package/patterns/three-agent-architect-builder-reviewer/README.md +2 -0
- package/patterns/three-agent-architect-builder-reviewer/scaffold/INSTALL.md +1 -1
- package/patterns/three-agent-architect-builder-reviewer/scaffold/claude-md-snippet.md +3 -1
- package/scripts/adopt.mjs +65 -4
package/ADOPTING.md
CHANGED
|
@@ -16,7 +16,11 @@ npx agent-templates@latest adopt three-agent-architect-builder-reviewer C:\path\
|
|
|
16
16
|
|
|
17
17
|
Pin an exact version for reproducibility (`npx agent-templates@0.1.0 …`) — a published version is a verified snapshot of the catalog's recommendations. Alternatives: `npx github:Ruihang2017/agent-templates …` (tracks the repo, no release needed; npx caches git installs — `--prefer-online` picks up updates), or from a checkout: `node scripts/adopt.mjs three-agent-architect-builder-reviewer <target-dir>`.
|
|
18
18
|
|
|
19
|
-
Idempotent (re-runs skip what exists; `--force` overwrites). It installs the scaffold `.claude/` (agents with pinned model/effort, stage commands, write guard, workflows, publish script), the universal `templates/ticket.template.md`, the platform tracker templates
|
|
19
|
+
Idempotent (re-runs skip what exists; `--force` overwrites). It installs the scaffold `.claude/` (agents with pinned model/effort, stage commands, write guard, workflows, publish script), the universal `templates/ticket.template.md`, the platform tracker templates into `.github/` or `.gitlab/`, creates `docs/prd|adr|plans/`, copies a root `PRD.md` to `docs/PRD.md`, and seeds or appends `CLAUDE.md` from the pattern snippet (marker-checked, never duplicated).
|
|
20
|
+
|
|
21
|
+
**Platform (GitHub vs GitLab)** is resolved before anything is installed — from the origin remote host, else a repo-local signal (`.gitlab-ci.yml` → GitLab, existing `.github/` → GitHub). If neither is present (e.g. a fresh repo with only a `PRD.md`), adopt **does not guess**: run interactively it asks; run non-interactively (an agent or CI) it exits without installing and asks you to pass `--platform gh|glab`. So on a bare repo, either set the remote first or pass `--platform`. The resolved value is written to the `Tracker:` line in `CLAUDE.md`, which the pipeline reads.
|
|
22
|
+
|
|
23
|
+
**Upstream issue escalation is opt-in.** By default the installed `CLAUDE.md` carries **no** instruction to file pattern-level problems against any catalog — no external repo slug is written into your repo (safe for commercial and private projects). Pass `--upstream` to include the bullet pointing at the catalog you adopted from, or `--upstream <owner/repo>` to point it at your own fork or internal catalog.
|
|
20
24
|
|
|
21
25
|
## 2. New project from a bare PRD.md — end to end
|
|
22
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-templates",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Catalog of multi-agent development architecture patterns - design write-ups plus drop-in scaffolding (agents, slash commands, workflows, templates).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Horace Hou",
|
|
@@ -154,6 +154,8 @@ Install steps and usage modes: [scaffold/INSTALL.md](scaffold/INSTALL.md). Front
|
|
|
154
154
|
| 2026-07-18 | **`/start-all` shipped** (catalog issue #20; maintainer-decided semantics: failed module blocks dependents, independent branches continue in `autonomous`, any non-CLEAR stops everything in `supervised`, no cost cap, resume via closed-issue filtering): deterministic DAG script (`milestone-dag.mjs`, ticket `blocked_by` as ground truth, cycles/dangling refs are hard errors) + `start-all` workflow composing `run-milestone` per module + `/start-all` command. 19 new E2E checks (DAG fixtures incl. cycle; module-policy scenarios via stubbed child workflows). Per-ticket stages unchanged. | Maintainer answers on issue #20, 2026-07-18. | Horace Hou (decisions) / Claude Fable 5 (build) |
|
|
155
155
|
| 2026-07-18 | **First field bug fixed** (catalog issue #21, reported from Ruihang2017/Groundwork through the upstream feedback channel): CRLF-rewritten workflow scripts are rejected by the Workflow tool on Windows. Fix: repo-wide `.gitattributes` (`eol=lf`), working tree renormalized (67 files), `adopt.mjs` installs all text as LF, E2E `\r` guards added. Recorded as the pattern's first true `[internal]` pitfall observation. | Field report (Horace Hou via Groundwork), 2026-07-18. | Horace Hou (report) / Claude Fable 5 (fix) |
|
|
156
156
|
| 2026-07-20 | **Doc/site command coverage mechanized** (catalog issue #35, maintainer direction: `/start-all` had shipped but never appeared in the npm README or the catalog site, so users could not discover it — surfacing commands must be impossible to forget). The command frontmatter is the single source of truth: `build-site.mjs` now renders a Commands section per pattern from it (site complete by construction), README carries the full command list, and an E2E coverage gate asserts every `scaffold/.claude/commands/*.md` name appears in BOTH the generated site and README with a non-empty description (plus a non-vacuous sentinel guard). Adding a command without documenting it now fails the merge gate. No pattern-behavior change. | Maintainer direction (Horace Hou), 2026-07-20. | Horace Hou (direction) / Claude Opus 4.8 (build) |
|
|
157
|
+
| 2026-07-20 | **Upstream issue escalation made opt-in** (catalog issue #40, maintainer decision): the CLAUDE.md snippet's "file pattern-level problems against the catalog" bullet — which hardcoded the maintainer's repo slug and a `gh issue create` instruction — is now wrapped in `<!-- upstream-escalation -->` markers and stripped by default. `adopt --upstream` opts in (bare = the catalog you adopted from; `--upstream <owner/repo>` retargets to a fork or internal catalog); the markers never leak into CLAUDE.md. Rationale: commercial and private adopters do not want an external repo slug or "file issues upstream" behavior written into their repo unasked. A neighboring line that named the product was also generalized to "the upstream pattern catalog". E2E: default adopt writes no catalog slug/marker/bullet; `--upstream` and `--upstream <repo>` include and retarget it. | Maintainer decision (Horace Hou), 2026-07-20. | Horace Hou (decision) / Claude Opus 4.8 (build) |
|
|
158
|
+
| 2026-07-20 | **adopt never guesses the platform** (catalog issue #38, maintainer decision, refining #34): the no-signal fallback no longer silently defaults to `gh`. Resolution order — `--platform`, origin host, repo-local signal (`.gitlab-ci.yml`/`.github/`); when none resolve, adopt prompts if interactive and otherwise exits without installing, asking for `--platform gh|glab`. This makes the agent-driven flow confirm the platform (adopt errors → agent asks the user → re-runs with `--platform`) before any infrastructure is built. E2E: no-signal non-TTY exits 1 and installs nothing; a repo-local signal still resolves without `--platform`. | Maintainer decision (Horace Hou), 2026-07-20. | Horace Hou (decision) / Claude Opus 4.8 (build) |
|
|
157
159
|
| 2026-07-20 | **Platform compatibility hardened** (catalog issue #34, maintainer question): `adopt.mjs` detection now uses multiple deterministic signals — origin host `gitlab`/`github` substring, then a repo-local signal (`.gitlab-ci.yml` → glab, existing `.github/` → gh), then default gh with a loud ambiguity note — so a self-hosted GitLab on a custom domain is no longer misread as GitHub. The resolved platform is recorded as a `Tracker:` line in the adopted CLAUDE.md, and `/start-milestone` · `/start-all` · `/nightly-issues` now read `platform` from that line instead of guessing (parallel to Operating mode / testCmd). E2E: self-hosted-GitLab detection, `Tracker:` line matches platform, ambiguity note. Runtime scripts' own `--platform`/autodetect unchanged. | Maintainer question (Horace Hou), 2026-07-20. | Horace Hou (question) / Claude Opus 4.8 (fix) |
|
|
158
160
|
| 2026-07-19 | **Field bug #4 fixed** (catalog issue #31, field report from Ruihang2017/Groundwork2): `milestone-dag.mjs` emitted each module's tickets in ID order instead of topologically by intra-module `blocked_by`, so a module whose declared order deviates from ID order dispatched a ticket before its blocker and failed at the Builder's precondition gate. Fix: a shared Kahn `topoSort` now orders both modules and, within each module, tickets by intra-module `blocked_by` (cross-module edges stay module-level; ID order as tiebreak; intra-module cycle fails loudly). E2E +7 (suite-dag D6 the 0502/0503 inversion, D7 intra-module cycle, D8 ID-order tiebreak). run-milestone dispatch loop unchanged. | Field report (Horace Hou via Groundwork2), 2026-07-18. | Horace Hou (report) / Claude Opus 4.8 (fix) |
|
|
159
161
|
| 2026-07-19 | **Pipeline tool surface explicitly pre-allowed** (catalog issue #30, maintainer direction): scaffold `settings.json` now enumerates allow rules for everything the stages legitimately run — deterministic scripts, git branch/commit/push/read set, `npm test`-family, `gh`/`glab` issue list/view/comment/edit|update|note/close/create — so a fully autonomous `/start-all` → deliver → nightly cycle triggers zero interactive approvals. Deliberately not pre-allowed (role discipline stays mechanical): `git merge`/`rebase`/`reset`/`clean`, `gh pr *`, and bare `git:*`/`gh:*`/`glab:*` wildcards — E2E asserts both directions so drift fails the gate. Risk accepted and recorded: `git push:*` includes force variants; `gh issue create:*` is broader than the publish-tickets discipline (nightly report issue needs it); prefix-deny of flags is structurally incomplete so not attempted. | Maintainer direction (Horace Hou), 2026-07-19. | Horace Hou (direction) / Claude Fable 5 (build) |
|
|
@@ -12,7 +12,7 @@ From a checkout of the catalog: `node scripts/adopt.mjs three-agent-architect-bu
|
|
|
12
12
|
2. Install the catalog's **universal templates** (shared by all patterns — source of truth is the catalog root, not this scaffold): `templates/ticket.template.md` → the target repo's `templates/`; the platform half of `templates/tracker/` → `.github/` (ISSUE_TEMPLATE/ + PULL_REQUEST_TEMPLATE.md) or `.gitlab/` (issue_templates/ + merge_request_templates/). Then fill the PR/MR template's **Constraint check** section with the target repo's CLAUDE.md non-negotiables. Hand-written issues and pipeline PRs now share one format that triage can convert and reviewers can verify.
|
|
13
13
|
3. The write-guard hook needs Node.js ≥ 18 on PATH. It denies main-session Edit/Write with a dispatch instruction; subagent writes pass. Override switch for human-approved out-of-pipeline edits: create `.claude/allow-main-writes`, delete it afterwards — and add that path to `.gitignore`.
|
|
14
14
|
4. The tracker steps (`publish-tickets.mjs`, `deliver-ticket.mjs`, `/verify-delivery`, the nightly sweep) need the platform CLI installed and authenticated: `gh` (GitHub) or `glab` (GitLab). The publish script autodetects the platform from the origin remote; override with `--platform gh|glab` (test doubles / non-PATH binaries: `GH_BIN` / `GLAB_BIN` env overrides). Delivery (merge `--no-ff` + push + verified issue close + deterministic DoD) is `deliver-ticket.mjs` — never an agent improvising git/tracker writes; `settings.json` carries explicit allow rules for the pipeline's whole tool surface (the three deterministic scripts, the enumerated git branch/commit/push set, `npm test`-family test commands, and the enumerated `gh`/`glab` issue commands) so unattended runs do not stall waiting for interactive approval (catalog issues #26, #30). Deliberately NOT pre-allowed: `git merge` / `rebase` / `reset` / `clean` and `gh pr *` — merging belongs to `deliver-ticket.mjs`. If your project's test command is not `npm test` / `npm run test` / `node --test`, add one more allow rule for it (e.g. `Bash(pytest:*)`).
|
|
15
|
-
5. Append the content of `claude-md-snippet.md` to the target repo's `CLAUDE.md`, and set the **Operating mode** line (`supervised` for a fresh adoption; `autonomous` is the target) and the **Tracker** line (`gh` for GitHub, `glab` for GitLab — the commands and workflows read the platform from this line). `adopt.mjs` sets Tracker automatically from the origin remote; set it by hand only on a manual install.
|
|
15
|
+
5. Append the content of `claude-md-snippet.md` to the target repo's `CLAUDE.md`, and set the **Operating mode** line (`supervised` for a fresh adoption; `autonomous` is the target) and the **Tracker** line (`gh` for GitHub, `glab` for GitLab — the commands and workflows read the platform from this line). `adopt.mjs` sets Tracker automatically from the origin remote; set it by hand only on a manual install. The snippet contains one **opt-in** bullet between `<!-- upstream-escalation -->` markers that tells agents to file pattern-level problems against the catalog — **delete that block (and its markers) unless you want it**, or repoint the `--repo` slug at your own catalog. `adopt.mjs` strips it by default; keep it only with `--upstream`.
|
|
16
16
|
6. Ensure the docs layout the pipeline assumes exists: `docs/PRD.md`, `docs/prd/<module>/README.md` (the sub-PRD — `/start-milestone` hard-requires it), `docs/prd/<module>/tickets/` (author tickets from `templates/ticket.template.md`), `docs/adr/`, and an empty `docs/plans/`.
|
|
17
17
|
7. Check the pattern entry's expiry (README metadata table). If expired, re-verify the model/effort table against current official docs before adopting — do not copy an expired recommendation into a new project.
|
|
18
18
|
|
|
@@ -35,5 +35,7 @@ Rules:
|
|
|
35
35
|
- Trivial/mechanical changes may skip the pipeline only with an explicit human OK.
|
|
36
36
|
- Ticket files are the issue-content source of truth. Issues are created only by `.claude/scripts/publish-tickets.mjs` (`[<id>]` title prefix = dedupe key); to change an issue body, edit the ticket file and republish. Issue state (close) moves via the deliver step / `/verify-delivery`, never by hand mid-pipeline.
|
|
37
37
|
- Agents own the whole test pyramid: the Builder writes and runs unit + integration tests (and E2E where the ticket's acceptance calls for it); the Reviewer re-runs the full suite independently; the deliver step re-runs it on the merged default branch. The human tests exactly once — the Gate 2 smoke test after the PRD's tasks are all done.
|
|
38
|
+
<!-- upstream-escalation:start -->
|
|
38
39
|
- **Pattern-level problems go upstream, not here.** If the pipeline itself misbehaves (a role boundary misfits, a model/effort pin looks stale, an orchestration bug), file an issue against the pattern catalog — `gh issue create --repo Ruihang2017/agent-templates` using its issue templates — so the catalog's own nightly sweep triages it. Project bugs stay in this repo's tracker.
|
|
39
|
-
|
|
40
|
+
<!-- upstream-escalation:end -->
|
|
41
|
+
- Model/effort per role are pinned in `.claude/agents/*.md`. Change them by updating the pattern entry in the upstream pattern catalog first (new as-of date + provenance entry), then syncing here — never by editing only this repo.
|
package/scripts/adopt.mjs
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
20
20
|
import { execFileSync } from 'node:child_process'
|
|
21
|
+
import { createInterface } from 'node:readline/promises'
|
|
21
22
|
import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
22
23
|
import { fileURLToPath } from 'node:url'
|
|
23
24
|
|
|
@@ -31,10 +32,27 @@ if (pIx !== -1 && (!PLATFORM || PLATFORM.startsWith('--'))) {
|
|
|
31
32
|
console.error('missing or invalid --platform value (expected gh or glab)')
|
|
32
33
|
process.exit(1)
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
+
|
|
36
|
+
// --upstream [owner/repo]: opt IN to the "file pattern-level problems against the
|
|
37
|
+
// catalog" bullet in CLAUDE.md (off by default — the bullet names a specific repo and
|
|
38
|
+
// tells agents to file issues there, which commercial/private adopters do not want, and
|
|
39
|
+
// the repo slug should never land in a CLAUDE.md unasked; issue #40). Bare --upstream
|
|
40
|
+
// targets the catalog this pattern came from; --upstream <repo> points elsewhere.
|
|
41
|
+
const CATALOG_REPO = 'Ruihang2017/agent-templates'
|
|
42
|
+
const uIx = argv.indexOf('--upstream')
|
|
43
|
+
const UPSTREAM = uIx !== -1
|
|
44
|
+
let UPSTREAM_REPO = CATALOG_REPO
|
|
45
|
+
if (uIx !== -1 && argv[uIx + 1] && !argv[uIx + 1].startsWith('--')) UPSTREAM_REPO = argv[uIx + 1]
|
|
46
|
+
|
|
47
|
+
// positional args = everything that isn't a flag or a flag's consumed value
|
|
48
|
+
const consumed = new Set()
|
|
49
|
+
for (const ix of [pIx, uIx]) {
|
|
50
|
+
if (ix !== -1 && argv[ix + 1] && !argv[ix + 1].startsWith('--')) consumed.add(ix + 1)
|
|
51
|
+
}
|
|
52
|
+
const positional = argv.filter((a, i) => !a.startsWith('--') && !consumed.has(i))
|
|
35
53
|
const [pattern, targetArg] = positional
|
|
36
54
|
if (!pattern || !targetArg) {
|
|
37
|
-
console.error('usage: node scripts/adopt.mjs <pattern-name> <target-dir> [--platform gh|glab] [--force]')
|
|
55
|
+
console.error('usage: node scripts/adopt.mjs <pattern-name> <target-dir> [--platform gh|glab] [--upstream [owner/repo]] [--force]')
|
|
38
56
|
process.exit(1)
|
|
39
57
|
}
|
|
40
58
|
|
|
@@ -72,7 +90,35 @@ if (!PLATFORM) {
|
|
|
72
90
|
else if (/github/i.test(host)) { PLATFORM = 'gh'; console.log(`platform: gh (from origin host '${host}'; override with --platform)`) }
|
|
73
91
|
else if (existsSync(join(target, '.gitlab-ci.yml'))) { PLATFORM = 'glab'; console.log(`platform: glab (from .gitlab-ci.yml${inconclusive}; override with --platform)`) }
|
|
74
92
|
else if (existsSync(join(target, '.github'))) { PLATFORM = 'gh'; console.log(`platform: gh (from existing .github/${inconclusive}; override with --platform)`) }
|
|
75
|
-
else {
|
|
93
|
+
else {
|
|
94
|
+
// No signal: never guess — a wrong guess installs the wrong tracker config and a
|
|
95
|
+
// wrong Tracker line (issue #38). Ask when interactive; otherwise stop and install
|
|
96
|
+
// nothing so the agent/CI can re-run with an explicit --platform.
|
|
97
|
+
const reason = host ? `could not classify origin host '${host}'` : 'no git remote, and no .gitlab-ci.yml or .github/ to infer from'
|
|
98
|
+
if (process.stdin.isTTY) {
|
|
99
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
100
|
+
// Ctrl+D / closed stdin before an answer: abort cleanly (nothing installed)
|
|
101
|
+
// instead of leaving the top-level await unsettled.
|
|
102
|
+
rl.on('close', () => {
|
|
103
|
+
if (!PLATFORM) {
|
|
104
|
+
console.error('\nplatform: undetermined (input closed). Re-run with --platform gh|glab (nothing was installed).')
|
|
105
|
+
process.exit(1)
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
try {
|
|
109
|
+
console.log(`Cannot determine the tracker platform (${reason}).`)
|
|
110
|
+
for (;;) {
|
|
111
|
+
const a = (await rl.question('Which tracker is this repo on? [gh/glab] ')).trim().toLowerCase()
|
|
112
|
+
if (a === 'gh' || a === 'github') { PLATFORM = 'gh'; break }
|
|
113
|
+
if (a === 'glab' || a === 'gitlab') { PLATFORM = 'glab'; break }
|
|
114
|
+
console.log("please answer 'gh' or 'glab'")
|
|
115
|
+
}
|
|
116
|
+
} finally { rl.close() }
|
|
117
|
+
} else {
|
|
118
|
+
console.error(`platform: undetermined — ${reason}. Re-run with --platform gh|glab (nothing was installed).`)
|
|
119
|
+
process.exit(1)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
76
122
|
}
|
|
77
123
|
if (PLATFORM !== 'gh' && PLATFORM !== 'glab') {
|
|
78
124
|
console.error(`unknown platform: ${PLATFORM} (expected gh or glab)`)
|
|
@@ -167,8 +213,23 @@ if (existsSync(rootPrd) && !existsSync(docsPrd)) {
|
|
|
167
213
|
// 6. CLAUDE.md: create from the snippet, or append it once (marker-checked, never duplicated).
|
|
168
214
|
// The snippet defaults its Tracker line to `gh`; rewrite it to the resolved platform so the
|
|
169
215
|
// pipeline reads the correct tracker from CLAUDE.md instead of re-guessing each run (issue #34).
|
|
170
|
-
|
|
216
|
+
// Normalize CRLF up front: the marker/Tracker matching below needs literal \n, and a
|
|
217
|
+
// catalog checkout under git autocrlf can carry \r\n — don't let that silently no-op the
|
|
218
|
+
// strip and leak the block (same posture as copyFile; catalog issues #21/#23/#40).
|
|
219
|
+
let snippet = readFileSync(join(scaffold, 'claude-md-snippet.md'), 'utf8')
|
|
220
|
+
.replace(/\r\n/g, '\n')
|
|
171
221
|
.replace('**Tracker: `gh`**', `**Tracker: \`${PLATFORM}\`**`)
|
|
222
|
+
// Upstream-escalation bullet is opt-in (issue #40): keep it only with --upstream (pointing
|
|
223
|
+
// at the chosen catalog repo), otherwise strip the whole marked block so no catalog repo
|
|
224
|
+
// slug or "file issues upstream" instruction lands in the adopted CLAUDE.md.
|
|
225
|
+
const UP_RE = /\n<!-- upstream-escalation:start -->\n([\s\S]*?)\n<!-- upstream-escalation:end -->/
|
|
226
|
+
if (UPSTREAM) {
|
|
227
|
+
snippet = snippet.replace(UP_RE, (_, bullet) => '\n' + bullet.split(CATALOG_REPO).join(UPSTREAM_REPO))
|
|
228
|
+
console.log(`upstream escalation: on (issues -> ${UPSTREAM_REPO})`)
|
|
229
|
+
} else {
|
|
230
|
+
snippet = snippet.replace(UP_RE, '')
|
|
231
|
+
console.log('upstream escalation: off (enable with --upstream [owner/repo])')
|
|
232
|
+
}
|
|
172
233
|
const MARKER = '## Delivery pipeline — three-agent Architect / Builder / Reviewer'
|
|
173
234
|
const claudeMd = join(target, 'CLAUDE.md')
|
|
174
235
|
if (!existsSync(claudeMd)) {
|