agent-templates 0.1.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.
Files changed (37) hide show
  1. package/ADOPTING.md +57 -0
  2. package/CLAUDE.md +125 -0
  3. package/LICENSE +21 -0
  4. package/README.md +32 -0
  5. package/package.json +32 -0
  6. package/patterns/three-agent-architect-builder-reviewer/README.md +153 -0
  7. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/architect.md +31 -0
  8. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/builder.md +25 -0
  9. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/reviewer.md +33 -0
  10. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/triage.md +31 -0
  11. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/breakdown-prd.md +18 -0
  12. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/build-ticket.md +12 -0
  13. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/nightly-issues.md +14 -0
  14. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/plan-ticket.md +10 -0
  15. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/review-ticket.md +14 -0
  16. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/start-milestone.md +15 -0
  17. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/verify-delivery.md +20 -0
  18. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/hooks/guard-main-session-writes.mjs +53 -0
  19. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/scripts/publish-tickets.mjs +263 -0
  20. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/settings.json +15 -0
  21. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/workflows/nightly-issues.js +139 -0
  22. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/workflows/run-milestone.js +223 -0
  23. package/patterns/three-agent-architect-builder-reviewer/scaffold/INSTALL.md +65 -0
  24. package/patterns/three-agent-architect-builder-reviewer/scaffold/claude-md-snippet.md +37 -0
  25. package/scripts/adopt.mjs +177 -0
  26. package/scripts/build-site.mjs +264 -0
  27. package/scripts/cli.mjs +52 -0
  28. package/templates/pattern-README.template.md +65 -0
  29. package/templates/ticket.template.md +104 -0
  30. package/templates/tracker/github/ISSUE_TEMPLATE/bug-report.md +28 -0
  31. package/templates/tracker/github/ISSUE_TEMPLATE/decision-record.md +25 -0
  32. package/templates/tracker/github/ISSUE_TEMPLATE/task.md +36 -0
  33. package/templates/tracker/github/PULL_REQUEST_TEMPLATE.md +54 -0
  34. package/templates/tracker/gitlab/issue_templates/bug-report.md +23 -0
  35. package/templates/tracker/gitlab/issue_templates/decision-record.md +20 -0
  36. package/templates/tracker/gitlab/issue_templates/task.md +31 -0
  37. package/templates/tracker/gitlab/merge_request_templates/default.md +55 -0
