create-agent-rig 0.4.0 → 0.6.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 +316 -11
- package/README.md +58 -19
- package/package.json +2 -1
- package/packages/cli/dist/commands/create.js +8 -3
- package/packages/cli/dist/commands/init.js +85 -34
- package/packages/cli/dist/commands/upgrade.js +112 -30
- package/packages/cli/dist/index.js +81 -19
- package/packages/cli/dist/lib/copy-tree.js +35 -6
- package/packages/cli/dist/lib/init-settings.js +12 -0
- package/packages/cli/dist/lib/install-set.js +6 -8
- package/packages/cli/dist/lib/manifest.js +31 -9
- package/packages/cli/dist/lib/safe-path.js +30 -0
- package/scripts/prepare.mjs +1 -1
- package/templates/agent-os/init/AGENTS.md +199 -0
- package/templates/agent-os/init/CLAUDE.md +69 -9
- package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +105 -0
- package/templates/agent-os/stack/aws-cdk/.agents/skills/ro-debug/SKILL.md +117 -0
- package/templates/agent-os/stack/aws-cdk/.claude/agents/cdk-diff-reviewer.md +41 -3
- package/templates/agent-os/stack/aws-cdk/.claude/rules/aws-cdk.md +1 -1
- package/templates/agent-os/stack/aws-cdk/.claude/skills/post-deploy-verify/SKILL.md +41 -0
- package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +4 -0
- package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +29 -0
- package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +236 -0
- package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +958 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/SKILL.md +102 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.mjs +78 -0
- package/templates/agent-os/universal/.agents/skills/new-invariant/guard-invariant.example.test.mjs +89 -0
- package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +396 -0
- package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +73 -0
- package/templates/agent-os/universal/.claude/agents/code-reviewer.md +40 -1
- package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +71 -1
- package/templates/agent-os/universal/.claude/agents/security-scanner.md +40 -0
- package/templates/agent-os/universal/.claude/hooks/gate-stop-dod.mjs +301 -26
- package/templates/agent-os/universal/.claude/hooks/guard-bash.mjs +43 -5
- package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +25 -11
- package/templates/agent-os/universal/.claude/hooks/guard-rulebook.mjs +127 -0
- package/templates/agent-os/universal/.claude/hooks/guard-secret-file.mjs +180 -0
- package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +32 -13
- package/templates/agent-os/universal/.claude/hooks/inject-rules.mjs +175 -7
- package/templates/agent-os/universal/.claude/hooks/lib/edit-input.mjs +503 -0
- package/templates/agent-os/universal/.claude/rules/autonomy.md +69 -1
- package/templates/agent-os/universal/.claude/rules/invariants.md +104 -19
- package/templates/agent-os/universal/.claude/rules/workflow.md +55 -5
- package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1260 -0
- package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +65 -2
- package/templates/agent-os/universal/.claude/scripts/doctor.mjs +351 -0
- package/templates/agent-os/universal/.claude/scripts/git-env.mjs +49 -0
- package/templates/agent-os/universal/.claude/scripts/lib/gate-coverage.mjs +306 -0
- package/templates/agent-os/universal/.claude/scripts/lib/revalidation-points.mjs +28 -0
- package/templates/agent-os/universal/.claude/scripts/lib/secrets.mjs +490 -0
- package/templates/agent-os/universal/.claude/scripts/lib/verdict.mjs +462 -0
- package/templates/agent-os/universal/.claude/scripts/preflight.mjs +33 -17
- package/templates/agent-os/universal/.claude/scripts/queue/as-of.mjs +51 -0
- package/templates/agent-os/universal/.claude/scripts/queue/checkout.mjs +149 -0
- package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +809 -28
- package/templates/agent-os/universal/.claude/scripts/queue/gate-rounds.mjs +160 -0
- package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +95 -16
- package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +543 -15
- package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +457 -47
- package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +289 -25
- package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +197 -0
- package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +316 -0
- package/templates/agent-os/universal/.claude/scripts/revalidation-report.mjs +180 -0
- package/templates/agent-os/universal/.claude/scripts/run-journal.mjs +435 -0
- package/templates/agent-os/universal/.claude/scripts/run-state.mjs +539 -0
- package/templates/agent-os/universal/.claude/scripts/stop-flag.mjs +15 -8
- package/templates/agent-os/universal/.claude/scripts/unattended-flag.mjs +239 -0
- package/templates/agent-os/universal/.claude/scripts/verdict.mjs +198 -0
- package/templates/agent-os/universal/.claude/settings.json +11 -2
- package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +118 -7
- package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +667 -38
- package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +359 -26
- package/templates/agent-os/universal/.codex/agents/code-reviewer.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/prose-reviewer.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/security-scanner.toml +4 -0
- package/templates/agent-os/universal/.codex/agents/test-writer.toml +4 -0
- package/templates/agent-os/universal/.codex/hooks.json +70 -0
- package/templates/agent-os/universal/AGENTS.md +166 -0
- package/templates/agent-os/universal/CLAUDE.md +49 -14
- package/templates/agent-os/universal/PLAN.md +7 -40
- package/templates/agent-os/universal/docs/decisions/closing-a-task.md +98 -0
- package/templates/agent-os/universal/docs/decisions/codex-adapter.md +108 -0
- package/templates/agent-os/universal/docs/decisions/fail-open-guards.md +43 -0
- package/templates/agent-os/universal/docs/decisions/gate-coverage.md +83 -0
- package/templates/agent-os/universal/docs/decisions/review-lanes.md +72 -0
- package/templates/agent-os/universal/docs/decisions/run-directory.md +44 -0
- package/templates/agent-os/universal/docs/decisions/spacing-rations-mechanisms.md +125 -0
- package/templates/agent-os/universal/docs/decisions/stop-conditions-in-a-file.md +46 -0
- package/templates/agent-os/universal/docs/decisions/two-empty-endings.md +74 -0
- package/templates/agent-os/universal/journal/README.md +101 -0
- package/templates/agent-os/universal/layers.json +45 -2
- package/templates/hash-history.json +310 -49
- package/templates/release-ledger.json +9 -0
- package/templates/skeleton/aws-serverless/.github/workflows/deploy.yml +34 -1
- package/templates/skeleton/aws-serverless/README.md +91 -9
- package/templates/skeleton/aws-serverless/apps/web/src/lib/api.ts +9 -2
- package/templates/skeleton/aws-serverless/gitignore +37 -0
- package/templates/skeleton/aws-serverless/infra/bin/app.ts +73 -13
- package/templates/skeleton/aws-serverless/infra/lib/app-stack.ts +136 -4
- package/templates/skeleton/aws-serverless/infra/lib/web-stack.ts +14 -1
- package/templates/skeleton/aws-serverless/infra/test/allowed-origins.test.ts +301 -0
- package/templates/skeleton/aws-serverless/infra/test/app-composition.test.ts +137 -0
- package/templates/skeleton/aws-serverless/infra/test/app-stack.test.ts +36 -20
- package/templates/skeleton/aws-serverless/infra/test/web-stack.test.ts +15 -12
- package/templates/skeleton/aws-serverless/services/api/src/handlers/create-note.ts +33 -6
- package/templates/skeleton/aws-serverless/services/api/src/handlers/list-notes.ts +18 -5
- package/templates/skeleton/aws-serverless/services/api/src/usecases/create-note.ts +11 -0
- package/templates/skeleton/aws-serverless/services/api/test/create-note.handler.test.ts +120 -1
- package/templates/skeleton/aws-serverless/services/api/test/list-notes.test.ts +72 -1
- package/templates/skeleton/node-service/README.md +11 -1
- package/templates/skeleton/node-service/gitignore +34 -0
- package/templates/skeleton/node-service/packages/db/src/note-store.ts +47 -10
- package/templates/skeleton/node-service/packages/db/test/note-store.test.ts +20 -0
- package/templates/skeleton/node-service/services/api/src/main.ts +2 -9
- package/templates/skeleton/node-service/services/api/src/server.ts +93 -10
- package/templates/skeleton/node-service/services/api/src/static-dir.ts +20 -0
- package/templates/skeleton/node-service/services/api/test/server.test.ts +98 -13
- package/templates/skeleton/node-service/services/api/test/static-dir.test.ts +28 -0
|
@@ -10,8 +10,144 @@
|
|
|
10
10
|
// The injected content is deliberately STATELESS — rules, never facts about
|
|
11
11
|
// the moment (mid-session injections are replayed on resume, so timestamps
|
|
12
12
|
// or SHAs here would lie). And it is only the load-bearing part, not the
|
|
13
|
-
// whole rulebook
|
|
14
|
-
|
|
13
|
+
// whole rulebook.
|
|
14
|
+
//
|
|
15
|
+
// Which part is load-bearing is not this hook's judgment to make: the rule
|
|
16
|
+
// file marks what it does not need injected, and everything else goes. That
|
|
17
|
+
// division of labour is the whole design, and it was arrived at the expensive
|
|
18
|
+
// way. An earlier version of this file selected `## ` sections from a kept
|
|
19
|
+
// list — and four review rounds each found a different way for that selection
|
|
20
|
+
// to return a silently truncated excerpt: a heading inside a code fence, a
|
|
21
|
+
// heading inside a skipped region, a section whose heading and body fell on
|
|
22
|
+
// opposite sides of a marker, a heading inside an HTML block. Each fix closed
|
|
23
|
+
// one spelling and the next round found another, because a parser that infers
|
|
24
|
+
// structure has no bottom. This one does not parse structure at all.
|
|
25
|
+
//
|
|
26
|
+
// ⚠ The saving rests on an assumption this repository cannot enforce: that the
|
|
27
|
+
// tool already loads `.claude/rules/*.md` as project instructions, so injecting
|
|
28
|
+
// the whole file pays for it twice. That is harness behaviour, observable but
|
|
29
|
+
// not pinned here. Where it does not hold, this is a plain subtraction — which
|
|
30
|
+
// is why every ambiguity resolves toward injecting more.
|
|
31
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
32
|
+
import { fileURLToPath } from 'node:url';
|
|
33
|
+
|
|
34
|
+
// Regions the rule file marks as not worth injecting. The marker is explicit
|
|
35
|
+
// and lives in the rule file itself, where the person editing it can see it —
|
|
36
|
+
// an earlier version keyed on heading level instead, which made "what every
|
|
37
|
+
// session is governed by" a silent consequence of a formatting choice.
|
|
38
|
+
const SKIP_OPEN = '<!-- inject:skip -->';
|
|
39
|
+
const SKIP_CLOSE = '<!-- /inject:skip -->';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A fence opener, as CommonMark defines one: three or more backticks or
|
|
43
|
+
* tildes. The closer has to be the same character and no shorter, which is why
|
|
44
|
+
* this returns the run rather than a boolean — a three-backtick line inside a
|
|
45
|
+
* four-backtick block is content, not the end of the block.
|
|
46
|
+
*/
|
|
47
|
+
function fenceRun(line) {
|
|
48
|
+
const trimmed = line.trimStart();
|
|
49
|
+
const char = trimmed[0];
|
|
50
|
+
if (char !== '`' && char !== '~') return null;
|
|
51
|
+
let length = 0;
|
|
52
|
+
while (trimmed[length] === char) length += 1;
|
|
53
|
+
if (length < 3) return null;
|
|
54
|
+
// `bare` is what separates a closer from an opener: an opener may carry an
|
|
55
|
+
// info string (```sh), a closer may not.
|
|
56
|
+
return { char, length, bare: trimmed.slice(length).trim() === '' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The whole file, minus every region the file itself marks with
|
|
61
|
+
* `<!-- inject:skip -->` … `<!-- /inject:skip -->`. That is the entire
|
|
62
|
+
* operation: nothing here reads a heading, so no arrangement of headings —
|
|
63
|
+
* indented, inside an HTML block, split across a marker — can change what
|
|
64
|
+
* survives. What is omitted is a decision made in the rule file, by whoever
|
|
65
|
+
* writes the rule, and visible on the line above it.
|
|
66
|
+
*
|
|
67
|
+
* Fenced code is data, not structure, so a marker inside a fence is content.
|
|
68
|
+
* Both fence characters, with the CommonMark closing rule (same character, at
|
|
69
|
+
* least as long, nothing but whitespace after it) — a rules file quotes shell
|
|
70
|
+
* and markdown at each other, and an opener may carry an info string where a
|
|
71
|
+
* closer may not.
|
|
72
|
+
*
|
|
73
|
+
* It returns the input UNCHANGED whenever the markup is malformed: a skip
|
|
74
|
+
* region left open, one closed without being opened, one nested inside
|
|
75
|
+
* another, or a fence left open. Partial output is the dangerous answer,
|
|
76
|
+
* because a governance section can go missing with nothing to notice it; a run
|
|
77
|
+
* that gets the whole file has only paid twice.
|
|
78
|
+
*
|
|
79
|
+
* The limits, so nobody relies on cover that is not here. A marker is
|
|
80
|
+
* recognised only as the first non-whitespace text on its own line — so one
|
|
81
|
+
* inside a blockquote is not a marker, and its text reaches the context. And
|
|
82
|
+
* fence-awareness is the only structure it knows: a BALANCED marker pair
|
|
83
|
+
* written inside an indented code block or an HTML comment is obeyed, so a
|
|
84
|
+
* document that demonstrates the markers loses the lines between them. Show
|
|
85
|
+
* them inside a fence.
|
|
86
|
+
*/
|
|
87
|
+
export function excerptAutonomy(markdown) {
|
|
88
|
+
const lines = markdown.split('\n');
|
|
89
|
+
const kept = [];
|
|
90
|
+
let skipping = false;
|
|
91
|
+
let fence = null;
|
|
92
|
+
let malformed = false;
|
|
93
|
+
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
const run = fenceRun(line);
|
|
96
|
+
if (fence) {
|
|
97
|
+
if (run && run.char === fence.char && run.length >= fence.length && run.bare) fence = null;
|
|
98
|
+
} else if (run) {
|
|
99
|
+
fence = run;
|
|
100
|
+
} else {
|
|
101
|
+
const trimmed = line.trim();
|
|
102
|
+
// Matched by prefix: a marker with something after it is still a marker,
|
|
103
|
+
// and the whole line goes. Requiring the line to be exactly the marker
|
|
104
|
+
// would let `<!-- inject:skip --> note` open nothing and then print
|
|
105
|
+
// itself into the context.
|
|
106
|
+
if (trimmed.startsWith(SKIP_OPEN)) {
|
|
107
|
+
// A second open inside a region is as much a mistake as a stray close,
|
|
108
|
+
// and the two are the same signal: the markers do not pair up.
|
|
109
|
+
malformed ||= skipping;
|
|
110
|
+
skipping = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (trimmed.startsWith(SKIP_CLOSE)) {
|
|
114
|
+
malformed ||= !skipping;
|
|
115
|
+
skipping = false;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!skipping) kept.push(line);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (fence || skipping || malformed) return markdown;
|
|
123
|
+
const excerpt = kept.join('\n').trim();
|
|
124
|
+
// Nothing left is the largest possible version of "partial output", and the
|
|
125
|
+
// marker pairing cannot see it: a balanced pair around the whole file is
|
|
126
|
+
// well-formed. A zero, not a threshold — this is deliberately not the byte
|
|
127
|
+
// ratio an earlier round used and deleted.
|
|
128
|
+
return excerpt === '' ? markdown : excerpt;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether this file is being run as a script rather than imported.
|
|
133
|
+
*
|
|
134
|
+
* The realpath on both sides is the point: ESM resolves `import.meta.url`
|
|
135
|
+
* through symlinks while `process.argv[1]` keeps the path as typed, so a
|
|
136
|
+
* project under a symlinked directory — a macOS temp dir, a symlinked home, a
|
|
137
|
+
* checkout behind a link — fails a naive equality check. The hook would then
|
|
138
|
+
* print nothing and exit 0, which reads exactly like a healthy session.
|
|
139
|
+
*/
|
|
140
|
+
function invokedDirectly() {
|
|
141
|
+
if (!process.argv[1]) return false;
|
|
142
|
+
const real = (p) => {
|
|
143
|
+
try {
|
|
144
|
+
return realpathSync(p);
|
|
145
|
+
} catch {
|
|
146
|
+
return p;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return real(fileURLToPath(import.meta.url)) === real(process.argv[1]);
|
|
150
|
+
}
|
|
15
151
|
|
|
16
152
|
function main() {
|
|
17
153
|
let input;
|
|
@@ -22,15 +158,47 @@ function main() {
|
|
|
22
158
|
}
|
|
23
159
|
if (input.hook_event_name !== 'SessionStart') return 0;
|
|
24
160
|
|
|
161
|
+
let rules;
|
|
25
162
|
try {
|
|
26
|
-
|
|
27
|
-
process.stdout.write(
|
|
28
|
-
`[agent-os] Autonomy rules refresh — in force regardless of compaction:\n\n${rules}\n`,
|
|
29
|
-
);
|
|
163
|
+
rules = readFileSync(new URL('../rules/autonomy.md', import.meta.url), 'utf8');
|
|
30
164
|
} catch {
|
|
31
165
|
// no rules file — nothing to inject, never an error
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The cut is the only thing allowed to fail here, and its failure resolves
|
|
170
|
+
// the way every other ambiguity in this file does: inject more. Leaving it
|
|
171
|
+
// inside the read's catch meant a throw in the excerpter printed NOTHING and
|
|
172
|
+
// exited 0 — the rules gone, and the session looking healthy.
|
|
173
|
+
let body;
|
|
174
|
+
try {
|
|
175
|
+
body = excerptAutonomy(rules);
|
|
176
|
+
} catch {
|
|
177
|
+
body = rules;
|
|
32
178
|
}
|
|
179
|
+
|
|
180
|
+
// The banner reports what happened, so it cannot be written once and assumed:
|
|
181
|
+
// on the fallback path nothing was removed, and telling a session to go read
|
|
182
|
+
// four sections it is already holding is the same kind of false report the
|
|
183
|
+
// cut itself is built to avoid.
|
|
184
|
+
// Compared trimmed, because the two paths differ by a trailing newline that
|
|
185
|
+
// is not a removal: the excerpt is trimmed and the fallback is not. A rules
|
|
186
|
+
// file that marks nothing would otherwise be announced as an excerpt — which
|
|
187
|
+
// is the case a project re-scoping its own rulebook lands in, every session.
|
|
188
|
+
const notice =
|
|
189
|
+
body.trim() === rules.trim()
|
|
190
|
+
? 'This is `.claude/rules/autonomy.md` in full.\n\n'
|
|
191
|
+
: 'This is `.claude/rules/autonomy.md` with the sections it marks as ' +
|
|
192
|
+
'reference removed — read the file itself for those: how the Tier-2 ' +
|
|
193
|
+
'gate is swept from outside, how external work is reconciled, ' +
|
|
194
|
+
'post-deploy verification, and the escalation format.\n\n';
|
|
195
|
+
|
|
196
|
+
process.stdout.write(
|
|
197
|
+
`[agent-os] Autonomy rules refresh — in force regardless of compaction.\n${notice}${body}\n`,
|
|
198
|
+
);
|
|
33
199
|
return 0;
|
|
34
200
|
}
|
|
35
201
|
|
|
36
|
-
|
|
202
|
+
if (invokedDirectly()) {
|
|
203
|
+
process.exit(main());
|
|
204
|
+
}
|