agent-templates 0.1.0 → 0.2.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 (38) hide show
  1. package/ADOPTING.md +57 -57
  2. package/CLAUDE.md +128 -125
  3. package/LICENSE +21 -21
  4. package/README.md +32 -32
  5. package/package.json +32 -32
  6. package/patterns/three-agent-architect-builder-reviewer/README.md +161 -153
  7. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/architect.md +31 -31
  8. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/builder.md +25 -25
  9. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/reviewer.md +33 -33
  10. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/agents/triage.md +31 -31
  11. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/breakdown-prd.md +18 -18
  12. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/nightly-issues.md +14 -14
  13. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/review-ticket.md +14 -14
  14. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/start-all.md +17 -0
  15. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/start-milestone.md +15 -15
  16. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/commands/verify-delivery.md +20 -20
  17. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/hooks/guard-main-session-writes.mjs +53 -53
  18. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/scripts/milestone-dag.mjs +94 -0
  19. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/scripts/publish-tickets.mjs +263 -263
  20. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/settings.json +15 -15
  21. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/workflows/nightly-issues.js +139 -139
  22. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/workflows/run-milestone.js +223 -223
  23. package/patterns/three-agent-architect-builder-reviewer/scaffold/.claude/workflows/start-all.js +118 -0
  24. package/patterns/three-agent-architect-builder-reviewer/scaffold/INSTALL.md +65 -65
  25. package/patterns/three-agent-architect-builder-reviewer/scaffold/claude-md-snippet.md +38 -37
  26. package/scripts/adopt.mjs +187 -177
  27. package/scripts/build-site.mjs +264 -264
  28. package/scripts/cli.mjs +52 -52
  29. package/templates/pattern-README.template.md +65 -65
  30. package/templates/ticket.template.md +104 -104
  31. package/templates/tracker/github/ISSUE_TEMPLATE/bug-report.md +28 -28
  32. package/templates/tracker/github/ISSUE_TEMPLATE/decision-record.md +25 -25
  33. package/templates/tracker/github/ISSUE_TEMPLATE/task.md +36 -36
  34. package/templates/tracker/github/PULL_REQUEST_TEMPLATE.md +54 -54
  35. package/templates/tracker/gitlab/issue_templates/bug-report.md +23 -23
  36. package/templates/tracker/gitlab/issue_templates/decision-record.md +20 -20
  37. package/templates/tracker/gitlab/issue_templates/task.md +31 -31
  38. package/templates/tracker/gitlab/merge_request_templates/default.md +55 -55
