create-agent-rig 0.9.1 → 0.10.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 (234) hide show
  1. package/CHANGELOG.md +419 -3
  2. package/README.md +267 -305
  3. package/package.json +8 -14
  4. package/packages/cli/dist/commands/create.js +56 -88
  5. package/packages/cli/dist/commands/doctor.js +213 -0
  6. package/packages/cli/dist/commands/init.js +111 -50
  7. package/packages/cli/dist/commands/integrations.js +468 -0
  8. package/packages/cli/dist/commands/setup-wizard.js +61 -0
  9. package/packages/cli/dist/commands/uninstall.js +1223 -0
  10. package/packages/cli/dist/commands/upgrade.js +472 -90
  11. package/packages/cli/dist/index.js +651 -46
  12. package/packages/cli/dist/integrations/declaration.js +158 -0
  13. package/packages/cli/dist/integrations/doctor-guards.js +126 -0
  14. package/packages/cli/dist/integrations/doctor-workflow.js +25 -0
  15. package/packages/cli/dist/integrations/mcp-json.js +107 -0
  16. package/packages/cli/dist/integrations/memory-doctor.js +124 -0
  17. package/packages/cli/dist/integrations/registry.js +22 -0
  18. package/packages/cli/dist/integrations/spawn.js +228 -0
  19. package/packages/cli/dist/integrations/spec-kit.js +280 -0
  20. package/packages/cli/dist/integrations/verify.js +193 -0
  21. package/packages/cli/dist/integrations/windows-job.js +275 -0
  22. package/packages/cli/dist/lib/elevated-paths.js +74 -0
  23. package/packages/cli/dist/lib/install-set.js +1 -44
  24. package/packages/cli/dist/lib/manifest.js +64 -5
  25. package/packages/cli/dist/lib/prompts.js +0 -23
  26. package/packages/cli/dist/lib/safe-path.js +193 -2
  27. package/packages/cli/dist/lib/safe-text.js +78 -0
  28. package/packages/cli/dist/lib/substitute.js +1 -52
  29. package/packages/cli/dist/lib/summary.js +5 -5
  30. package/packages/cli/dist/templates.js +4 -14
  31. package/scripts/prepare.mjs +28 -12
  32. package/templates/agent-os/subagent-routing.json +4 -4
  33. package/templates/agent-os/universal/.agents/skills/check-premises/SKILL.md +13 -9
  34. package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +31 -6
  35. package/templates/agent-os/universal/.agents/skills/new-invariant/SKILL.md +5 -5
  36. package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +1 -1
  37. package/templates/agent-os/universal/.agents/skills/worktree-task/SKILL.md +3 -3
  38. package/templates/agent-os/universal/.claude/agents/code-reviewer.md +8 -6
  39. package/templates/agent-os/universal/.claude/agents/implementation-agent.md +41 -0
  40. package/templates/agent-os/universal/.claude/agents/prose-reviewer.md +8 -4
  41. package/templates/agent-os/universal/.claude/agents/security-scanner.md +4 -2
  42. package/templates/agent-os/universal/.claude/agents/test-writer.md +2 -2
  43. package/templates/agent-os/universal/.claude/hooks/inject-rules.mjs +83 -4
  44. package/templates/agent-os/universal/.claude/rules/autonomy.md +55 -27
  45. package/templates/agent-os/universal/.claude/rules/invariants.md +9 -8
  46. package/templates/agent-os/universal/.claude/rules/workflow.md +47 -22
  47. package/templates/agent-os/universal/.claude/scripts/decision-router.mjs +1 -1
  48. package/templates/agent-os/universal/.claude/scripts/detect-missed-gate.mjs +12 -9
  49. package/templates/agent-os/universal/.claude/scripts/doctor.mjs +100 -2
  50. package/templates/agent-os/universal/.claude/scripts/lib/claim-records.mjs +236 -6
  51. package/templates/agent-os/universal/.claude/scripts/queue/state.mjs +2 -2
  52. package/templates/agent-os/universal/.claude/scripts/reconcile-external-prs.mjs +4 -3
  53. package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +16 -0
  54. package/templates/agent-os/universal/.claude/settings.json +0 -8
  55. package/templates/agent-os/universal/.claude/skills/check-premises/SKILL.md +13 -9
  56. package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +31 -6
  57. package/templates/agent-os/universal/.claude/skills/new-invariant/SKILL.md +5 -5
  58. package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +1 -1
  59. package/templates/agent-os/universal/.claude/skills/worktree-task/SKILL.md +3 -3
  60. package/templates/agent-os/universal/.codex/agents/code-reviewer.toml +2 -2
  61. package/templates/agent-os/universal/.codex/agents/implementation-agent.toml +6 -0
  62. package/templates/agent-os/universal/.codex/agents/prose-reviewer.toml +2 -2
  63. package/templates/agent-os/universal/.codex/agents/security-scanner.toml +1 -1
  64. package/templates/agent-os/universal/.codex/agents/test-writer.toml +1 -1
  65. package/templates/agent-os/universal/.codex/hooks.json +0 -10
  66. package/templates/agent-os/universal/AGENTS.md +208 -107
  67. package/templates/agent-os/universal/CLAUDE.md +15 -162
  68. package/templates/agent-os/universal/PLAN.md +17 -10
  69. package/templates/agent-os/universal/docs/decisions/agents-md-canonical.md +313 -0
  70. package/templates/agent-os/universal/docs/decisions/codex-adapter.md +15 -10
  71. package/templates/agent-os/universal/docs/decisions/review-lanes.md +12 -8
  72. package/templates/agent-os/universal/docs/decisions/session-start-wire-format.md +206 -0
  73. package/templates/agent-os/universal/docs/decisions/subagent-routing.md +6 -3
  74. package/templates/agent-os/universal/docs/decisions/workflow-layer-split.md +235 -0
  75. package/templates/agent-os/universal/layers.json +25 -30
  76. package/templates/hash-history.json +8 -4
  77. package/templates/release-ledger.json +2 -1
  78. package/packages/cli/dist/lib/composition.js +0 -20
  79. package/packages/cli/dist/lib/targets.js +0 -28
  80. package/packages/cli/dist/policy/benchmark/corpus.js +0 -165
  81. package/packages/cli/dist/policy/core/adapter.js +0 -18
  82. package/packages/cli/dist/policy/core/coverage.js +0 -253
  83. package/packages/cli/dist/policy/core/decision-record.js +0 -287
  84. package/packages/cli/dist/policy/core/declaration.js +0 -127
  85. package/packages/cli/dist/policy/core/evidence-matrix.js +0 -94
  86. package/packages/cli/dist/policy/core/probe.js +0 -442
  87. package/packages/cli/dist/policy/core/registry.js +0 -115
  88. package/packages/cli/dist/policy/core/validation.js +0 -275
  89. package/packages/cli/dist/policy/core/vocabulary.js +0 -123
  90. package/packages/cli/dist/policy/harness/claude.js +0 -47
  91. package/packages/cli/dist/policy/harness/codex.js +0 -87
  92. package/packages/cli/dist/policy/harness/index.js +0 -15
  93. package/packages/cli/dist/policy/harness/shared-hooks.js +0 -28
  94. package/packages/cli/dist/policy/index.js +0 -17
  95. package/templates/agent-os/init/AGENTS.md +0 -201
  96. package/templates/agent-os/init/CLAUDE.md +0 -201
  97. package/templates/agent-os/stack/aws-cdk/.agents/skills/post-deploy-verify/SKILL.md +0 -105
  98. package/templates/agent-os/stack/aws-cdk/.agents/skills/ro-debug/SKILL.md +0 -117
  99. package/templates/agent-os/stack/aws-cdk/.claude/agents/cdk-diff-reviewer.md +0 -89
  100. package/templates/agent-os/stack/aws-cdk/.claude/rules/aws-cdk.md +0 -105
  101. package/templates/agent-os/stack/aws-cdk/.claude/skills/post-deploy-verify/SKILL.md +0 -105
  102. package/templates/agent-os/stack/aws-cdk/.claude/skills/ro-debug/SKILL.md +0 -117
  103. package/templates/agent-os/stack/aws-cdk/.codex/agents/cdk-diff-reviewer.toml +0 -6
  104. package/templates/agent-os/stack/node-ts/.claude/hooks/dod-checks.json +0 -1
  105. package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +0 -85
  106. package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +0 -89
  107. package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +0 -68
  108. package/templates/agent-os/universal/.claude/rules/architecture.md +0 -81
  109. package/templates/skeleton/aws-serverless/.github/workflows/ci.yml +0 -28
  110. package/templates/skeleton/aws-serverless/.github/workflows/deploy.yml +0 -90
  111. package/templates/skeleton/aws-serverless/README.md +0 -181
  112. package/templates/skeleton/aws-serverless/apps/web/next.config.mjs +0 -17
  113. package/templates/skeleton/aws-serverless/apps/web/package.json +0 -19
  114. package/templates/skeleton/aws-serverless/apps/web/src/app/layout.tsx +0 -17
  115. package/templates/skeleton/aws-serverless/apps/web/src/app/page.tsx +0 -96
  116. package/templates/skeleton/aws-serverless/apps/web/src/lib/api.ts +0 -36
  117. package/templates/skeleton/aws-serverless/apps/web/src/lib/validate.ts +0 -23
  118. package/templates/skeleton/aws-serverless/apps/web/test/shared-validation.test.ts +0 -38
  119. package/templates/skeleton/aws-serverless/apps/web/tsconfig.json +0 -14
  120. package/templates/skeleton/aws-serverless/eslint.config.mjs +0 -20
  121. package/templates/skeleton/aws-serverless/gitignore +0 -56
  122. package/templates/skeleton/aws-serverless/infra/bin/app.ts +0 -79
  123. package/templates/skeleton/aws-serverless/infra/cdk.json +0 -3
  124. package/templates/skeleton/aws-serverless/infra/lib/app-stack.ts +0 -248
  125. package/templates/skeleton/aws-serverless/infra/lib/web-stack.ts +0 -45
  126. package/templates/skeleton/aws-serverless/infra/package.json +0 -18
  127. package/templates/skeleton/aws-serverless/infra/test/allowed-origins.test.ts +0 -301
  128. package/templates/skeleton/aws-serverless/infra/test/app-composition.test.ts +0 -137
  129. package/templates/skeleton/aws-serverless/infra/test/app-stack.test.ts +0 -120
  130. package/templates/skeleton/aws-serverless/infra/test/web-stack.test.ts +0 -44
  131. package/templates/skeleton/aws-serverless/package.json +0 -30
  132. package/templates/skeleton/aws-serverless/packages/core/package.json +0 -11
  133. package/templates/skeleton/aws-serverless/packages/core/src/events.ts +0 -14
  134. package/templates/skeleton/aws-serverless/packages/core/src/index.ts +0 -15
  135. package/templates/skeleton/aws-serverless/packages/core/src/note.ts +0 -69
  136. package/templates/skeleton/aws-serverless/packages/core/test/events.test.ts +0 -23
  137. package/templates/skeleton/aws-serverless/packages/core/test/note.test.ts +0 -101
  138. package/templates/skeleton/aws-serverless/packages/db/package.json +0 -14
  139. package/templates/skeleton/aws-serverless/packages/db/src/client.ts +0 -17
  140. package/templates/skeleton/aws-serverless/packages/db/src/index.ts +0 -2
  141. package/templates/skeleton/aws-serverless/packages/db/src/note-model.ts +0 -52
  142. package/templates/skeleton/aws-serverless/packages/db/test/note-model.test.ts +0 -91
  143. package/templates/skeleton/aws-serverless/packages/shared/package.json +0 -11
  144. package/templates/skeleton/aws-serverless/packages/shared/src/env.ts +0 -17
  145. package/templates/skeleton/aws-serverless/packages/shared/src/errors.ts +0 -33
  146. package/templates/skeleton/aws-serverless/packages/shared/src/index.ts +0 -3
  147. package/templates/skeleton/aws-serverless/packages/shared/src/logger.ts +0 -20
  148. package/templates/skeleton/aws-serverless/packages/shared/test/env.test.ts +0 -26
  149. package/templates/skeleton/aws-serverless/packages/shared/test/errors.test.ts +0 -28
  150. package/templates/skeleton/aws-serverless/packages/shared/test/logger.test.ts +0 -19
  151. package/templates/skeleton/aws-serverless/pnpm-lock.yaml +0 -2855
  152. package/templates/skeleton/aws-serverless/pnpm-workspace.yaml +0 -14
  153. package/templates/skeleton/aws-serverless/services/api/package.json +0 -15
  154. package/templates/skeleton/aws-serverless/services/api/src/adapters/sqs-publisher.ts +0 -26
  155. package/templates/skeleton/aws-serverless/services/api/src/handlers/create-note.ts +0 -69
  156. package/templates/skeleton/aws-serverless/services/api/src/handlers/list-notes.ts +0 -37
  157. package/templates/skeleton/aws-serverless/services/api/src/list-main.ts +0 -12
  158. package/templates/skeleton/aws-serverless/services/api/src/main.ts +0 -21
  159. package/templates/skeleton/aws-serverless/services/api/src/usecases/create-note.ts +0 -41
  160. package/templates/skeleton/aws-serverless/services/api/src/usecases/list-notes.ts +0 -14
  161. package/templates/skeleton/aws-serverless/services/api/test/create-note.handler.test.ts +0 -211
  162. package/templates/skeleton/aws-serverless/services/api/test/create-note.usecase.test.ts +0 -45
  163. package/templates/skeleton/aws-serverless/services/api/test/list-notes.test.ts +0 -122
  164. package/templates/skeleton/aws-serverless/services/api/test/sqs-publisher.test.ts +0 -22
  165. package/templates/skeleton/aws-serverless/services/worker/package.json +0 -12
  166. package/templates/skeleton/aws-serverless/services/worker/src/handlers/note-created.ts +0 -15
  167. package/templates/skeleton/aws-serverless/services/worker/src/main.ts +0 -7
  168. package/templates/skeleton/aws-serverless/services/worker/src/usecases/process-note-created.ts +0 -37
  169. package/templates/skeleton/aws-serverless/services/worker/test/note-created.test.ts +0 -61
  170. package/templates/skeleton/aws-serverless/tsconfig.base.json +0 -15
  171. package/templates/skeleton/aws-serverless/tsconfig.json +0 -16
  172. package/templates/skeleton/aws-serverless/vitest.config.ts +0 -14
  173. package/templates/skeleton/node-service/.github/workflows/ci.yml +0 -27
  174. package/templates/skeleton/node-service/.github/workflows/deploy.yml +0 -29
  175. package/templates/skeleton/node-service/README.md +0 -104
  176. package/templates/skeleton/node-service/apps/web/next.config.mjs +0 -17
  177. package/templates/skeleton/node-service/apps/web/package.json +0 -19
  178. package/templates/skeleton/node-service/apps/web/src/app/layout.tsx +0 -17
  179. package/templates/skeleton/node-service/apps/web/src/app/page.tsx +0 -96
  180. package/templates/skeleton/node-service/apps/web/src/lib/api.ts +0 -29
  181. package/templates/skeleton/node-service/apps/web/src/lib/validate.ts +0 -23
  182. package/templates/skeleton/node-service/apps/web/test/shared-validation.test.ts +0 -38
  183. package/templates/skeleton/node-service/apps/web/tsconfig.json +0 -14
  184. package/templates/skeleton/node-service/eslint.config.mjs +0 -20
  185. package/templates/skeleton/node-service/gitignore +0 -54
  186. package/templates/skeleton/node-service/package.json +0 -30
  187. package/templates/skeleton/node-service/packages/core/package.json +0 -11
  188. package/templates/skeleton/node-service/packages/core/src/events.ts +0 -14
  189. package/templates/skeleton/node-service/packages/core/src/index.ts +0 -15
  190. package/templates/skeleton/node-service/packages/core/src/note.ts +0 -69
  191. package/templates/skeleton/node-service/packages/core/test/events.test.ts +0 -23
  192. package/templates/skeleton/node-service/packages/core/test/note.test.ts +0 -101
  193. package/templates/skeleton/node-service/packages/db/package.json +0 -12
  194. package/templates/skeleton/node-service/packages/db/src/index.ts +0 -1
  195. package/templates/skeleton/node-service/packages/db/src/note-store.ts +0 -100
  196. package/templates/skeleton/node-service/packages/db/test/note-store.test.ts +0 -100
  197. package/templates/skeleton/node-service/packages/shared/package.json +0 -11
  198. package/templates/skeleton/node-service/packages/shared/src/env.ts +0 -17
  199. package/templates/skeleton/node-service/packages/shared/src/errors.ts +0 -33
  200. package/templates/skeleton/node-service/packages/shared/src/index.ts +0 -3
  201. package/templates/skeleton/node-service/packages/shared/src/logger.ts +0 -20
  202. package/templates/skeleton/node-service/packages/shared/test/env.test.ts +0 -26
  203. package/templates/skeleton/node-service/packages/shared/test/errors.test.ts +0 -28
  204. package/templates/skeleton/node-service/packages/shared/test/logger.test.ts +0 -19
  205. package/templates/skeleton/node-service/pnpm-lock.yaml +0 -2402
  206. package/templates/skeleton/node-service/pnpm-workspace.yaml +0 -13
  207. package/templates/skeleton/node-service/scripts/build-artifact.mjs +0 -34
  208. package/templates/skeleton/node-service/services/api/package.json +0 -17
  209. package/templates/skeleton/node-service/services/api/src/adapters/spool-publisher.ts +0 -23
  210. package/templates/skeleton/node-service/services/api/src/handlers/create-note.ts +0 -40
  211. package/templates/skeleton/node-service/services/api/src/handlers/list-notes.ts +0 -23
  212. package/templates/skeleton/node-service/services/api/src/main.ts +0 -40
  213. package/templates/skeleton/node-service/services/api/src/server.ts +0 -172
  214. package/templates/skeleton/node-service/services/api/src/static-dir.ts +0 -20
  215. package/templates/skeleton/node-service/services/api/src/usecases/create-note.ts +0 -30
  216. package/templates/skeleton/node-service/services/api/src/usecases/list-notes.ts +0 -14
  217. package/templates/skeleton/node-service/services/api/test/artifact.test.ts +0 -92
  218. package/templates/skeleton/node-service/services/api/test/create-note.handler.test.ts +0 -64
  219. package/templates/skeleton/node-service/services/api/test/create-note.usecase.test.ts +0 -43
  220. package/templates/skeleton/node-service/services/api/test/list-notes.test.ts +0 -48
  221. package/templates/skeleton/node-service/services/api/test/package-manager.test.ts +0 -40
  222. package/templates/skeleton/node-service/services/api/test/package-manager.ts +0 -51
  223. package/templates/skeleton/node-service/services/api/test/server.test.ts +0 -208
  224. package/templates/skeleton/node-service/services/api/test/spool-publisher.test.ts +0 -32
  225. package/templates/skeleton/node-service/services/api/test/static-dir.test.ts +0 -29
  226. package/templates/skeleton/node-service/services/worker/package.json +0 -16
  227. package/templates/skeleton/node-service/services/worker/src/main.ts +0 -28
  228. package/templates/skeleton/node-service/services/worker/src/spool.ts +0 -60
  229. package/templates/skeleton/node-service/services/worker/src/usecases/process-note-created.ts +0 -38
  230. package/templates/skeleton/node-service/services/worker/test/process-note-created.test.ts +0 -34
  231. package/templates/skeleton/node-service/services/worker/test/spool.test.ts +0 -76
  232. package/templates/skeleton/node-service/tsconfig.base.json +0 -15
  233. package/templates/skeleton/node-service/tsconfig.json +0 -13
  234. package/templates/skeleton/node-service/vitest.config.ts +0 -12
