create-agent-rig 0.3.2 → 0.5.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 +294 -10
- package/README.md +84 -13
- package/package.json +2 -1
- package/packages/cli/dist/commands/create.js +41 -8
- package/packages/cli/dist/commands/init.js +120 -34
- package/packages/cli/dist/commands/upgrade.js +382 -0
- package/packages/cli/dist/index.js +132 -21
- package/packages/cli/dist/lib/copy-tree.js +44 -7
- package/packages/cli/dist/lib/history.js +49 -0
- package/packages/cli/dist/lib/init-settings.js +12 -0
- package/packages/cli/dist/lib/install-set.js +44 -0
- package/packages/cli/dist/lib/manifest.js +111 -0
- package/packages/cli/dist/lib/prompts.js +20 -0
- package/packages/cli/dist/lib/safe-path.js +71 -0
- package/packages/cli/dist/lib/substitute.js +32 -0
- package/packages/cli/dist/lib/version.js +15 -0
- package/templates/agent-os/init/AGENTS.md +191 -0
- package/templates/agent-os/init/CLAUDE.md +61 -9
- package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +98 -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 +34 -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 +34 -0
- package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +4 -0
- package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +233 -0
- package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +676 -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 +338 -0
- package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +73 -0
- package/templates/agent-os/universal/.claude/agents/code-reviewer.md +33 -1
- package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +64 -1
- package/templates/agent-os/universal/.claude/agents/security-scanner.md +33 -0
- package/templates/agent-os/universal/.claude/hooks/gate-stop-dod.mjs +274 -24
- 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-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 +480 -0
- package/templates/agent-os/universal/.claude/rules/autonomy.md +61 -1
- package/templates/agent-os/universal/.claude/rules/invariants.md +71 -16
- package/templates/agent-os/universal/.claude/rules/workflow.md +55 -5
- package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1242 -0
- package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +65 -2
- package/templates/agent-os/universal/.claude/scripts/git-env.mjs +49 -0
- package/templates/agent-os/universal/.claude/scripts/lib/secrets.mjs +490 -0
- package/templates/agent-os/universal/.claude/scripts/lib/verdict.mjs +433 -0
- package/templates/agent-os/universal/.claude/scripts/preflight.mjs +6 -16
- package/templates/agent-os/universal/.claude/scripts/queue/checkout.mjs +89 -0
- package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +334 -23
- package/templates/agent-os/universal/.claude/scripts/queue/gate-rounds.mjs +160 -0
- package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +7 -2
- package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +413 -8
- package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +75 -13
- package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +222 -21
- package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +197 -0
- package/templates/agent-os/universal/.claude/scripts/run-journal.mjs +435 -0
- package/templates/agent-os/universal/.claude/scripts/run-state.mjs +441 -0
- package/templates/agent-os/universal/.claude/scripts/verdict.mjs +101 -0
- package/templates/agent-os/universal/.claude/settings.json +7 -2
- package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +115 -7
- package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +395 -33
- package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +300 -25
- 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 +65 -0
- package/templates/agent-os/universal/AGENTS.md +164 -0
- package/templates/agent-os/universal/CLAUDE.md +47 -14
- package/templates/agent-os/universal/PLAN.md +7 -29
- 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/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 +62 -0
- package/templates/agent-os/universal/journal/README.md +101 -0
- package/templates/agent-os/universal/layers.json +36 -2
- package/templates/hash-history.json +264 -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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,243 @@ the generator.
|
|
|
7
7
|
Versions are published to npm as [`create-agent-rig`](https://www.npmjs.com/package/create-agent-rig);
|
|
8
8
|
`npx github:serhii-baksheiev/create-agent-rig` keeps working for either path.
|
|
9
9
|
|
|
10
|
+
Numbering is ordinary semver — **additive is a minor, a fix is a patch** — so
|
|
11
|
+
that "I only take minors" remains a usable policy; 0.3.2 shipped additive
|
|
12
|
+
content as a patch by the owner's call and stays recorded as one.
|
|
13
|
+
|
|
14
|
+
## 0.5.0
|
|
15
|
+
|
|
16
|
+
**Codex is a harness of this rig now, not a thing you adapt it to.** A generated
|
|
17
|
+
project carries one rulebook and two readers: `CLAUDE.md` for Claude Code and
|
|
18
|
+
the same text as `AGENTS.md` for Codex, with repository skills in
|
|
19
|
+
`.agents/skills/`, agent profiles in `.codex/agents/` and portable hook wiring
|
|
20
|
+
in `.codex/hooks.json`. Neither harness gets the weaker policy, and the derived
|
|
21
|
+
half is drift-checked rather than maintained twice.
|
|
22
|
+
|
|
23
|
+
**This release ships untagged, by the owner's decision, and it has exactly one
|
|
24
|
+
consequence — read it if you ever run `upgrade` on a rig whose
|
|
25
|
+
`.claude/.rig-manifest.json` is missing or unreadable.**
|
|
26
|
+
`templates/hash-history.json`, the table such a rig is measured against, is
|
|
27
|
+
built from `v*` tags. So 0.5.0's bytes never enter it, and the `0.4.0` row it
|
|
28
|
+
gained in this release carries the _previous_ release's bytes: that tag points at
|
|
29
|
+
0.3.2's content, which is why the row adds no hash to any path. Both paths 0.4.0
|
|
30
|
+
actually changed — `.claude/skills/loop/SKILL.md` and `PLAN.md` — are in the
|
|
31
|
+
table with their 0.3.x hashes; what is absent is 0.4.0's bytes from their hash
|
|
32
|
+
lists.
|
|
33
|
+
|
|
34
|
+
**Who that reaches, and who it does not.** `create`, `init` and `upgrade` each
|
|
35
|
+
write the manifest, and `upgrade` matches it **before** it consults the table, so
|
|
36
|
+
a rig whose manifest is present and parseable is unaffected whatever the table
|
|
37
|
+
says. Without a readable manifest the table decides, and it decides in the
|
|
38
|
+
conservative direction: bytes it recognises are replaced, bytes it does not are
|
|
39
|
+
kept and reported as yours — see `packages/cli/test/upgrade.test.ts` › "replaces
|
|
40
|
+
a file that matches a released version, and reports the rest".
|
|
41
|
+
|
|
42
|
+
A rig installed **before** 0.4.0 is not the exposed case: its 0.3.x bytes are in
|
|
43
|
+
the table, so those two files are recognised and replaced. The rig that keeps
|
|
44
|
+
them is one installed at **0.4.0** whose manifest is unreadable.
|
|
45
|
+
|
|
46
|
+
**And the scale of it grows with this release, which matters more than those two
|
|
47
|
+
files.** Because 0.5.0 is untagged, nothing it ships enters the table either — so
|
|
48
|
+
a rig installed at 0.5.0 and later upgraded **without a readable manifest** has
|
|
49
|
+
most of its agent-os files unrecognised, and many of them are paths the table has
|
|
50
|
+
no row for at all. Every one is kept and reported as yours, so no edit is lost
|
|
51
|
+
and no file is silently skipped, but almost nothing would be refreshed either. **Commit `.claude/.rig-manifest.json`** — that single habit makes the
|
|
52
|
+
table irrelevant to you, and it is what `README.md` puts in bold.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- **Codex is now a native target of the Agent OS.** Generated and `init`ed
|
|
57
|
+
projects receive `AGENTS.md`, repository skills in `.agents/skills/`, custom
|
|
58
|
+
agent profiles in `.codex/agents/`, and portable `.codex/hooks.json` wiring.
|
|
59
|
+
These files are derived from the Claude Code sources and drift-checked.
|
|
60
|
+
- Architecture guards now understand Codex `apply_patch` payloads, inspecting
|
|
61
|
+
additions and bounded existing content for moves, so removing an old
|
|
62
|
+
violation does not create a false block.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- **`upgrade` now replaces `.claude/settings.json` when the manifest proves you
|
|
67
|
+
never touched it** — closing the decision 0.4.0's notes left open below. The
|
|
68
|
+
case it exists for is a release that adds a hook: the hook file arrived and
|
|
69
|
+
the wiring that calls it did not, so the guard sat on disk doing nothing. When
|
|
70
|
+
the on-disk bytes hash-match the entry the manifest recorded for the installed
|
|
71
|
+
release, they are provably the rig's own and the release's version is written.
|
|
72
|
+
|
|
73
|
+
**Three limits. The first two are there because not having them was tried,
|
|
74
|
+
and each produced a regression two reviewers reproduced independently.**
|
|
75
|
+
|
|
76
|
+
1. **The released-hash fallback does not apply to this file.** Every other
|
|
77
|
+
file the rig installed can be recognised by matching a tagged release even
|
|
78
|
+
with no manifest entry. This one cannot: a rig with no manifest that has
|
|
79
|
+
run `init` is recorded as `kind: "init"`, and the wiring that flavour
|
|
80
|
+
writes deliberately omits the hooks `init` does not install.
|
|
81
|
+
2. **A replacement that would stop calling a hook still present in
|
|
82
|
+
`.claude/hooks/` is handed over instead**, whatever the manifest says. This
|
|
83
|
+
is the guard that does not depend on getting `kind` right — a manifest
|
|
84
|
+
saying `init` on a rig `create` produced reaches the same wrong wiring
|
|
85
|
+
through the hash arm alone.
|
|
86
|
+
3. **Anything else is unchanged:** the new entries are printed for you to
|
|
87
|
+
merge, and nothing is written.
|
|
88
|
+
|
|
89
|
+
- **`init --force` is deprecated.** It refuses, names `upgrade` as the command
|
|
90
|
+
that refreshes a rig, and writes nothing. It only ever replaced `CLAUDE.md`,
|
|
91
|
+
which `upgrade` now does per file and with the manifest behind it. **The flag
|
|
92
|
+
is removed in 0.6** — this release is the one warning you get.
|
|
93
|
+
|
|
94
|
+
The way into a `create` rig that `--force` used to provide is a deleted
|
|
95
|
+
`CLAUDE.md`; that is what `init`'s refusal is actually about, and it is the
|
|
96
|
+
case the manifest-preserving fix below was written for.
|
|
97
|
+
|
|
98
|
+
### Security
|
|
99
|
+
|
|
100
|
+
- 🔴 **A committed `.claude/.rig-manifest.json` could run code on the machine
|
|
101
|
+
of whoever upgraded the rig.** `project.name`, `project.scope`,
|
|
102
|
+
`project.region` and `stacks` were each validated — but only as _path_
|
|
103
|
+
segments, a predicate that asks whether a value can steer a write. Two of
|
|
104
|
+
them are also substituted into installed **files**:
|
|
105
|
+
`.claude/scripts/stop-flag.mjs` embeds the name inside a single-quoted
|
|
106
|
+
JavaScript string literal that `guard-bash` imports on every Bash call. A
|
|
107
|
+
value closing that quote steers no path at all and passed — it executed in
|
|
108
|
+
the hook process, **and** moved the kill switch's path off
|
|
109
|
+
`~/.claude/<name>-loop-STOP`, so the brake read as installed while doing
|
|
110
|
+
nothing. The manifest travels in pull requests, so the delivery was an
|
|
111
|
+
ordinary PR plus an `upgrade`. All four are now held to the shape the rig
|
|
112
|
+
actually produces (`^[a-z0-9_][a-z0-9._-]*$`), and a manifest carrying
|
|
113
|
+
anything else is void as a whole rather than corrected.
|
|
114
|
+
|
|
115
|
+
**Checking a rig you upgraded from a manifest you did not write — three
|
|
116
|
+
places, because the name is not the only value that travelled.**
|
|
117
|
+
`.claude/scripts/stop-flag.mjs` is the executable sink: its kill-switch line
|
|
118
|
+
must read your own project name. `region` lands in
|
|
119
|
+
`.claude/skills/ro-debug/SKILL.md` as `export AWS_REGION=…` on rigs carrying
|
|
120
|
+
the `aws-cdk` overlay — and a manifest also declares `stacks`, so it can
|
|
121
|
+
request that overlay on a rig that never had it. The name is substituted into
|
|
122
|
+
the documents the agent obeys as well (`CLAUDE.md`, `PLAN.md`, the `loop`
|
|
123
|
+
skill), where a hostile value arrives as injected text rather than as code.
|
|
124
|
+
|
|
125
|
+
Nothing `create` or `init` writes is rejected by the new rule — including an
|
|
126
|
+
empty `region` and a name with a leading underscore, which
|
|
127
|
+
`projectNameFor` really can produce.
|
|
128
|
+
|
|
129
|
+
### Fixed
|
|
130
|
+
|
|
131
|
+
- **`init --force` inside a generated project used to make `upgrade` stop
|
|
132
|
+
refreshing the stack overlays — silently.** ⚠ Read this next to the
|
|
133
|
+
deprecation above: `--force` is refused in this same release, so
|
|
134
|
+
the route described here is gone. The fix is not idle — the manifest is
|
|
135
|
+
preserved on **every** `init` over a `create` rig, and the remaining route in
|
|
136
|
+
is a deleted `CLAUDE.md`. `init` rewrote the rig manifest
|
|
137
|
+
as `kind: "init"`, `stacks: []`, empty `region`, and `upgrade` trusts a
|
|
138
|
+
manifest wholesale rather than re-detecting: the stack files simply left the
|
|
139
|
+
plan, reported neither as deleted nor as a conflict, and `CLAUDE.md` came
|
|
140
|
+
back in the `init` flavour. `init` now carries the `kind`, `project` and
|
|
141
|
+
`stacks` it found in the manifest through unchanged, and adds an entry for
|
|
142
|
+
each file it wrote without dropping the entries already there. It also says,
|
|
143
|
+
before writing anything, that this rig came from `create` and `upgrade` is
|
|
144
|
+
the command that refreshes it.
|
|
145
|
+
|
|
146
|
+
⚠ **Both halves read the manifest, so a rig that has none — anything
|
|
147
|
+
installed before 0.4.0 — is not covered.** There `init` still writes
|
|
148
|
+
`kind: "init"`, `stacks: []`, empty `region`, and prints no advisory; worse,
|
|
149
|
+
such a rig could previously be recovered by `upgrade`, which re-detects the
|
|
150
|
+
install from the files on disk **only when there is no manifest at all**, and
|
|
151
|
+
the one `init` writes takes that route away. On a pre-0.4.0 rig, run
|
|
152
|
+
`upgrade` before `init`.
|
|
153
|
+
|
|
154
|
+
**Recovering a rig whose manifest was already flattened:** delete
|
|
155
|
+
`.claude/.rig-manifest.json` and run `upgrade` — the detection restores
|
|
156
|
+
`kind`, `stacks` and `region` from the files themselves; hand-writing the
|
|
157
|
+
manifest is not needed and `parseManifest` rejects the whole file on any
|
|
158
|
+
malformed field. What that does **not** repair is `CLAUDE.md`: the flattening
|
|
159
|
+
`init` overwrote it with the `init` flavour, so `upgrade` reports it as
|
|
160
|
+
`conflict` ("not a version this rig ever released — treated as yours") and
|
|
161
|
+
the create flavour has to be merged back by hand.
|
|
162
|
+
|
|
163
|
+
- **The `jira` queue adapter was calling an endpoint Atlassian removed.** Both
|
|
164
|
+
selection and the triage dedupe went through `GET /rest/api/3/search`, which
|
|
165
|
+
answers `410 Gone`; the adapter threw on the status line and the loop read
|
|
166
|
+
that as an unreadable queue. It now uses `POST /rest/api/3/search/jql`. If
|
|
167
|
+
your rig is on the `jira` adapter, this is the difference between a loop that
|
|
168
|
+
works and one that reports an empty board. Cursor pagination
|
|
169
|
+
(`nextPageToken`) is **not** implemented yet, so a board with more open issues
|
|
170
|
+
than `limit` (default 100) still loses its tail.
|
|
171
|
+
|
|
172
|
+
### Changed — action needed if your board uses the `jira` adapter
|
|
173
|
+
|
|
174
|
+
- 🔴 **The elevated-tier marker on Jira is now the `elevated` label, not
|
|
175
|
+
`human-review`.** A board that marked elevated work with `human-review` will,
|
|
176
|
+
after this upgrade, hand every item to the loop as `normal` — the
|
|
177
|
+
elevated-spacing ration silently stops holding anything back. **Relabel those
|
|
178
|
+
issues to `elevated` before running the loop again.** The change is
|
|
179
|
+
deliberate: on a Jira board `human-review` reads as "a human is looking at
|
|
180
|
+
it", which is a different claim from "this change is expensive to reverse".
|
|
181
|
+
The `github-issues` adapter is unaffected and still reads `human-review`,
|
|
182
|
+
where it does mean a human reviewed the diff.
|
|
183
|
+
- **Selection now excludes the `operator-queue` label as well as `triage`.** An
|
|
184
|
+
item in the owner's lane is work a human has taken, so the loop no longer
|
|
185
|
+
picks one up. If you used `operator-queue` for something else, rename it
|
|
186
|
+
first.
|
|
187
|
+
|
|
188
|
+
## 0.4.0
|
|
189
|
+
|
|
190
|
+
Upgrading is a command now: **`npx create-agent-rig@0.4.0 upgrade`** (`@latest`
|
|
191
|
+
once you know what latest is — this section will not). That sentence replaces
|
|
192
|
+
the six-file manual procedure 0.3.2 had to print, and it is the whole point of
|
|
193
|
+
this release: a rig you cannot bring forward stops being maintained at whatever
|
|
194
|
+
version you installed it at.
|
|
195
|
+
|
|
196
|
+
Read the [Upgrading](README.md#upgrading-a-rig-you-already-have) section before
|
|
197
|
+
the first run on an existing rig; `--dry-run` prints the plan and writes
|
|
198
|
+
nothing.
|
|
199
|
+
|
|
200
|
+
### Added
|
|
201
|
+
|
|
202
|
+
- **`create-agent-rig upgrade [--dry-run] [--yes]`** — brings an installed rig
|
|
203
|
+
to this version: it replaces the files the rig wrote **and you have not
|
|
204
|
+
touched**, installs what the release adds, and **reports everything else**.
|
|
205
|
+
There is no three-way merge and no patching, by decision rather than
|
|
206
|
+
omission: silently folding your edits into the documents an agent loop obeys
|
|
207
|
+
is how a rig stops meaning what its owner thinks it means. Every conflict
|
|
208
|
+
names the file, why it was kept, and the path to the new version, so the diff
|
|
209
|
+
you may want is one command away.
|
|
210
|
+
- **`.claude/.rig-manifest.json`** — written by `create` and by `init`: the rig
|
|
211
|
+
version and a hash per installed file. **Commit it.** It is what lets an
|
|
212
|
+
upgrade tell a file the rig wrote from a file you own, and without it in the
|
|
213
|
+
repository the command is blind on CI and on a colleague's machine. `init`
|
|
214
|
+
records only files it actually wrote — never one it kept, which would be
|
|
215
|
+
claiming somebody else's document.
|
|
216
|
+
- **A released-hash table travels in the package**, generated from the git tags
|
|
217
|
+
at release time and never by hand. It is what makes a 0.3.x rig upgradable at
|
|
218
|
+
all, and it answers a second question too: a file that shipped in every
|
|
219
|
+
release it covers and is gone from disk was **deleted on purpose**, so it
|
|
220
|
+
stays deleted. The rules tell you to delete the invariants your project does
|
|
221
|
+
not have; an upgrade that quietly restored them would be undoing your work.
|
|
222
|
+
- **The `loop` skill writes back what a close unblocked**, in a required
|
|
223
|
+
journal field with three distinct answers — the items that were waiting, by
|
|
224
|
+
name; "nothing was waiting"; or "this queue has no dependency links" where
|
|
225
|
+
the adapter cannot answer at all. It is a **report, not an edit** to those
|
|
226
|
+
items: correcting queue state by hand destroys the evidence that the state is
|
|
227
|
+
unreliable, which the rules forbid by name.
|
|
228
|
+
|
|
229
|
+
### Two things `upgrade` deliberately will not do
|
|
230
|
+
|
|
231
|
+
- **Replace `.claude/settings.json`.** It is where your own hooks live, so the
|
|
232
|
+
new wiring is printed for you to merge — in the dry run too. The cost is
|
|
233
|
+
real and stated: a release that adds a hook delivers the file and not its
|
|
234
|
+
wiring, and whether a manifest-proven-unmodified settings file should be
|
|
235
|
+
refreshed is an open decision for 0.5.
|
|
236
|
+
- **Touch the skeleton.** After `create`, the code is your project. The manifest
|
|
237
|
+
covers the agent-os layer and nothing else.
|
|
238
|
+
|
|
239
|
+
### Deferred, and on what condition
|
|
240
|
+
|
|
241
|
+
- The clarify-gate (`C-0…C-2`) — unchanged from 0.3.2: it enters once that gate
|
|
242
|
+
has fired at least once anywhere. Until then there is nothing to copy but an
|
|
243
|
+
intention.
|
|
244
|
+
- `init --force` now overlaps `upgrade`, and its future is an open question
|
|
245
|
+
rather than a deprecation: decided in 0.5, unchanged here.
|
|
246
|
+
|
|
10
247
|
## 0.3.2
|
|
11
248
|
|
|
12
249
|
Numbered as a patch by the owner's call; the content below is additive, so
|
|
@@ -35,10 +272,14 @@ will not touch them:
|
|
|
35
272
|
.claude/hooks/gate-stop-dod.mjs # judges the tree it is in
|
|
36
273
|
```
|
|
37
274
|
|
|
38
|
-
Delete those six and re-run `init`, or copy them across by hand.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
275
|
+
Delete those six and re-run `init`, or copy them across by hand. This note tells
|
|
276
|
+
you the manual steps rather than an easy sentence that leaves half the release
|
|
277
|
+
inert — that failure mode is the whole subject of 0.3.1, immediately below.
|
|
278
|
+
|
|
279
|
+
> **Superseded in 0.4.0.** `create-agent-rig upgrade` delivers exactly these
|
|
280
|
+
> files, and the ones every release after it changes. The procedure above is
|
|
281
|
+
> kept as the record of what 0.3.2 asked of its users; do not follow it if you
|
|
282
|
+
> have 0.4.0 or later.
|
|
42
283
|
|
|
43
284
|
### Added
|
|
44
285
|
|
|
@@ -256,7 +497,8 @@ layers, and the `aws-serverless` and `node-service` targets.
|
|
|
256
497
|
## Releasing
|
|
257
498
|
|
|
258
499
|
`npm publish` needs 2FA and cannot be undone, so an agent prepares a release and
|
|
259
|
-
**stops at
|
|
500
|
+
**stops at the first step it is not allowed to take** — normally `npm publish`,
|
|
501
|
+
sometimes earlier (step 6). Everything before that is mechanical:
|
|
260
502
|
|
|
261
503
|
1. `pnpm test` — the full suite, including the e2e that generates both targets
|
|
262
504
|
cold and runs their own checks through the git path **and** the pack path.
|
|
@@ -264,8 +506,50 @@ layers, and the `aws-serverless` and `node-service` targets.
|
|
|
264
506
|
tree, are in the tarball. This is where scaffolders break, and the git path
|
|
265
507
|
cannot catch it.
|
|
266
508
|
3. Version in `package.json` (and the private inner package, kept in step).
|
|
267
|
-
4.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
509
|
+
4. `node scripts/build-hash-history.mjs` — regenerate the released-hash table
|
|
510
|
+
from the tags **after** the version bump, so the version now shipping is the
|
|
511
|
+
first one it excludes. Forgetting it would leave `upgrade` unable to
|
|
512
|
+
recognise the previous release. ⚠ With no tag cut (step 7), what excludes the
|
|
513
|
+
shipping version is the absent tag rather than that filter — and the same
|
|
514
|
+
absence keeps it out of every later table too, which is the cost step 7
|
|
515
|
+
states.
|
|
516
|
+
5. This file, and `PLAN.md` if the plan's claims changed.
|
|
517
|
+
6. **`pnpm test` again — this run, not step 1, is the one that can catch a
|
|
518
|
+
stale hash table.** The check compares the table against the versions this
|
|
519
|
+
file lists below the one in `package.json`, so before steps 3–5 it is
|
|
520
|
+
comparing the _old_ release to the _old_ table and passes either way. A
|
|
521
|
+
guard that can only fire after the thing it guards has changed has to be run
|
|
522
|
+
after it.
|
|
523
|
+
7. **Tagging is not part of this project's release process** — standing owner
|
|
524
|
+
decision, recorded at 0.5.0: the owner publishes by hand and does not tag. So
|
|
525
|
+
this step is deliberately _not_ performed, and the cost is stated here rather
|
|
526
|
+
than discovered later.
|
|
527
|
+
|
|
528
|
+
Step 4 builds the table from `v*` tags, so an untagged release never enters
|
|
529
|
+
it. That is free for the release being prepared and **not free for the one
|
|
530
|
+
after it**: step 6 fails as soon as the CHANGELOG lists an untagged release
|
|
531
|
+
_below_ the version being prepared, and its message says "stale table", which
|
|
532
|
+
running the builder cannot satisfy. Measured against this repository at
|
|
533
|
+
0.5.0 preparation: with `0.5.0` untagged, a `0.5.0` bump passes and an
|
|
534
|
+
`0.6.0` bump fails. The check is
|
|
535
|
+
`test/template/hash-history.test.ts` › "covers every released version below
|
|
536
|
+
the one being prepared". **AR-35 carries the fix**, and which shape it takes
|
|
537
|
+
is that item's to decide, not this note's.
|
|
538
|
+
|
|
539
|
+
**What a releaser may do when step 6 fails, stated so it is not inferred:**
|
|
540
|
+
stop, and land AR-35 first. Cutting the missing tag after the fact is _not_
|
|
541
|
+
the sanctioned way out — it would put the table's honesty back on a step this
|
|
542
|
+
process does not perform, which is the whole reason the decision is recorded
|
|
543
|
+
here.
|
|
544
|
+
|
|
545
|
+
If a tag is ever cut anyway, the older warning still applies: check first
|
|
546
|
+
that it does not exist (`git ls-remote --tags origin`), because a leftover
|
|
547
|
+
from an abandoned attempt is a published ref, deleting or moving one is an
|
|
548
|
+
**owner** action, and a tag on the wrong commit makes the next table **name a
|
|
549
|
+
version whose bytes it does not carry**. `v0.4.0` is in exactly that state —
|
|
550
|
+
it points at 0.3.2's content.
|
|
551
|
+
|
|
552
|
+
8. **Owner:** `npm publish`.
|
|
553
|
+
9. **Owner:** smoke the published artifact — `npx create-agent-rig@<version>` in
|
|
554
|
+
an empty directory, then `pnpm install && pnpm check` inside it; and
|
|
555
|
+
`upgrade --dry-run` in a rig installed from the previous version.
|
package/README.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Scaffold a project that ships with an **agent operating system** — rules,
|
|
4
4
|
gates, and hooks that hold the architecture mechanically, not by prose.
|
|
5
5
|
|
|
6
|
+
The same Agent OS is native to both **Claude Code and Codex**. Claude-facing
|
|
7
|
+
files remain the authoring surface; the generator derives Codex's `AGENTS.md`,
|
|
8
|
+
repository skills under `.agents/skills/`, custom agents under `.codex/agents/`,
|
|
9
|
+
and `.codex/hooks.json`. `node scripts/sync-codex-adapter.mjs --check` refuses
|
|
10
|
+
drift between the two projections.
|
|
11
|
+
|
|
6
12
|
```sh
|
|
7
13
|
npx create-agent-rig my-app # choose a target interactively
|
|
8
14
|
npx create-agent-rig my-app --target node-service # or name it up front
|
|
@@ -22,28 +28,91 @@ npx create-agent-rig init --dry-run # print the plan, write nothing
|
|
|
22
28
|
```
|
|
23
29
|
|
|
24
30
|
`init` drops in the autonomy tiers, stop rules, workflow, and the enforcement
|
|
25
|
-
hooks — **wired
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
hook
|
|
31
|
+
hooks — **wired** for both harnesses, in `.claude/settings.json` and
|
|
32
|
+
`.codex/hooks.json`, each naming exactly the hooks it installed — plus matching
|
|
33
|
+
`CLAUDE.md` and `AGENTS.md` maps that describe that rig rather than the generated
|
|
34
|
+
monorepo. It refuses to clobber either existing map; if the repo already has a
|
|
35
|
+
Claude or Codex hook config, it keeps it and prints the entries to merge,
|
|
36
|
+
because a hook nothing calls is not enforcement.
|
|
30
37
|
|
|
31
38
|
Two things it deliberately leaves to you, and says so in the installed
|
|
32
|
-
|
|
39
|
+
maps: the Definition-of-Done gate has no `dod-checks.json` (it cannot know
|
|
33
40
|
your commands), and the elevated-path list names only what every repo has.
|
|
34
41
|
|
|
42
|
+
After generation or upgrade, review the checked-in `.codex/hooks.json` in Codex's
|
|
43
|
+
`/hooks` view and explicitly trust it if Codex presents a trust prompt. The
|
|
44
|
+
[official Codex hooks documentation](https://learn.chatgpt.com/docs/hooks)
|
|
45
|
+
records trust against the current hook hash, so a changed hook definition may
|
|
46
|
+
require that review again; the adapter does not silently replace user-owned hook
|
|
47
|
+
configuration.
|
|
48
|
+
|
|
49
|
+
## Upgrading a rig you already have
|
|
50
|
+
|
|
51
|
+
A release changes files, and `init` only ever _adds_ — so bringing an existing
|
|
52
|
+
rig forward is its own command:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npx create-agent-rig@latest upgrade --dry-run # print the plan, write nothing
|
|
56
|
+
npx create-agent-rig@latest upgrade # print the plan, then ask before writing
|
|
57
|
+
npx create-agent-rig@latest upgrade --yes # the answer up front (required off a terminal)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It replaces the files the rig installed **and you have not touched**, installs
|
|
61
|
+
what the release added, and **reports everything else** — no three-way merge, no
|
|
62
|
+
patching. Silently merging your edits into the documents an agent loop obeys is
|
|
63
|
+
how a rig quietly stops meaning what you think it means; a conflict report is how
|
|
64
|
+
it does not. Each conflict names the file, why it was kept, and the path to the
|
|
65
|
+
new version so you can diff it yourself.
|
|
66
|
+
|
|
67
|
+
How it knows: `create` and `init` write `.claude/.rig-manifest.json` — the rig
|
|
68
|
+
version plus a hash per installed file. **Commit it**; without it in the
|
|
69
|
+
repository the command is blind on CI and on a colleague's machine. Rigs
|
|
70
|
+
installed before 0.4.0 have no manifest, so the package also carries the hashes
|
|
71
|
+
of every **tagged** release (0.3.0 onward — 0.1.0 and 0.2.0 shipped untagged,
|
|
72
|
+
and a rig from those reports every file as yours) and recognises a file matching
|
|
73
|
+
one of them. Releases from 0.5.0 on ship untagged as well, so the table does not
|
|
74
|
+
carry them either: on a rig with no manifest most of their files are reported as yours
|
|
75
|
+
rather than refreshed. This is why committing the manifest is the sentence in
|
|
76
|
+
bold above and not an aside.
|
|
77
|
+
|
|
78
|
+
`.claude/settings.json` is replaced only when the manifest's recorded hash
|
|
79
|
+
proves the rig wrote those exact bytes and you have not touched them — the case
|
|
80
|
+
where a release adds a hook and the wiring that calls it. Anything else, and it
|
|
81
|
+
is where your own hooks live: the new wiring is printed for you to merge, never
|
|
82
|
+
written. Unlike every other file, a match against the released hashes is not
|
|
83
|
+
enough for this one, and a replacement that would stop calling a hook the
|
|
84
|
+
current wiring names — while that hook's file is still in `.claude/hooks/` — is
|
|
85
|
+
handed over instead.
|
|
86
|
+
|
|
87
|
+
**A file you deleted stays deleted.** The rules invite you to delete the ones
|
|
88
|
+
whose invariant your project does not have, so an upgrade that quietly restored
|
|
89
|
+
them would be undoing your work. With a manifest that is direct — it names the
|
|
90
|
+
file, the disk does not have it, and the manifest is _evidence_, not a command.
|
|
91
|
+
Without one, the shipped table answers instead: a file that was in every release
|
|
92
|
+
it covers was there to be removed. The single case nothing can tell apart is a
|
|
93
|
+
file a **later** release added, which your rig never had — that one is installed,
|
|
94
|
+
and `--dry-run` lists it before anything is written.
|
|
95
|
+
|
|
35
96
|
## What you get
|
|
36
97
|
|
|
37
98
|
**A system of boundaries, each held by tooling.** An agent (or a human using
|
|
38
99
|
one) cannot talk its way past them — each guard is a pre-write scan that stops
|
|
39
100
|
the normal path cold (review and tests back it; the claim is stated exactly,
|
|
40
|
-
never inflated). The
|
|
41
|
-
`.claude/settings.json`:
|
|
101
|
+
never inflated). The hook implementations live once in `.claude/hooks/` and are
|
|
102
|
+
wired by both `.claude/settings.json` and `.codex/hooks.json`:
|
|
42
103
|
|
|
43
104
|
- **`guard-core-purity`** — refuses any edit that puts I/O, clock, randomness,
|
|
44
105
|
environment access, or a non-allowlisted import into the pure domain core;
|
|
45
106
|
- **`guard-web-boundary`** — refuses `db`/service imports from the frontend;
|
|
46
107
|
the web talks to the backend over HTTP only;
|
|
108
|
+
- **`guard-secret-file`** — refuses an edit that writes a credential: either the
|
|
109
|
+
path names one (`jira.env`, `id_rsa`, anything under `secrets/`) or the text
|
|
110
|
+
carries a credential VALUE. Both arms read one vocabulary,
|
|
111
|
+
`.claude/scripts/lib/secrets.mjs`, and a refusal names the pattern and the line
|
|
112
|
+
and **never the matched value** — printing it would leak the secret in the act
|
|
113
|
+
of refusing it. Its four blind spots are in its own header, each naming the
|
|
114
|
+
test that pins it or saying plainly that none does — and those tests live in
|
|
115
|
+
this generator, not in the rig;
|
|
47
116
|
- **`block-no-verify`** — refuses bypassing pre-commit checks (and knows the
|
|
48
117
|
difference between using the `--no-verify`/`-n` flag and merely mentioning it
|
|
49
118
|
in a message);
|
|
@@ -58,7 +127,9 @@ never inflated). The hooks live in `.claude/hooks/` and are wired in
|
|
|
58
127
|
check is red; it fails open (a missing or corrupt config never makes the
|
|
59
128
|
session unquittable) and never blocks twice in a row;
|
|
60
129
|
- **`inject-rules`** — re-injects the autonomy rules at session start, so they
|
|
61
|
-
survive compaction and resumes
|
|
130
|
+
survive compaction and resumes: the whole file, minus the regions the file
|
|
131
|
+
itself marks as reference. What is left out is a decision written in
|
|
132
|
+
`autonomy.md` on the line above it, not one this hook infers.
|
|
62
133
|
|
|
63
134
|
**A brake that is a real file.** `touch ~/.claude/<project>-loop-STOP` and no
|
|
64
135
|
merge lands until it is removed — enforced at the tool layer, so it holds even if
|
|
@@ -87,8 +158,8 @@ never), **stop rules** (three strikes, flaky ≠ retry, session staleness),
|
|
|
87
158
|
`prose-reviewer`, and `cdk-diff-reviewer` on the AWS target), **skills** (`pr-ship` pre-merge gate;
|
|
88
159
|
`loop` queue driver; `worktree-task` for concurrent sessions; `new-invariant`, a
|
|
89
160
|
generator for the invariant→hook→test pattern; `post-deploy-verify` and
|
|
90
|
-
`ro-debug` on the AWS target), and
|
|
91
|
-
orients by.
|
|
161
|
+
`ro-debug` on the AWS target), and matching one-page `CLAUDE.md` / `AGENTS.md`
|
|
162
|
+
maps a fresh session orients by.
|
|
92
163
|
|
|
93
164
|
**The hooks are examples, not laws.** `.claude/rules/invariants.md` states the
|
|
94
165
|
pattern behind each one — a stated invariant, a mechanical check, a test for the
|
|
@@ -158,8 +229,8 @@ under test, because that is exactly where scaffolders break. A grep-test keeps
|
|
|
158
229
|
the universal rules free of any provider mention; the hook-blocking behavior
|
|
159
230
|
itself is under test; and a weekly lockfile-free run resolves each template's
|
|
160
231
|
dependencies fresh to catch upstream breakage early. This repo dogfoods its own
|
|
161
|
-
rulebook —
|
|
162
|
-
drift fails the suite.
|
|
232
|
+
rulebook — the Claude and Codex projections are composed from the templates,
|
|
233
|
+
and drift fails the suite.
|
|
163
234
|
|
|
164
235
|
**And the enforcement layer is adversarially reviewed, not just tested.** The
|
|
165
236
|
Bash guard went through four review rounds with ten reviewers, who executed it
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agent-rig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Scaffold a new project with an agent operating system (rules, gates, hooks) and a runnable code skeleton",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"create",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"template",
|
|
11
11
|
"agent",
|
|
12
12
|
"claude-code",
|
|
13
|
+
"codex",
|
|
13
14
|
"agent-os",
|
|
14
15
|
"aws-serverless",
|
|
15
16
|
"node-service"
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { mkdir, readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readFile, readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
|
-
import { copyTree, listTree } from '../lib/copy-tree.js';
|
|
5
|
+
import { copyTree, listTree, mapConcurrent } from '../lib/copy-tree.js';
|
|
6
6
|
import { ALLOWED_OVERWRITES, detectCollisions } from '../lib/composition.js';
|
|
7
|
+
import { agentOsLayerDirs } from '../lib/install-set.js';
|
|
8
|
+
import { sha256, writeManifest } from '../lib/manifest.js';
|
|
7
9
|
import { substituteContent, substituteFileName } from '../lib/substitute.js';
|
|
8
10
|
import { gitEnv } from '../lib/git-env.js';
|
|
9
11
|
import { DEFAULT_TARGET, TARGETS, TARGET_NAMES } from '../lib/targets.js';
|
|
10
|
-
import {
|
|
12
|
+
import { skeletonDir } from '../templates.js';
|
|
13
|
+
import { packageVersion } from '../lib/version.js';
|
|
11
14
|
/** A user-facing failure: message is printed as-is, no stack trace. */
|
|
12
15
|
export class CreateError extends Error {
|
|
13
16
|
}
|
|
@@ -36,13 +39,10 @@ export async function createProject(dirArg, options) {
|
|
|
36
39
|
transformName: (name) => substituteFileName(name, ctx),
|
|
37
40
|
};
|
|
38
41
|
// Layer 2 (the skeleton) + layer 1 (agent-os: universal + stack overlays).
|
|
42
|
+
const agentOsLayers = agentOsLayerDirs(target.stacks);
|
|
39
43
|
const layers = [
|
|
40
44
|
{ name: `skeleton/${target.skeletonDir}`, dir: skeletonDir(target.skeletonDir) },
|
|
41
|
-
|
|
42
|
-
...target.stacks.map((stack) => ({
|
|
43
|
-
name: `agent-os/stack/${stack}`,
|
|
44
|
-
dir: agentOsStackDir(stack),
|
|
45
|
-
})),
|
|
45
|
+
...agentOsLayers,
|
|
46
46
|
];
|
|
47
47
|
// Composition safety: layers must claim disjoint paths. Checked before any
|
|
48
48
|
// copy — a collision is a template bug and must never be resolved by order.
|
|
@@ -61,11 +61,44 @@ export async function createProject(dirArg, options) {
|
|
|
61
61
|
for (const layer of layers) {
|
|
62
62
|
await copyTree(layer.dir, projectDir, transforms);
|
|
63
63
|
}
|
|
64
|
+
await recordInstall(projectDir, agentOsLayers, transforms, ctx, targetName);
|
|
64
65
|
if (options.git !== false) {
|
|
65
66
|
await initGitBaseline(projectDir);
|
|
66
67
|
}
|
|
67
68
|
return { projectDir, projectName };
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Record the agent-os layer in `.claude/.rig-manifest.json`, so a later
|
|
72
|
+
* `upgrade` can tell a file the rig wrote from a file the project's own people
|
|
73
|
+
* changed.
|
|
74
|
+
*
|
|
75
|
+
* The skeleton is **not** recorded, and that is the boundary of the whole
|
|
76
|
+
* upgrade story: once generated, the code belongs to the project. The hashes
|
|
77
|
+
* are read back off the disk rather than recomputed, so the manifest states
|
|
78
|
+
* what is actually there and cannot drift from what was copied. The manifest
|
|
79
|
+
* lands before the baseline commit — it is part of the pristine template, and
|
|
80
|
+
* it belongs in the project's git history.
|
|
81
|
+
*/
|
|
82
|
+
async function recordInstall(projectDir, agentOsLayers, transforms, ctx, target) {
|
|
83
|
+
const files = {};
|
|
84
|
+
for (const layer of agentOsLayers) {
|
|
85
|
+
const paths = await listTree(layer.dir, transforms);
|
|
86
|
+
const hashes = await mapConcurrent(paths, 16, async (rel) => ({
|
|
87
|
+
rel,
|
|
88
|
+
hash: sha256(await readFile(path.join(projectDir, ...rel.split('/')), 'utf8')),
|
|
89
|
+
}));
|
|
90
|
+
for (const { rel, hash } of hashes) {
|
|
91
|
+
files[rel] = hash;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
await writeManifest(projectDir, {
|
|
95
|
+
version: await packageVersion(),
|
|
96
|
+
kind: 'create',
|
|
97
|
+
project: { name: ctx.projectName, scope: ctx.projectScope, region: ctx.region },
|
|
98
|
+
stacks: [...(TARGETS[target]?.stacks ?? [])],
|
|
99
|
+
files,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
69
102
|
const run = promisify(execFile);
|
|
70
103
|
async function initGitBaseline(projectDir) {
|
|
71
104
|
// Disable git's background maintenance for these one-shot commands: a commit
|