@@ -1,139 +1,139 @@
1
- export const meta = {
2
- name: 'nightly-issues',
3
- description: 'Nightly sweep: triage open issues, run fixable ones through the three-agent pipeline, post the morning report',
4
- phases: [{ title: 'Triage' }, { title: 'Fix' }, { title: 'Report' }],
5
- }
6
-
7
- // Deterministic nightly orchestrator. The command (/nightly-issues) collects the
8
- // issue list and date (Date is unavailable in workflow scripts) and passes:
9
- //
10
- // args:
11
- // {
12
- // issues: [{ number, title, body, labels: [..], isNew: bool, url }],
13
- // maxIssues: 5, // optional cost cap per night
14
- // defaultBranch: 'main', // optional
15
- // platform: 'gh' | 'glab', // default 'gh'
16
- // reportDate: 'YYYY-MM-DD', // required — stamped by the caller
17
- // }
18
- //
19
- // Design: triage never touches the tracker; the pipeline (child run-milestone
20
- // workflow) does the fixing with continueOnFailure=true (issues are independent,
21
- // unlike milestone tickets); a single report step does ALL tracker writes at the
22
- // end — comments, labels, and the "Nightly report <date>" issue the human reads
23
- // over morning email (tracker notifications).
24
-
25
- const cfg = Object.assign({ maxIssues: 5, defaultBranch: 'main', platform: 'gh' }, args)
26
- if (!Array.isArray(cfg.issues)) throw new Error('args.issues must be an array (may be empty)')
27
- if (typeof cfg.reportDate !== 'string' || !cfg.reportDate) throw new Error('args.reportDate is required (the caller stamps the date)')
28
- if (!Number.isInteger(cfg.maxIssues) || cfg.maxIssues < 1) throw new Error('args.maxIssues must be an integer >= 1')
29
- if (cfg.platform !== 'gh' && cfg.platform !== 'glab') throw new Error("args.platform must be 'gh' or 'glab'")
30
-
31
- const SKIP_LABELS = ['nightly:escalated', 'triage:invalid', 'needs-human']
32
- const eligible = cfg.issues.filter(function (i) {
33
- return i && Number.isInteger(i.number) && !(i.labels || []).some(function (l) { return SKIP_LABELS.includes(l) })
34
- })
35
- const candidates = eligible.slice(0, cfg.maxIssues)
36
- if (eligible.length > candidates.length) {
37
- log('cap: processing ' + candidates.length + '/' + eligible.length + ' eligible issues (maxIssues=' + cfg.maxIssues + '); the rest wait for the next night')
38
- }
39
-
40
- const TRIAGE = {
41
- type: 'object',
42
- properties: {
43
- classification: { enum: ['fixable', 'invalid', 'needs-human'] },
44
- reason: { type: 'string' },
45
- ticketPath: { type: 'string' },
46
- },
47
- required: ['classification', 'reason'],
48
- }
49
- const REPORT = {
50
- type: 'object',
51
- properties: { reportUrl: { type: 'string' }, notes: { type: 'string' } },
52
- required: ['reportUrl'],
53
- }
54
-
55
- // ---- Phase 1: triage (parallel, read-only + ticket synthesis) ----
56
- const triaged = await parallel(candidates.map(function (issue) {
57
- return function () {
58
- return agent(
59
- 'Triage this tracker issue per your role definition.\n' +
60
- 'Issue #' + issue.number + ': ' + issue.title + '\n' +
61
- 'Labels: ' + JSON.stringify(issue.labels || []) + '\n' +
62
- 'Body:\n' + (issue.body || '(empty)') + '\n\n' +
63
- 'If fixable, the ticket file MUST be docs/prd/99-nightly/tickets/ISS-' + issue.number + '-<slug>.md with id ISS-' + issue.number + '.',
64
- { agentType: 'triage', label: 'triage:#' + issue.number, phase: 'Triage', schema: TRIAGE }
65
- ).then(function (t) {
66
- return { issue: issue, triage: t || { classification: 'needs-human', reason: 'triage agent returned nothing' } }
67
- })
68
- }
69
- }))
70
- const outcomes = triaged.filter(Boolean)
71
-
72
- const fixable = outcomes.filter(function (o) { return o.triage.classification === 'fixable' && o.triage.ticketPath })
73
- for (const o of outcomes) {
74
- if (o.triage.classification === 'fixable' && !o.triage.ticketPath) {
75
- o.triage.classification = 'needs-human'
76
- o.triage.reason = 'classified fixable but returned no ticket path — ' + o.triage.reason
77
- }
78
- }
79
- log('triage: ' + fixable.length + ' fixable, ' +
80
- outcomes.filter(function (o) { return o.triage.classification === 'invalid' }).length + ' invalid, ' +
81
- outcomes.filter(function (o) { return o.triage.classification === 'needs-human' }).length + ' needs-human')
82
-
83
- // ---- Phase 2: fix via the milestone runner (child workflow, one nesting level) ----
84
- let pipeline_results = []
85
- if (fixable.length > 0) {
86
- const tickets = fixable.map(function (o) {
87
- return { id: 'ISS-' + o.issue.number, path: o.triage.ticketPath, issue: o.issue.number }
88
- })
89
- try {
90
- const child = await workflow('run-milestone', {
91
- tickets: tickets,
92
- mode: 'autonomous',
93
- defaultBranch: cfg.defaultBranch,
94
- platform: cfg.platform,
95
- continueOnFailure: true, // nightly issues are independent; one failure must not strand the rest
96
- })
97
- pipeline_results = (child && child.results) || []
98
- } catch (e) {
99
- log('pipeline failed to run: ' + (e && e.message ? e.message : String(e)))
100
- pipeline_results = tickets.map(function (t) { return { id: t.id, status: 'failed', stage: 'pipeline', detail: 'milestone runner did not run' } })
101
- }
102
- }
103
- const resultFor = function (issueNumber) {
104
- return pipeline_results.find(function (r) { return r.id === 'ISS-' + issueNumber }) || null
105
- }
106
-
107
- // ---- Phase 3: report (the ONLY tracker-writing step) ----
108
- const digest = outcomes.map(function (o) {
109
- const r = resultFor(o.issue.number)
110
- return {
111
- number: o.issue.number,
112
- title: o.issue.title,
113
- isNew: Boolean(o.issue.isNew),
114
- classification: o.triage.classification,
115
- reason: o.triage.reason,
116
- pipeline: r ? { status: r.status, stage: r.stage || null, bounces: r.bounces || 0, detail: r.detail || null } : null,
117
- }
118
- })
119
-
120
- const report = await agent(
121
- 'You are the nightly report step — the only step allowed to write to the tracker (' + cfg.platform + ' CLI via Bash). ' +
122
- 'Process this digest of tonight\'s sweep (JSON): ' + JSON.stringify({ date: cfg.reportDate, digest: digest, notProcessed: eligible.length - candidates.length }) + '\n' +
123
- 'Do, in order:\n' +
124
- '1. Per issue: post ONE comment stating the outcome — fixed (link the merge) / attempted-but-not-solved (why, findings) / needs-human (why) / invalid (why). Base every statement on the digest; fabricate nothing.\n' +
125
- '2. Labels: add `triage:invalid` to invalid issues (do NOT close them — the human decides in the morning); add `nightly:escalated` to attempted-but-not-solved and pipeline-failed issues; add `needs-human` to needs-human issues.\n' +
126
- '3. For issues whose pipeline status is `delivered`: verify the issue is actually closed; close it if the deliver step missed it.\n' +
127
- '4. Create a tracker issue titled exactly "Nightly report ' + cfg.reportDate + '" (search first; if it already exists, comment on it instead of duplicating) with sections: New overnight · Fixed & closed · Attempted, not solved · Needs human · Invalid (ignore) · Not processed (cap). Use issue references (#N) so the tracker links them.\n' +
128
- 'Return reportUrl and notes (anything that went wrong).',
129
- { label: 'report:' + cfg.reportDate, phase: 'Report', schema: REPORT }
130
- )
131
-
132
- return {
133
- date: cfg.reportDate,
134
- processed: digest,
135
- notProcessed: eligible.length - candidates.length,
136
- fixed: digest.filter(function (d) { return d.pipeline && d.pipeline.status === 'delivered' }).length,
137
- reportUrl: report ? report.reportUrl : null,
138
- reportNotes: report ? report.notes : 'report step returned nothing',
139
- }
1
+ export const meta = {
2
+ name: 'nightly-issues',
3
+ description: 'Nightly sweep: triage open issues, run fixable ones through the three-agent pipeline, post the morning report',
4
+ phases: [{ title: 'Triage' }, { title: 'Fix' }, { title: 'Report' }],
5
+ }
6
+
7
+ // Deterministic nightly orchestrator. The command (/nightly-issues) collects the
8
+ // issue list and date (Date is unavailable in workflow scripts) and passes:
9
+ //
10
+ // args:
11
+ // {
12
+ // issues: [{ number, title, body, labels: [..], isNew: bool, url }],
13
+ // maxIssues: 5, // optional cost cap per night
14
+ // defaultBranch: 'main', // optional
15
+ // platform: 'gh' | 'glab', // default 'gh'
16
+ // reportDate: 'YYYY-MM-DD', // required — stamped by the caller
17
+ // }
18
+ //
19
+ // Design: triage never touches the tracker; the pipeline (child run-milestone
20
+ // workflow) does the fixing with continueOnFailure=true (issues are independent,
21
+ // unlike milestone tickets); a single report step does ALL tracker writes at the
22
+ // end — comments, labels, and the "Nightly report <date>" issue the human reads
23
+ // over morning email (tracker notifications).
24
+
25
+ const cfg = Object.assign({ maxIssues: 5, defaultBranch: 'main', platform: 'gh' }, args)
26
+ if (!Array.isArray(cfg.issues)) throw new Error('args.issues must be an array (may be empty)')
27
+ if (typeof cfg.reportDate !== 'string' || !cfg.reportDate) throw new Error('args.reportDate is required (the caller stamps the date)')
28
+ if (!Number.isInteger(cfg.maxIssues) || cfg.maxIssues < 1) throw new Error('args.maxIssues must be an integer >= 1')
29
+ if (cfg.platform !== 'gh' && cfg.platform !== 'glab') throw new Error("args.platform must be 'gh' or 'glab'")
30
+
31
+ const SKIP_LABELS = ['nightly:escalated', 'triage:invalid', 'needs-human']
32
+ const eligible = cfg.issues.filter(function (i) {
33
+ return i && Number.isInteger(i.number) && !(i.labels || []).some(function (l) { return SKIP_LABELS.includes(l) })
34
+ })
35
+ const candidates = eligible.slice(0, cfg.maxIssues)
36
+ if (eligible.length > candidates.length) {
37
+ log('cap: processing ' + candidates.length + '/' + eligible.length + ' eligible issues (maxIssues=' + cfg.maxIssues + '); the rest wait for the next night')
38
+ }
39
+
40
+ const TRIAGE = {
41
+ type: 'object',
42
+ properties: {
43
+ classification: { enum: ['fixable', 'invalid', 'needs-human'] },
44
+ reason: { type: 'string' },
45
+ ticketPath: { type: 'string' },
46
+ },
47
+ required: ['classification', 'reason'],
48
+ }
49
+ const REPORT = {
50
+ type: 'object',
51
+ properties: { reportUrl: { type: 'string' }, notes: { type: 'string' } },
52
+ required: ['reportUrl'],
53
+ }
54
+
55
+ // ---- Phase 1: triage (parallel, read-only + ticket synthesis) ----
56
+ const triaged = await parallel(candidates.map(function (issue) {
57
+ return function () {
58
+ return agent(
59
+ 'Triage this tracker issue per your role definition.\n' +
60
+ 'Issue #' + issue.number + ': ' + issue.title + '\n' +
61
+ 'Labels: ' + JSON.stringify(issue.labels || []) + '\n' +
62
+ 'Body:\n' + (issue.body || '(empty)') + '\n\n' +
63
+ 'If fixable, the ticket file MUST be docs/prd/99-nightly/tickets/ISS-' + issue.number + '-<slug>.md with id ISS-' + issue.number + '.',
64
+ { agentType: 'triage', label: 'triage:#' + issue.number, phase: 'Triage', schema: TRIAGE }
65
+ ).then(function (t) {
66
+ return { issue: issue, triage: t || { classification: 'needs-human', reason: 'triage agent returned nothing' } }
67
+ })
68
+ }
69
+ }))
70
+ const outcomes = triaged.filter(Boolean)
71
+
72
+ const fixable = outcomes.filter(function (o) { return o.triage.classification === 'fixable' && o.triage.ticketPath })
73
+ for (const o of outcomes) {
74
+ if (o.triage.classification === 'fixable' && !o.triage.ticketPath) {
75
+ o.triage.classification = 'needs-human'
76
+ o.triage.reason = 'classified fixable but returned no ticket path — ' + o.triage.reason
77
+ }
78
+ }
79
+ log('triage: ' + fixable.length + ' fixable, ' +
80
+ outcomes.filter(function (o) { return o.triage.classification === 'invalid' }).length + ' invalid, ' +
81
+ outcomes.filter(function (o) { return o.triage.classification === 'needs-human' }).length + ' needs-human')
82
+
83
+ // ---- Phase 2: fix via the milestone runner (child workflow, one nesting level) ----
84
+ let pipeline_results = []
85
+ if (fixable.length > 0) {
86
+ const tickets = fixable.map(function (o) {
87
+ return { id: 'ISS-' + o.issue.number, path: o.triage.ticketPath, issue: o.issue.number }
88
+ })
89
+ try {
90
+ const child = await workflow('run-milestone', {
91
+ tickets: tickets,
92
+ mode: 'autonomous',
93
+ defaultBranch: cfg.defaultBranch,
94
+ platform: cfg.platform,
95
+ continueOnFailure: true, // nightly issues are independent; one failure must not strand the rest
96
+ })
97
+ pipeline_results = (child && child.results) || []
98
+ } catch (e) {
99
+ log('pipeline failed to run: ' + (e && e.message ? e.message : String(e)))
100
+ pipeline_results = tickets.map(function (t) { return { id: t.id, status: 'failed', stage: 'pipeline', detail: 'milestone runner did not run' } })
101
+ }
102
+ }
103
+ const resultFor = function (issueNumber) {
104
+ return pipeline_results.find(function (r) { return r.id === 'ISS-' + issueNumber }) || null
105
+ }
106
+
107
+ // ---- Phase 3: report (the ONLY tracker-writing step) ----
108
+ const digest = outcomes.map(function (o) {
109
+ const r = resultFor(o.issue.number)
110
+ return {
111
+ number: o.issue.number,
112
+ title: o.issue.title,
113
+ isNew: Boolean(o.issue.isNew),
114
+ classification: o.triage.classification,
115
+ reason: o.triage.reason,
116
+ pipeline: r ? { status: r.status, stage: r.stage || null, bounces: r.bounces || 0, detail: r.detail || null } : null,
117
+ }
118
+ })
119
+
120
+ const report = await agent(
121
+ 'You are the nightly report step — the only step allowed to write to the tracker (' + cfg.platform + ' CLI via Bash). ' +
122
+ 'Process this digest of tonight\'s sweep (JSON): ' + JSON.stringify({ date: cfg.reportDate, digest: digest, notProcessed: eligible.length - candidates.length }) + '\n' +
123
+ 'Do, in order:\n' +
124
+ '1. Per issue: post ONE comment stating the outcome — fixed (link the merge) / attempted-but-not-solved (why, findings) / needs-human (why) / invalid (why). Base every statement on the digest; fabricate nothing.\n' +
125
+ '2. Labels: add `triage:invalid` to invalid issues (do NOT close them — the human decides in the morning); add `nightly:escalated` to attempted-but-not-solved and pipeline-failed issues; add `needs-human` to needs-human issues.\n' +
126
+ '3. For issues whose pipeline status is `delivered`: verify the issue is actually closed; close it if the deliver step missed it.\n' +
127
+ '4. Create a tracker issue titled exactly "Nightly report ' + cfg.reportDate + '" (search first; if it already exists, comment on it instead of duplicating) with sections: New overnight · Fixed & closed · Attempted, not solved · Needs human · Invalid (ignore) · Not processed (cap). Use issue references (#N) so the tracker links them.\n' +
128
+ 'Return reportUrl and notes (anything that went wrong).',
129
+ { label: 'report:' + cfg.reportDate, phase: 'Report', schema: REPORT }
130
+ )
131
+
132
+ return {
133
+ date: cfg.reportDate,
134
+ processed: digest,
135
+ notProcessed: eligible.length - candidates.length,
136
+ fixed: digest.filter(function (d) { return d.pipeline && d.pipeline.status === 'delivered' }).length,
137
+ reportUrl: report ? report.reportUrl : null,
138
+ reportNotes: report ? report.notes : 'report step returned nothing',
139
+ }