package/package.json CHANGED
@@ -1,19 +1,15 @@
1
1
  {
2
2
  "name": "create-agent-rig",
3
- "version": "0.9.1",
4
- "description": "Scaffold a new project with an agent operating system (rules, gates, hooks) and a runnable code skeleton",
3
+ "version": "0.10.0",
4
+ "description": "Configure an agent operating system (rules, gates, hooks) for Claude Code and Codex in a new or existing repository — no application scaffolding",
5
5
  "keywords": [
6
- "create",
7
- "scaffold",
8
- "generator",
9
- "starter",
10
- "template",
11
6
  "agent",
12
7
  "claude-code",
13
8
  "codex",
14
9
  "agent-os",
15
- "aws-serverless",
16
- "node-service"
10
+ "rules",
11
+ "hooks",
12
+ "governance"
17
13
  ],
18
14
  "license": "MIT",
19
15
  "repository": {
@@ -38,16 +34,14 @@
38
34
  "packageManager": "pnpm@11.16.0",
39
35
  "scripts": {
40
36
  "prepare": "node scripts/prepare.mjs",
41
- "build": "tsc -p packages/cli/tsconfig.build.json",
37
+ "build": "node scripts/prepare.mjs --build-only",
42
38
  "lint": "eslint . && prettier --check .",
43
39
  "format": "prettier --write .",
44
40
  "typecheck": "tsc -p packages/cli/tsconfig.json && tsc -p tsconfig.json",
45
41
  "test": "pnpm build && vitest run",
46
- "test:unit": "vitest run --project unit --project template --project benchmark",
42
+ "test:unit": "vitest run --project unit --project template",
47
43
  "test:smoke": "vitest run --project unit",
48
- "test:e2e": "pnpm build && vitest run --project e2e",
49
- "template:install": "pnpm --dir templates/skeleton/aws-serverless install",
50
- "template:check": "pnpm --dir templates/skeleton/aws-serverless run check"
44
+ "test:e2e": "pnpm build && vitest run --project e2e"
51
45
  },
52
46
  "devDependencies": {
53
47
  "@eslint/js": "^10.0.1",
@@ -1,121 +1,89 @@
1
1
  import { execFile } from 'node:child_process';
2
- import { mkdir, readFile, readdir, stat } from 'node:fs/promises';
2
+ import { mkdir, readdir, stat } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { promisify } from 'node:util';
5
- import { copyTree, listTree, mapConcurrent } from '../lib/copy-tree.js';
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';
9
- import { substituteContent, substituteFileName } from '../lib/substitute.js';
10
5
  import { gitEnv } from '../lib/git-env.js';
11
- import { DEFAULT_TARGET, TARGETS, TARGET_NAMES } from '../lib/targets.js';
12
- import { skeletonDir } from '../templates.js';
13
- import { packageVersion } from '../lib/version.js';
6
+ import { initProject } from './init.js';
14
7
  /** A user-facing failure: message is printed as-is, no stack trace. */
15
8
  export class CreateError extends Error {
16
9
  }
17
- /** Valid npm package name (unscoped part) — also used as the npm scope. */
18
- const NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
10
+ /**
11
+ * A safe, predictable project name — also what ends up in a filename (the
12
+ * kill switch, `~/.claude/<name>-loop-STOP`) and inside a single-quoted string
13
+ * literal in a generated script (`stop-flag.mjs`), so it is validated
14
+ * up front rather than silently slugged. There is no npm scope to validate
15
+ * for any more (RP-177 retired the application skeleton this pattern used to
16
+ * double as): the shape survives only because it is still the right shape for
17
+ * a directory/kill-switch name, not because anything downstream reads it as a
18
+ * package identifier.
19
+ *
20
+ * No trailing `-` or `.`: `projectNameFor` (the name `init` would derive from
21
+ * this same directory with no identity supplied) strips both, and a name this
22
+ * pattern accepted but that function would rewrite is exactly the mismatch
23
+ * that once made a freshly created rig fail to match its own installed files
24
+ * on the very next `upgrade`.
25
+ */
26
+ const NAME_PATTERN = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;
27
+ /**
28
+ * `create <dir>` — a thin convenience wrapper (RP-177): make the directory,
29
+ * initialize Git, run the same install `init` runs inside it, then commit the
30
+ * pristine baseline. There is only one payload flavour; this command's entire
31
+ * value is not having to `mkdir`, `git init`, `cd` and run `init` by hand.
32
+ */
19
33
  export async function createProject(dirArg, options) {
20
34
  const projectDir = path.resolve(options.cwd, dirArg);
21
35
  const projectName = path.basename(projectDir);
22
36
  if (!NAME_PATTERN.test(projectName)) {
23
- throw new CreateError(`Invalid project name "${projectName}": use lowercase letters, digits, ".", "_" and "-" ` +
24
- '(it becomes the npm package name and scope).');
37
+ throw new CreateError(`Invalid project name "${projectName}": use lowercase letters, digits, ".", "_" and "-", ` +
38
+ 'starting and ending with a letter or digit.');
25
39
  }
26
40
  await ensureEmptyOrAbsent(projectDir);
27
- const targetName = options.target ?? DEFAULT_TARGET;
28
- const target = TARGETS[targetName];
29
- if (!target) {
30
- throw new CreateError(`Unknown target "${targetName}". Known targets: ${TARGET_NAMES.join(', ')}.`);
31
- }
32
- const ctx = {
33
- projectName,
34
- projectScope: projectName,
35
- region: target.defaultRegion ?? '',
36
- };
37
- const transforms = {
38
- transformContent: (content) => substituteContent(content, ctx),
39
- transformName: (name) => substituteFileName(name, ctx),
40
- };
41
- // Layer 2 (the skeleton) + layer 1 (agent-os: universal + stack overlays).
42
- const agentOsLayers = agentOsLayerDirs(target.stacks);
43
- const layers = [
44
- { name: `skeleton/${target.skeletonDir}`, dir: skeletonDir(target.skeletonDir) },
45
- ...agentOsLayers,
46
- ];
47
- // Composition safety: layers must claim disjoint paths. Checked before any
48
- // copy — a collision is a template bug and must never be resolved by order.
49
- const claimed = [];
50
- for (const layer of layers) {
51
- claimed.push({ name: layer.name, files: await listTree(layer.dir, transforms) });
52
- }
53
- const collisions = detectCollisions(claimed, ALLOWED_OVERWRITES);
54
- if (collisions.length > 0) {
55
- const detail = collisions
56
- .map((c) => ` ${c.path} — claimed by ${c.layers.join(' and ')}`)
57
- .join('\n');
58
- throw new CreateError(`Template layers collide (fix the templates, not the order):\n${detail}`);
59
- }
60
41
  await mkdir(projectDir, { recursive: true });
61
- for (const layer of layers) {
62
- await copyTree(layer.dir, projectDir, transforms);
63
- }
64
- await recordInstall(projectDir, agentOsLayers, transforms, ctx, targetName);
65
- if (options.git !== false) {
66
- await initGitBaseline(projectDir);
42
+ const gitReady = options.git !== false && (await initGitRepository(projectDir));
43
+ await initProject(projectDir, {
44
+ project: { name: projectName, scope: projectName, region: '' },
45
+ withWorkflow: options.withWorkflow,
46
+ });
47
+ if (gitReady) {
48
+ await commitGitBaseline(projectDir);
67
49
  }
68
50
  return { projectDir, projectName };
69
51
  }
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
- }
52
+ const run = promisify(execFile);
53
+ const gitInvocation = (projectDir) => ({
54
+ quiet: ['-c', 'gc.auto=0', '-c', 'maintenance.auto=false'],
55
+ where: { cwd: projectDir, env: gitEnv() },
56
+ });
57
+ async function initGitRepository(projectDir) {
58
+ const { quiet, where } = gitInvocation(projectDir);
59
+ try {
60
+ await run('git', [...quiet, 'init', '--quiet'], where);
61
+ return true;
62
+ }
63
+ catch {
64
+ // git missing or unusable — generation never fails on this.
65
+ return false;
93
66
  }
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
67
  }
102
- const run = promisify(execFile);
103
- async function initGitBaseline(projectDir) {
68
+ async function commitGitBaseline(projectDir) {
104
69
  // Disable git's background maintenance for these one-shot commands: a commit
105
70
  // can otherwise fork an auto-gc / maintenance process that keeps writing to
106
71
  // .git/objects/pack after we return — a non-deterministic tail that races any
107
72
  // caller cleaning up the directory, and pointless work on a one-commit repo.
108
- const quiet = ['-c', 'gc.auto=0', '-c', 'maintenance.auto=false'];
109
- const where = { cwd: projectDir, env: gitEnv() };
73
+ const { quiet, where } = gitInvocation(projectDir);
74
+ // Pin both repository locations even though cwd is already the child. If a
75
+ // later refactor calls this without a successful `git init`, Git must fail
76
+ // here rather than discover and mutate a parent repository.
77
+ const repository = [`--git-dir=${path.join(projectDir, '.git')}`, `--work-tree=${projectDir}`];
110
78
  try {
111
- await run('git', [...quiet, 'init', '--quiet'], where);
112
- await run('git', [...quiet, 'add', '-A'], where);
79
+ await run('git', [...quiet, ...repository, 'add', '-A'], where);
113
80
  // Explicit identity: the baseline must commit even where git has no
114
81
  // global user configured (fresh machines, CI). --no-verify here shields
115
82
  // the baseline from the USER'S global hooks only — the generated
116
83
  // project's own gates do not exist yet, so nothing is being bypassed.
117
84
  await run('git', [
118
85
  ...quiet,
86
+ ...repository,
119
87
  '-c',
120
88
  'user.name=create-agent-rig',
121
89
  '-c',
@@ -0,0 +1,213 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { MANIFEST_REL, parseManifest, sha256 } from '../lib/manifest.js';
3
+ import { readBounded, verifyIntegrations } from '../integrations/verify.js';
4
+ import { DECLARATION_REL, MAX_DECLARATION_BYTES, parseDeclaration, } from '../integrations/declaration.js';
5
+ import { REGISTRY } from '../integrations/registry.js';
6
+ import { inspectSpecKit, SPEC_KIT_VERSION, } from '../integrations/spec-kit.js';
7
+ import { inspectMemory } from '../integrations/memory-doctor.js';
8
+ import { inspectGuards } from '../integrations/doctor-guards.js';
9
+ import { inspectWorkflow } from '../integrations/doctor-workflow.js';
10
+ function text(bytes) {
11
+ try {
12
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ async function rigChecks(root, codexHash) {
19
+ const source = await readBounded(root, MANIFEST_REL, 1024 * 1024);
20
+ if (source.status === 'absent')
21
+ return [{ id: 'rig-manifest', status: 'warn', reason: 'not-installed' }];
22
+ const decoded = source.status === 'ok' ? text(source.bytes) : undefined;
23
+ const manifest = decoded === undefined ? null : parseManifest(decoded);
24
+ if (manifest === null || Object.keys(manifest.files).length > 4096)
25
+ return [{ id: 'rig-manifest', status: 'fail', reason: 'unreadable-manifest' }];
26
+ const checks = [{ id: 'rig-manifest', status: 'pass', reason: 'valid' }];
27
+ let contentDrift = false;
28
+ let lineDrift = false;
29
+ let absent = false;
30
+ let unreadable = false;
31
+ for (const [rel, recorded] of Object.entries(manifest.files)) {
32
+ const expected = rel === '.codex/config.toml' ? (codexHash ?? recorded) : recorded;
33
+ const file = await readBounded(root, rel, 1024 * 1024);
34
+ if (file.status === 'absent') {
35
+ absent = true;
36
+ continue;
37
+ }
38
+ if (file.status !== 'ok') {
39
+ unreadable = true;
40
+ continue;
41
+ }
42
+ if (sha256(file.bytes) === expected)
43
+ continue;
44
+ const decodedFile = text(file.bytes);
45
+ if (decodedFile !== undefined &&
46
+ (sha256(decodedFile.replace(/\r\n/g, '\n')) === expected ||
47
+ sha256(decodedFile.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n')) === expected))
48
+ lineDrift = true;
49
+ else
50
+ contentDrift = true;
51
+ }
52
+ checks.push({
53
+ id: 'rig-owned-files',
54
+ status: unreadable ? 'fail' : absent || contentDrift || lineDrift ? 'warn' : 'pass',
55
+ reason: unreadable
56
+ ? 'unreadable-owned-file'
57
+ : absent
58
+ ? 'absent-owned-file'
59
+ : contentDrift
60
+ ? 'content-drift'
61
+ : lineDrift
62
+ ? 'line-ending-drift'
63
+ : 'pristine',
64
+ });
65
+ return checks;
66
+ }
67
+ /** Read-only diagnosis. Runtime observations are returned, never persisted. */
68
+ export async function runDoctor(options) {
69
+ let json;
70
+ try {
71
+ const parsed = parseArgs({
72
+ args: options.args,
73
+ allowPositionals: false,
74
+ options: { json: { type: 'boolean' } },
75
+ });
76
+ json = parsed.values.json === true;
77
+ }
78
+ catch {
79
+ return { exitCode: 2, stdout: '', stderr: 'doctor accepts only --json\n' };
80
+ }
81
+ const source = await readBounded(options.cwd, DECLARATION_REL, MAX_DECLARATION_BYTES);
82
+ const declaration = source.status === 'ok' ? parseDeclaration(text(source.bytes) ?? '', REGISTRY) : undefined;
83
+ const intent = declaration?.status === 'ok' && declaration.rejected.length === 0 ? declaration : undefined;
84
+ const checks = await rigChecks(options.cwd, intent?.targets?.codex?.fileHash);
85
+ if (checks.some((check) => check.id === 'rig-manifest' && check.status === 'pass')) {
86
+ const [guards, workflow] = await Promise.all([
87
+ inspectGuards({ repoDir: options.cwd }),
88
+ inspectWorkflow({ repoDir: options.cwd }),
89
+ ]);
90
+ checks.push({ id: 'guards', ...guards }, { id: 'workflow', ...workflow });
91
+ }
92
+ const memory = await inspectMemory({ repoDir: options.cwd, env: options.env ?? process.env });
93
+ checks.push({ id: 'custom-memory', status: memory.status, reason: memory.reason });
94
+ let specKit;
95
+ const wiring = await verifyIntegrations({ repoDir: options.cwd, env: options.env });
96
+ const invalid = wiring.issues.some((issue) => issue.status !== 'missing');
97
+ const pendingHarness = wiring.integrations.some((entry) => Object.keys(entry.harnesses).length === 0);
98
+ checks.push({
99
+ id: 'integrations',
100
+ status: invalid ? 'fail' : pendingHarness ? 'warn' : 'pass',
101
+ reason: invalid
102
+ ? 'invalid-declaration'
103
+ : pendingHarness
104
+ ? 'harness-selection-pending'
105
+ : wiring.integrations.length
106
+ ? 'declared'
107
+ : 'not-selected',
108
+ });
109
+ for (const entry of wiring.integrations) {
110
+ if (Object.keys(entry.harnesses).length === 0) {
111
+ checks.push({
112
+ id: `${entry.id}:harness-selection`,
113
+ status: 'warn',
114
+ reason: 'harness-selection-pending',
115
+ });
116
+ continue;
117
+ }
118
+ if (entry.id === 'spec-kit') {
119
+ const selected = intent?.entries.find((candidate) => candidate.id === 'spec-kit');
120
+ if (selected?.version !== SPEC_KIT_VERSION) {
121
+ checks.push({ id: 'spec-kit', status: 'fail', reason: 'unsupported-requested-version' });
122
+ }
123
+ else {
124
+ const inspection = await inspectSpecKit({
125
+ repoDir: options.cwd,
126
+ harnesses: selected.harnesses ?? [],
127
+ env: options.env,
128
+ });
129
+ specKit = inspection;
130
+ checks.push({ id: 'spec-kit', status: inspection.status, reason: inspection.reason });
131
+ }
132
+ continue;
133
+ }
134
+ for (const [harness, state] of Object.entries(entry.harnesses)) {
135
+ checks.push({
136
+ id: `${entry.id}:${harness}`,
137
+ status: state.wiring === 'unreadable'
138
+ ? 'fail'
139
+ : state.wiring === 'healthy'
140
+ ? state.launcher === 'missing'
141
+ ? 'warn'
142
+ : 'pass'
143
+ : 'warn',
144
+ reason: state.wiring === 'healthy'
145
+ ? state.launcher === 'missing'
146
+ ? 'launcher-missing'
147
+ : 'wired'
148
+ : state.wiring,
149
+ });
150
+ }
151
+ }
152
+ const status = checks.some((check) => check.status === 'fail')
153
+ ? 'fail'
154
+ : checks.some((check) => check.status === 'warn')
155
+ ? 'warn'
156
+ : 'pass';
157
+ const wiringNames = {
158
+ healthy: 'wired',
159
+ missing: 'absent',
160
+ drift: 'drifted',
161
+ unowned: 'foreign',
162
+ unreadable: 'unreadable',
163
+ };
164
+ const integrations = wiring.integrations
165
+ .filter((entry) => entry.id !== 'spec-kit')
166
+ .map((entry) => ({
167
+ id: entry.id,
168
+ harnesses: Object.fromEntries(Object.entries(entry.harnesses).map(([harness, state]) => [
169
+ harness,
170
+ { ...state, wiring: wiringNames[state.wiring] },
171
+ ])),
172
+ }));
173
+ const report = {
174
+ schemaVersion: 1,
175
+ status: status === 'pass' ? 'ok' : status,
176
+ checks: checks.map((check) => ({
177
+ ...check,
178
+ status: check.status === 'pass' ? 'ok' : check.status,
179
+ detail: check.reason === 'workflow-verified'
180
+ ? 'Workflow and frozen revalidation scripts match this package; the RP-26 decision is not observed.'
181
+ : check.reason.replaceAll('-', ' '),
182
+ fix: check.status === 'pass'
183
+ ? ''
184
+ : check.id.startsWith('rig-') || check.id === 'guards' || check.id === 'workflow'
185
+ ? 'Review the installation with create-agent-rig upgrade before accepting changes.'
186
+ : check.id === 'custom-memory'
187
+ ? 'Check the machine-scoped Memory installation and its compatible version.'
188
+ : check.id === 'spec-kit'
189
+ ? 'Check the pinned Spec Kit launcher and authoritative integration status.'
190
+ : 'Review create-agent-rig setup list and the intended provider wiring.',
191
+ })),
192
+ integrations,
193
+ memory: { ...memory, status: memory.status === 'pass' ? 'ok' : memory.status },
194
+ ...(specKit
195
+ ? {
196
+ specKit: {
197
+ version: SPEC_KIT_VERSION,
198
+ ...specKit,
199
+ status: specKit.status === 'pass' ? 'ok' : specKit.status,
200
+ connectivity: 'not-observed',
201
+ trust: 'not-observed',
202
+ },
203
+ }
204
+ : {}),
205
+ };
206
+ return {
207
+ exitCode: status === 'fail' ? 1 : 0,
208
+ stdout: json
209
+ ? `${JSON.stringify(report)}\n`
210
+ : `${report.checks.map((check) => `${check.status}: ${check.id}: ${check.detail}${check.fix ? ` — ${check.fix}` : ''}`).join('\n')}\n`,
211
+ stderr: '',
212
+ };
213
+ }