@@ -0,0 +1,223 @@
1
+ export const meta = {
2
+ name: 'run-milestone',
3
+ description: 'Autonomous milestone runner: each ticket flows architect -> builder -> reviewer (bounce-capped in code) -> deliver',
4
+ }
5
+
6
+ // Deterministic orchestrator for the three-agent pattern. Control flow that used to be
7
+ // prose ("max 2 bounces", "never merge without CLEAR") is enforced HERE, in code.
8
+ //
9
+ // args:
10
+ // {
11
+ // tickets: [{ id: 'FND-01', path: 'docs/prd/01-foo/tickets/FND-01-x.md', issue: 12 }],
12
+ // mode: 'supervised' | 'autonomous',
13
+ // defaultBranch: 'main', // optional, default 'main'
14
+ // maxBounces: 2, // optional, default 2
15
+ // continueOnFailure: false, // optional; default fail-fast (tickets may depend on earlier ones)
16
+ // platform: 'gh' | 'glab' // tracker CLI for the deliver step, default 'gh'
17
+ // }
18
+ //
19
+ // Guarantees encoded below (each one exists because prose alone failed before):
20
+ // - reviewer prompts carry ONLY artifact refs (ticket path, computed plan path, branch name)
21
+ // - a reviewer infrastructure failure never consumes the bounce budget or dispatches a fix
22
+ // - nothing counts as delivered unless merged AND issueClosed AND dodPassed are all true
23
+ // - supervised mode STOPS the run after each CLEAR (later tickets may depend on the merge);
24
+ // re-running /start-milestone continues — already-closed issues are filtered out upstream
25
+
26
+ const cfg = Object.assign(
27
+ { maxBounces: 2, continueOnFailure: false, defaultBranch: 'main', platform: 'gh' },
28
+ args
29
+ )
30
+ if (!Array.isArray(cfg.tickets) || cfg.tickets.length === 0) {
31
+ throw new Error('args.tickets must be a non-empty array of {id, path, issue}')
32
+ }
33
+ for (const t of cfg.tickets) {
34
+ if (!t || typeof t.id !== 'string' || !t.id || typeof t.path !== 'string' || !t.path) {
35
+ throw new Error('every ticket needs a non-empty string id and path; got: ' + JSON.stringify(t))
36
+ }
37
+ }
38
+ if (cfg.mode !== 'supervised' && cfg.mode !== 'autonomous') {
39
+ throw new Error("args.mode must be 'supervised' or 'autonomous'")
40
+ }
41
+ if (!Number.isInteger(cfg.maxBounces) || cfg.maxBounces < 0) {
42
+ throw new Error('args.maxBounces must be an integer >= 0')
43
+ }
44
+ if (typeof cfg.defaultBranch !== 'string' || !cfg.defaultBranch) throw new Error('args.defaultBranch must be a non-empty string')
45
+ if (cfg.platform !== 'gh' && cfg.platform !== 'glab') throw new Error("args.platform must be 'gh' or 'glab'")
46
+
47
+ const PLAN = {
48
+ type: 'object',
49
+ properties: { planPath: { type: 'string' }, summary: { type: 'string' } },
50
+ required: ['planPath', 'summary'],
51
+ }
52
+ const BUILD = {
53
+ type: 'object',
54
+ properties: {
55
+ branch: { type: 'string' },
56
+ testsPassed: { type: 'boolean' },
57
+ testOutput: { type: 'string' },
58
+ deviations: { type: 'string' },
59
+ },
60
+ required: ['branch', 'testsPassed', 'testOutput'],
61
+ }
62
+ const VERDICT = {
63
+ type: 'object',
64
+ properties: {
65
+ verdict: { enum: ['CLEAR', 'BOUNCE'] },
66
+ checkedNote: { type: 'string' },
67
+ findings: {
68
+ type: 'array',
69
+ items: {
70
+ type: 'object',
71
+ properties: {
72
+ file: { type: 'string' },
73
+ line: { type: 'integer' },
74
+ scenario: { type: 'string' },
75
+ severity: { enum: ['blocker', 'major', 'minor'] },
76
+ },
77
+ required: ['file', 'scenario', 'severity'],
78
+ },
79
+ },
80
+ },
81
+ required: ['verdict'],
82
+ }
83
+ const DELIVERY = {
84
+ type: 'object',
85
+ properties: {
86
+ merged: { type: 'boolean' },
87
+ issueClosed: { type: 'boolean' },
88
+ dodPassed: { type: 'boolean' },
89
+ notes: { type: 'string' },
90
+ },
91
+ required: ['merged', 'issueClosed', 'dodPassed'],
92
+ }
93
+
94
+ const normalizePath = function (p) {
95
+ return String(p || '').replace(/\\/g, '/').replace(/^\.\//, '').trim()
96
+ }
97
+
98
+ const results = []
99
+ let stopRun = false
100
+
101
+ for (const t of cfg.tickets) {
102
+ if (stopRun) break
103
+ const P = 'T:' + t.id
104
+ const branch = 'ticket/' + t.id
105
+ const planPath = 'docs/plans/' + t.id + '.md' // computed in code; agent-returned paths are verified, never trusted
106
+
107
+ log('[' + t.id + '] architect: planning')
108
+ const plan = await agent(
109
+ 'You are running as the Architect stage of the three-agent pattern. Ticket file: ' + t.path +
110
+ '. Produce the implementation plan per your role definition and write it to EXACTLY ' + planPath +
111
+ '. Return planPath (must be ' + planPath + ') and a one-paragraph summary.',
112
+ { agentType: 'architect', label: 'plan:' + t.id, phase: P, schema: PLAN }
113
+ )
114
+ if (!plan || normalizePath(plan.planPath) !== planPath) {
115
+ results.push({ id: t.id, status: 'failed', stage: 'architect', detail: plan ? 'plan written to unexpected path: ' + plan.planPath : 'architect agent returned nothing' })
116
+ if (!cfg.continueOnFailure) break
117
+ continue
118
+ }
119
+
120
+ log('[' + t.id + '] builder: implementing on ' + branch)
121
+ let build = await agent(
122
+ 'Builder stage. Ticket: ' + t.path + '. Plan: ' + planPath + '. Create branch ' + branch +
123
+ ' from ' + cfg.defaultBranch + ', implement the plan there, commit, run the tests. Do NOT merge and do NOT touch the tracker. ' +
124
+ 'Return branch (must be ' + branch + '), testsPassed, testOutput (paste real output), deviations.',
125
+ { agentType: 'builder', label: 'build:' + t.id, phase: P, schema: BUILD }
126
+ )
127
+ const buildBad = function (b) { return !b || !b.testsPassed || String(b.branch).trim() !== branch }
128
+ if (buildBad(build)) {
129
+ results.push({
130
+ id: t.id, status: 'failed', stage: 'builder',
131
+ detail: !build ? 'builder agent returned nothing' : (String(build.branch).trim() !== branch ? 'worked on wrong branch: ' + build.branch : build.testOutput),
132
+ })
133
+ if (!cfg.continueOnFailure) break
134
+ continue
135
+ }
136
+
137
+ // Review loop — the bounce cap lives here, in code, not in prose.
138
+ const reviewOnce = function (tag) {
139
+ return agent(
140
+ 'Reviewer stage. Inputs (artifact refs only): ticket ' + t.path + ', plan ' + planPath +
141
+ ', diff = branch ' + branch + ' vs ' + cfg.defaultBranch + '. Review per your role definition; ' +
142
+ 'run the tests yourself — no test results are provided on purpose. Return verdict CLEAR or BOUNCE with findings ' +
143
+ '(a BOUNCE with zero findings is invalid).',
144
+ { agentType: 'reviewer', label: 'review:' + t.id + '#' + tag, phase: P, schema: VERDICT }
145
+ )
146
+ }
147
+ // A null/invalid reviewer result is an infrastructure failure, not a code finding:
148
+ // retry once (not counted against the bounce budget), then escalate — never sent to the builder.
149
+ const reviewValid = function (v) {
150
+ if (!v) return false
151
+ if (v.verdict === 'BOUNCE' && (!v.findings || v.findings.length === 0)) return false
152
+ return true
153
+ }
154
+
155
+ let bounces = 0
156
+ let verdict = await reviewOnce('0')
157
+ if (!reviewValid(verdict)) { log('[' + t.id + '] reviewer returned no usable verdict — retrying once'); verdict = await reviewOnce('0-retry') }
158
+ let reviewerBroken = !reviewValid(verdict)
159
+ let fixBroken = false
160
+
161
+ while (!reviewerBroken && verdict.verdict === 'BOUNCE' && bounces < cfg.maxBounces) {
162
+ bounces += 1
163
+ log('[' + t.id + '] bounce ' + bounces + '/' + cfg.maxBounces + ': back to builder with ' + verdict.findings.length + ' finding(s)')
164
+ build = await agent(
165
+ 'Builder stage, bounce fix. Ticket: ' + t.path + '. Plan: ' + planPath + '. Stay on branch ' + branch +
166
+ ' — do NOT merge and do NOT touch the tracker. Reviewer findings — address ALL of them and add regression tests: ' +
167
+ JSON.stringify(verdict.findings) + '. Run the tests. Return branch (must be ' + branch + '), testsPassed, testOutput, deviations.',
168
+ { agentType: 'builder', label: 'fix:' + t.id + '#' + bounces, phase: P, schema: BUILD }
169
+ )
170
+ if (buildBad(build)) { fixBroken = true; break }
171
+ verdict = await reviewOnce(String(bounces))
172
+ if (!reviewValid(verdict)) { log('[' + t.id + '] reviewer returned no usable verdict — retrying once'); verdict = await reviewOnce(bounces + '-retry') }
173
+ reviewerBroken = !reviewValid(verdict)
174
+ }
175
+
176
+ if (reviewerBroken || fixBroken || verdict.verdict !== 'CLEAR') {
177
+ const stage = reviewerBroken ? 'reviewer-failed' : (fixBroken ? 'bounce-fix-build' : 'review')
178
+ results.push({
179
+ id: t.id, status: 'escalated', stage: stage, bounces: bounces,
180
+ findings: reviewValid(verdict) ? (verdict.findings || []) : [],
181
+ detail: fixBroken ? (!build ? 'fix builder returned nothing' : (String(build.branch).trim() !== branch ? 'fix worked on wrong branch: ' + build.branch : build.testOutput)) : (reviewerBroken ? 'reviewer produced no usable verdict after one retry' : 'bounce cap exhausted'),
182
+ })
183
+ log('[' + t.id + '] escalated to a human (stage: ' + stage + ', after ' + bounces + ' bounce(s))')
184
+ if (!cfg.continueOnFailure) break
185
+ continue
186
+ }
187
+
188
+ if (cfg.mode === 'supervised') {
189
+ results.push({ id: t.id, status: 'awaiting-human-merge', branch: branch, bounces: bounces, note: verdict.checkedNote || '' })
190
+ log('[' + t.id + '] CLEAR — supervised mode: stopping the run for the human merge. Re-run /start-milestone after merging (closed issues are skipped).')
191
+ stopRun = true
192
+ continue
193
+ }
194
+
195
+ log('[' + t.id + '] deliver: merge + close issue + DoD')
196
+ const delivery = await agent(
197
+ 'Delivery step (autonomous mode — pre-authorized by the human Gate 1 start signal). ' +
198
+ 'Merge branch ' + branch + ' into ' + cfg.defaultBranch + ' with --no-ff and push. ' +
199
+ 'Close tracker issue ' + (t.issue ? '#' + t.issue : 'for ticket ' + t.id + ' (find it by its "[' + t.id + ']" title prefix)') +
200
+ ' via ' + cfg.platform + ' and verify it is ACTUALLY closed afterwards. ' +
201
+ 'Then run the Definition-of-Done checks from .claude/commands/verify-delivery.md for ticket ' + t.id +
202
+ ' (plan exists; tests green on the merged ' + cfg.defaultBranch + ' — run them yourself; CLEAR verdict recorded; merged to ' + cfg.defaultBranch + '; issue closed; writeback done). ' +
203
+ 'Never report an item you did not check. Return merged, issueClosed, dodPassed, notes.',
204
+ { label: 'deliver:' + t.id, phase: P, schema: DELIVERY }
205
+ )
206
+ // Delivered requires ALL THREE flags — a hallucinated dodPassed alone must not count.
207
+ if (!delivery || !(delivery.merged && delivery.issueClosed && delivery.dodPassed)) {
208
+ const missing = !delivery ? 'delivery agent returned nothing' : ['merged', 'issueClosed', 'dodPassed'].filter(function (k) { return !delivery[k] }).join(', ') + ' = false'
209
+ results.push({ id: t.id, status: 'delivery-incomplete', detail: missing + (delivery && delivery.notes ? ' — ' + delivery.notes : '') })
210
+ if (!cfg.continueOnFailure) break
211
+ continue
212
+ }
213
+ results.push({ id: t.id, status: 'delivered', bounces: bounces })
214
+ }
215
+
216
+ const throughPipeline = results.filter(function (r) { return r.status === 'delivered' || r.status === 'awaiting-human-merge' }).length
217
+ log('milestone run finished: ' + throughPipeline + '/' + cfg.tickets.length + ' tickets through the pipeline')
218
+
219
+ return {
220
+ mode: cfg.mode,
221
+ results: results,
222
+ notStarted: cfg.tickets.length - results.length, // > 0 means the run stopped early (fail-fast or supervised pause)
223
+ }
@@ -0,0 +1,65 @@
1
+ # Installing the three-agent scaffold
2
+
3
+ Source pattern: `agent-templates/patterns/three-agent-architect-builder-reviewer` — read its [README](../README.md) (especially the expiry trigger) before installing.
4
+
5
+ ## Quickstart
6
+
7
+ From a checkout of the catalog: `node scripts/adopt.mjs three-agent-architect-builder-reviewer <target-dir>` performs steps 1–6 below mechanically (idempotent; `--platform gh|glab`), then prints the next steps. Full walkthrough incl. the bare-PRD.md scenario: the catalog's [ADOPTING.md](../../../ADOPTING.md). The manual steps:
8
+
9
+ ## Steps
10
+
11
+ 1. Copy `.claude/` from this scaffold into the target repo root. If the target already has `.claude/settings.json`, merge the `hooks.PreToolUse` entry instead of overwriting.
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
+ 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
+ 4. The tracker steps (`publish-tickets.mjs`, the deliver stage, `/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).
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).
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
+ 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
+
19
+ ## Usage modes
20
+
21
+ - **Mode A — one orchestrator session (default):** run `/plan-ticket` → `/build-ticket` → `/review-ticket` → `/verify-delivery` from a single main session. Each stage executes in its own subagent, so stage contexts stay isolated. The orchestrator passes only artifacts between stages — ticket path, plan path, diff ref — never transcripts or agent self-assessments, and it never does stage work itself (see "Orchestrator discipline" in `claude-md-snippet.md`; role leakage is a recorded failure mode).
22
+ - **Mode B — three human-run sessions:** open a fresh Claude Code session per stage. Strongest isolation; use when the orchestrator session itself has grown long or has seen implementation detail.
23
+ - **Milestone mode (the target operating model):** `/start-milestone <module> [supervised|autonomous]` — Gate 1 in one action: verify inputs, publish tickets as issues, then run all tickets through `.claude/workflows/run-milestone.js`. Each stage still runs in its own subagent; ordering, the bounce cap, and merge policy are code, not prompts. The human returns at Gate 2 (smoke test) or on escalation.
24
+
25
+ ## Nightly sweep (unattended)
26
+
27
+ - **Entry:** `claude -p "/nightly-issues"` — slash commands expand in headless `-p` mode. `[official]`
28
+ - **Permissions:** pre-approve exactly what the pipeline needs in the target repo's `.claude/settings.json` (`permissions.allow`, e.g. `Bash(git:*)`, `Bash(gh:*)` or `Bash(glab:*)`, `Bash(node:*)`, `Bash(npm:*)`), then run with `--permission-mode dontAsk` — the documented CI recommendation: pre-approved tools run, everything else is auto-denied instead of blocking. Subagents run in `acceptEdits` mode (their file edits are auto-approved). Avoid `bypassPermissions` outside isolated containers (documented warning). `[official]`
29
+ - **Scheduling (Windows, primary):** Task Scheduler — runs whenever the machine is on at the trigger time:
30
+
31
+ ```
32
+ schtasks /create /tn "nightly-issues" /sc daily /st 02:00 ^
33
+ /tr "cmd /c cd /d C:\path\to\repo && claude -p \"/nightly-issues\" --permission-mode dontAsk >> .claude\nightly.log 2>&1"
34
+ ```
35
+
36
+ macOS/Linux: launchd / cron equivalents.
37
+ - **Why not Claude Code's native cron** (`/loop`, Cron tools): documented constraints — the session must stay open, recurring tasks expire after 7 days, and firings jitter up to 30 minutes — the wrong shape for "machine on, no session open". For no-local-machine automation the docs point to Routines (Anthropic infrastructure) or CI schedules. `[official]`
38
+ - **Morning email:** watch the repo / enable tracker notifications. The sweep posts per-issue outcome comments, labels (`triage:invalid` = flagged invalid, never auto-closed · `nightly:escalated` = won't retry until a human clears it · `needs-human`), closes delivered issues, and files a `Nightly report YYYY-MM-DD` issue — the tracker's own notification email is the delivery mechanism, no SMTP to configure.
39
+
40
+ ## Config-key verification record
41
+
42
+ `model:` and `effort:` in `.claude/agents/*.md`, and command frontmatter (`description`, `argument-hint`, `$ARGUMENTS`/`$N` substitution), verified against the official Claude Code docs on **2026-07-17**:
43
+
44
+ - <https://code.claude.com/docs/en/sub-agents.md> — agent frontmatter keys incl. `model` (full IDs and aliases; omitted = inherit) and `effort` (`low`/`medium`/`high`/`xhigh`/`max`); omitted `tools` inherits all tools.
45
+ - <https://code.claude.com/docs/en/skills.md> — command/skill frontmatter incl. `model`, `effort`, `argument-hint`; argument substitution via `$ARGUMENTS` and `$N` (0-based).
46
+ - <https://code.claude.com/docs/en/model-config.md> — effort levels incl. `max`; note the `effortLevel` *setting* accepts `low`–`xhigh` only (`max` is session-only), while agent/command frontmatter `effort` accepts `max`.
47
+
48
+ Hook mechanism (the main-session write guard), verified **2026-07-17**:
49
+
50
+ - <https://code.claude.com/docs/en/hooks.md> — PreToolUse settings schema (regex `matcher`, `type: command`); hooks fire for subagent tool calls too; the hook input carries `agent_id`/`agent_type` **only** when the call comes from a subagent, so their absence identifies the main session; deny via stdout JSON `hookSpecificOutput.permissionDecision: "deny"` (the reason string is fed back to the model), or exit code 2.
51
+ - <https://code.claude.com/docs/en/permissions.md> — `permissions.deny` rules apply to subagents as well (subagents inherit the parent's permission stack); **no documented main-session-only permission scope exists**, which is why the guard is a hook rather than a deny rule.
52
+
53
+ Workflow tool + `ultracode` (for the milestone runner), verified **2026-07-17**:
54
+
55
+ - <https://code.claude.com/docs/en/workflows> — the Workflow tool (deterministic multi-agent scripts: `export const meta`, `agent()` / `parallel()` / `pipeline()`) is publicly documented; `.claude/workflows/` is the documented project location for saved workflow scripts (also <https://code.claude.com/docs/en/claude-directory>).
56
+ - <https://code.claude.com/docs/en/model-config> — "Ultracode is a Claude Code setting rather than a model effort level": it applies `xhigh` effort plus automatic workflow orchestration. Persistable effort levels are `low`–`xhigh`; `max` and `ultracode` are session-only.
57
+
58
+ Headless + scheduling facts (for the nightly sweep), verified **2026-07-17**:
59
+
60
+ - <https://code.claude.com/docs/en/headless.md> — `claude -p "<prompt>"`; slash commands expand in `-p`; `--output-format`, `--max-turns`.
61
+ - <https://code.claude.com/docs/en/permission-modes.md> and <https://code.claude.com/docs/en/permissions.md> — `--permission-mode dontAsk` is the documented CI recommendation (pre-approved tools only, rest auto-denied); `acceptEdits`; `bypassPermissions` only for isolated environments; `--allowedTools`; settings `permissions.allow`; subagents run in `acceptEdits`.
62
+ - <https://code.claude.com/docs/en/workflows.md> — workflows are available in headless `claude -p`.
63
+ - <https://code.claude.com/docs/en/scheduled-tasks.md> and <https://code.claude.com/docs/en/routines.md> — native `/loop`/Cron tools require an open session, recurring tasks expire after 7 days, jitter up to 30 min; Routines run on Anthropic infrastructure.
64
+
65
+ Re-verify these keys when Claude Code major-versions, or when this record is more than 6 months old.
@@ -0,0 +1,37 @@
1
+ <!-- Append this block to the target repo's CLAUDE.md.
2
+ Source pattern: agent-templates/patterns/three-agent-architect-builder-reviewer (as of 2026-07-17). -->
3
+
4
+ ## Delivery pipeline — three-agent Architect / Builder / Reviewer
5
+
6
+ **Operating mode: `supervised`** <!-- switch to `autonomous` once the pattern holds on this repo -->
7
+
8
+ - `supervised` — a human confirms each merge and each tracker close; issue **creation** is authorized by the `/start-milestone` sign-off itself. The milestone runner stops after each CLEAR verdict for the human merge — re-run `/start-milestone` to continue (closed issues are skipped). Use for a fresh adoption.
9
+ - `autonomous` (target) — humans decide at exactly two gates: Gate 1, sign-off of master PRD → sub-PRDs → tickets before the pipeline starts; Gate 2, a smoke test of the delivered milestone. Between them the pipeline self-drives: merge on CLEAR, `/verify-delivery` repairs gaps (including closing the issue) automatically. A human is pulled in only on the exception path — 2 bounce cycles without convergence, a failed build, or an unrepairable verify-delivery item.
10
+
11
+ Every non-trivial ticket flows through three stages; no agent judges its own work.
12
+
13
+ - **`/breakdown-prd [notes]`** — pre-Gate-1: the Architect decomposes `docs/PRD.md` into a breakdown plan + sub-PRDs + template-compliant tickets (disjoint file-scopes, dependency DAG), then stops for human review.
14
+ - **`/plan-ticket <ticket>`** — Architect (`claude-sonnet-5` @ `xhigh`) reads the ticket + codebase → implementation plan at `docs/plans/<ticket-id>.md`. Writes no production code.
15
+ - **`/build-ticket <ticket>`** — Builder (`claude-opus-4-8` @ `xhigh`) implements the plan, runs tests until green, records deviations. Never merges or self-clears.
16
+ - **`/review-ticket <ticket>`** — Reviewer (`claude-fable-5` @ `max`) in a **fresh context**, deliberately a different model tier from the Builder. Focus: edge cases, concurrency, security-sensitive paths. Verdict **CLEAR** or **BOUNCE**; merge requires CLEAR.
17
+ - **`/verify-delivery <ticket>`** — post-merge Definition-of-Done check: plan on disk · tests green · CLEAR verdict · MR merged into the default branch · **tracker issue closed** · writeback done. Run after **every** merge; tracker auto-close side effects are never trusted blindly.
18
+ - **`/start-milestone <module> [mode]`** — the Gate 1 start signal: verify sub-PRD + tickets, publish tickets as tracker issues (deterministic idempotent script — agents never hand-create issues), then run every ticket through the deterministic `run-milestone` workflow (stage order, bounce cap, merge policy enforced in code).
19
+ - **`/nightly-issues [max]`** — unattended sweep (OS-scheduled, `claude -p`): triage open issues → auto-fix fixable ones through the pipeline → per-issue comments + labels + a `Nightly report <date>` issue for the morning read. Invalid issues are labeled `triage:invalid`, never auto-closed. Setup: scaffold INSTALL.md § Nightly sweep.
20
+
21
+ Orchestrator discipline (hard rules for the main session):
22
+
23
+ - The main session **orchestrates only**: it invokes the stage commands and relays artifacts (ticket path, plan path, diff ref). It never plans, implements, or reviews a ticket inline — that work belongs to the stage subagents, whatever the size of the change.
24
+ - If a stage subagent fails or is unavailable, report the failure and stop. **Never absorb its role.**
25
+ - This is **mechanically enforced**: a PreToolUse guard (`.claude/hooks/guard-main-session-writes.mjs`) denies Edit/Write in the main session; subagent calls pass. For a human-approved out-of-pipeline edit, create `.claude/allow-main-writes` (git-ignored) and delete it afterwards.
26
+ - Known guard boundary: Bash is not covered. Bash in the main session is for orchestration only (git, tracker CLI, running commands) — never for writing files (`echo >`, `git apply`, heredocs). The guard stops reflexive edits, not determined bypasses; this rule covers the rest.
27
+ - A ticket is done only when `/verify-delivery` passes all items — "the MR is merged" is not done (known failure: MRs merged with issues left open, observed 2026-07-17).
28
+
29
+ Rules:
30
+
31
+ - The Reviewer never runs in the Builder's session and never edits code; it receives only the ticket, the plan, and the diff ref — never transcripts.
32
+ - Maximum 2 bounce cycles, then escalate to a human.
33
+ - Trivial/mechanical changes may skip the pipeline only with an explicit human OK.
34
+ - 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.
35
+ - 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.
36
+ - **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.
37
+ - Model/effort per role are pinned in `.claude/agents/*.md`. Change them by updating the pattern entry in agent-templates first (new as-of date + provenance entry), then syncing here — never by editing only this repo.
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ // adopt.mjs — one-command pattern installer for new and existing projects.
3
+ //
4
+ // Usage (run from a checkout of the agent-templates catalog):
5
+ // node scripts/adopt.mjs <pattern-name> <target-dir> [--platform gh|glab] [--force]
6
+ //
7
+ // Installs into <target-dir>:
8
+ // .claude/ from the pattern's scaffold (per-file; existing files skipped)
9
+ // templates/ticket.template.md the universal ticket format
10
+ // .github/ or .gitlab/ universal tracker templates (issues + PR/MR) for the platform
11
+ // docs/PRD.md copied from a root PRD.md if present and docs/PRD.md is absent
12
+ // docs/prd/ docs/adr/ docs/plans/ the docs skeleton the pipeline assumes
13
+ // CLAUDE.md created from the snippet, or snippet appended once (marker-checked)
14
+ //
15
+ // Idempotent: re-running skips everything that exists (--force overwrites files, never
16
+ // re-appends the snippet). Exit 0 = installed/verified; exit 1 = bad invocation.
17
+
18
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
19
+ import { execFileSync } from 'node:child_process'
20
+ import { basename, dirname, join, relative, resolve } from 'node:path'
21
+ import { fileURLToPath } from 'node:url'
22
+
23
+ const CATALOG = fileURLToPath(new URL('..', import.meta.url))
24
+ const argv = process.argv.slice(2)
25
+ const FORCE = argv.includes('--force')
26
+
27
+ const pIx = argv.indexOf('--platform')
28
+ let PLATFORM = pIx !== -1 ? argv[pIx + 1] || '' : ''
29
+ if (pIx !== -1 && (!PLATFORM || PLATFORM.startsWith('--'))) {
30
+ console.error('missing or invalid --platform value (expected gh or glab)')
31
+ process.exit(1)
32
+ }
33
+ const positional = argv.filter((a, i) => !a.startsWith('--') && (pIx === -1 || i !== pIx + 1))
34
+ const [pattern, targetArg] = positional
35
+ if (!pattern || !targetArg) {
36
+ console.error('usage: node scripts/adopt.mjs <pattern-name> <target-dir> [--platform gh|glab] [--force]')
37
+ process.exit(1)
38
+ }
39
+
40
+ const scaffold = join(CATALOG, 'patterns', pattern, 'scaffold')
41
+ if (!existsSync(scaffold)) {
42
+ const available = existsSync(join(CATALOG, 'patterns'))
43
+ ? readdirSync(join(CATALOG, 'patterns')).filter((d) => existsSync(join(CATALOG, 'patterns', d, 'scaffold')))
44
+ : []
45
+ console.error(`unknown pattern: ${pattern}\navailable: ${available.join(', ') || '(none)'}`)
46
+ process.exit(1)
47
+ }
48
+ const target = resolve(targetArg)
49
+ let targetOk = false
50
+ try { targetOk = statSync(target).isDirectory() } catch {}
51
+ if (!targetOk) {
52
+ console.error(`target is not a directory: ${target}`)
53
+ process.exit(1)
54
+ }
55
+
56
+ if (!PLATFORM) {
57
+ try {
58
+ const origin = execFileSync('git', ['-C', target, 'remote', 'get-url', 'origin'], {
59
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
60
+ }).trim()
61
+ const host = (origin.match(/(?:@|:\/\/)([^/:]+)[/:]/) || [])[1] || ''
62
+ PLATFORM = host.includes('gitlab') ? 'glab' : 'gh'
63
+ console.log(`platform: ${PLATFORM} (autodetected from ${host || origin}; override with --platform)`)
64
+ } catch {
65
+ PLATFORM = 'gh'
66
+ console.log('platform: gh (no origin remote detected; override with --platform glab)')
67
+ }
68
+ }
69
+ if (PLATFORM !== 'gh' && PLATFORM !== 'glab') {
70
+ console.error(`unknown platform: ${PLATFORM} (expected gh or glab)`)
71
+ process.exit(1)
72
+ }
73
+
74
+ let installed = 0
75
+ let skipped = 0
76
+ const note = (line) => console.log(line)
77
+
78
+ function* walk(dir) {
79
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
80
+ const f = join(dir, e.name)
81
+ if (e.isDirectory()) yield* walk(f)
82
+ else yield f
83
+ }
84
+ }
85
+
86
+ const copyFile = (src, dst, label) => {
87
+ if (existsSync(dst) && !FORCE) {
88
+ console.log(`= exists ${label}`)
89
+ skipped++
90
+ return false
91
+ }
92
+ mkdirSync(dirname(dst), { recursive: true })
93
+ cpSync(src, dst)
94
+ console.log(`+ install ${label}`)
95
+ installed++
96
+ return true
97
+ }
98
+
99
+ // 1. scaffold .claude/ (per-file so re-runs skip; settings.json conflicts get a manual-merge note)
100
+ for (const src of walk(join(scaffold, '.claude'))) {
101
+ const rel = relative(scaffold, src).replaceAll('\\', '/')
102
+ const dst = join(target, rel)
103
+ const existed = existsSync(dst)
104
+ copyFile(src, dst, rel)
105
+ if (existed && !FORCE && rel === '.claude/settings.json') {
106
+ note(' (note) existing .claude/settings.json kept — merge the hooks.PreToolUse entry and permissions.allow from the scaffold manually')
107
+ }
108
+ }
109
+
110
+ // 2. universal ticket template
111
+ copyFile(join(CATALOG, 'templates', 'ticket.template.md'), join(target, 'templates', 'ticket.template.md'), 'templates/ticket.template.md')
112
+
113
+ // 3. platform tracker templates (issues + PR/MR)
114
+ const trackerSrc = join(CATALOG, 'templates', 'tracker', PLATFORM === 'gh' ? 'github' : 'gitlab')
115
+ const trackerDstRoot = join(target, PLATFORM === 'gh' ? '.github' : '.gitlab')
116
+ for (const src of walk(trackerSrc)) {
117
+ const rel = relative(trackerSrc, src).replaceAll('\\', '/')
118
+ copyFile(src, join(trackerDstRoot, rel), `${PLATFORM === 'gh' ? '.github' : '.gitlab'}/${rel}`)
119
+ }
120
+
121
+ // 4. docs skeleton
122
+ for (const d of ['docs/prd', 'docs/adr', 'docs/plans']) {
123
+ const dir = join(target, d)
124
+ if (!existsSync(dir)) {
125
+ mkdirSync(dir, { recursive: true })
126
+ writeFileSync(join(dir, '.gitkeep'), '')
127
+ console.log(`+ mkdir ${d}/`)
128
+ installed++
129
+ } else {
130
+ console.log(`= exists ${d}/`)
131
+ skipped++
132
+ }
133
+ }
134
+
135
+ // 5. root PRD.md -> docs/PRD.md (copy, never move — the pipeline reads docs/PRD.md)
136
+ const rootPrd = join(target, 'PRD.md')
137
+ const docsPrd = join(target, 'docs', 'PRD.md')
138
+ if (existsSync(rootPrd) && !existsSync(docsPrd)) {
139
+ cpSync(rootPrd, docsPrd)
140
+ console.log('+ install docs/PRD.md (copied from root PRD.md — the pipeline reads docs/PRD.md; delete the root copy when ready)')
141
+ installed++
142
+ } else if (existsSync(docsPrd)) {
143
+ console.log('= exists docs/PRD.md')
144
+ skipped++
145
+ } else {
146
+ note(' (note) no PRD.md found — write docs/PRD.md before running /breakdown-prd')
147
+ }
148
+
149
+ // 6. CLAUDE.md: create from the snippet, or append it once (marker-checked, never duplicated)
150
+ const snippet = readFileSync(join(scaffold, 'claude-md-snippet.md'), 'utf8')
151
+ const MARKER = '## Delivery pipeline — three-agent Architect / Builder / Reviewer'
152
+ const claudeMd = join(target, 'CLAUDE.md')
153
+ if (!existsSync(claudeMd)) {
154
+ const header = `# ${basename(target)} — Project Constitution\n\n> Auto-loaded into every session. Installed by agent-templates adopt.mjs on ${new Date().toISOString().slice(0, 10)}.\n> Add your project facts and non-negotiable constraints above the pipeline section.\n\n`
155
+ writeFileSync(claudeMd, header + snippet)
156
+ console.log('+ install CLAUDE.md (seeded from the pattern snippet)')
157
+ installed++
158
+ } else if (!readFileSync(claudeMd, 'utf8').includes(MARKER)) {
159
+ writeFileSync(claudeMd, readFileSync(claudeMd, 'utf8').trimEnd() + '\n\n' + snippet)
160
+ console.log('+ append CLAUDE.md (pipeline snippet appended)')
161
+ installed++
162
+ } else {
163
+ console.log('= exists CLAUDE.md (pipeline snippet already present)')
164
+ skipped++
165
+ }
166
+
167
+ console.log(`\nadopt: ${installed} installed, ${skipped} already present. Pattern: ${pattern}, platform: ${PLATFORM}.`)
168
+ console.log(`
169
+ NEXT STEPS (details: ${join(CATALOG, 'ADOPTING.md')})
170
+ 1. Review CLAUDE.md — set the Operating mode line (start: supervised) and add your
171
+ project facts; fill the Constraint check section of the PR/MR template.
172
+ 2. Tracker: git remote + authenticated CLI (${PLATFORM} auth login). Node >= 18 on PATH.
173
+ 3. In Claude Code, in the target repo: /breakdown-prd
174
+ (Architect decomposes docs/PRD.md into sub-PRDs + tickets, then stops for your review)
175
+ 4. Gate 1 — review the breakdown, then: /start-milestone docs/prd/00-<module> supervised
176
+ 5. Graduate to autonomous when the pattern holds; optional nightly sweep:
177
+ see the pattern's INSTALL.md § Nightly sweep.`)