specrails-core 4.1.1 โ†’ 4.2.1

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 (66) hide show
  1. package/README.md +4 -4
  2. package/bin/specrails-core.mjs +302 -0
  3. package/commands/doctor.md +5 -5
  4. package/commands/enrich.md +9 -9
  5. package/dist/installer/cli.js +167 -0
  6. package/dist/installer/cli.js.map +1 -0
  7. package/dist/installer/commands/doctor.js +144 -0
  8. package/dist/installer/commands/doctor.js.map +1 -0
  9. package/dist/installer/commands/init.js +182 -0
  10. package/dist/installer/commands/init.js.map +1 -0
  11. package/dist/installer/commands/perf-check.js +16 -0
  12. package/dist/installer/commands/perf-check.js.map +1 -0
  13. package/dist/installer/commands/update.js +170 -0
  14. package/dist/installer/commands/update.js.map +1 -0
  15. package/dist/installer/phases/install-config.js +120 -0
  16. package/dist/installer/phases/install-config.js.map +1 -0
  17. package/dist/installer/phases/manifest.js +93 -0
  18. package/dist/installer/phases/manifest.js.map +1 -0
  19. package/dist/installer/phases/prereqs.js +116 -0
  20. package/dist/installer/phases/prereqs.js.map +1 -0
  21. package/dist/installer/phases/provider-detect.js +111 -0
  22. package/dist/installer/phases/provider-detect.js.map +1 -0
  23. package/dist/installer/phases/scaffold.js +373 -0
  24. package/dist/installer/phases/scaffold.js.map +1 -0
  25. package/dist/installer/util/errors.js +79 -0
  26. package/dist/installer/util/errors.js.map +1 -0
  27. package/dist/installer/util/exec.js +151 -0
  28. package/dist/installer/util/exec.js.map +1 -0
  29. package/dist/installer/util/fs.js +153 -0
  30. package/dist/installer/util/fs.js.map +1 -0
  31. package/dist/installer/util/git.js +113 -0
  32. package/dist/installer/util/git.js.map +1 -0
  33. package/dist/installer/util/logger.js +55 -0
  34. package/dist/installer/util/logger.js.map +1 -0
  35. package/dist/installer/util/paths.js +66 -0
  36. package/dist/installer/util/paths.js.map +1 -0
  37. package/dist/installer/util/prompts.js +49 -0
  38. package/dist/installer/util/prompts.js.map +1 -0
  39. package/dist/installer/util/template.js +60 -0
  40. package/dist/installer/util/template.js.map +1 -0
  41. package/docs/deployment.md +2 -1
  42. package/docs/installation.md +6 -3
  43. package/docs/testing/test-matrix-codex.md +19 -11
  44. package/docs/updating.md +24 -49
  45. package/docs/user-docs/faq.md +1 -1
  46. package/docs/windows.md +53 -0
  47. package/{templates/settings/integration-contract.json โ†’ integration-contract.json} +2 -2
  48. package/package.json +25 -10
  49. package/pinned-versions.json +4 -0
  50. package/schemas/profile.v1.json +11 -3
  51. package/templates/agents/sr-architect.md +1 -1
  52. package/templates/agents/sr-reviewer.md +1 -1
  53. package/templates/commands/specrails/compat-check.md +3 -3
  54. package/templates/commands/specrails/doctor.md +5 -5
  55. package/templates/commands/specrails/enrich.md +9 -9
  56. package/templates/commands/specrails/reconfig.md +2 -2
  57. package/templates/commands/specrails/refactor-recommender.md +2 -2
  58. package/templates/commands/specrails/vpc-drift.md +1 -1
  59. package/templates/skills/sr-compat-check/SKILL.md +3 -3
  60. package/templates/skills/sr-refactor-recommender/SKILL.md +2 -2
  61. package/bin/doctor.sh +0 -127
  62. package/bin/perf-check.sh +0 -21
  63. package/bin/specrails-core.js +0 -262
  64. package/commands/setup.md +0 -1461
  65. package/install.sh +0 -1231
  66. package/update.sh +0 -870
package/README.md CHANGED
@@ -16,7 +16,7 @@ npx specrails-core@latest init # install into the current repo
16
16
  /specrails:enrich # optional: deep codebase analysis
17
17
  ```
18
18
 
19
- > **Requirements:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), git, Node 18+.
19
+ > **Requirements:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), git, Node 20+. Cross-platform: macOS, Linux, Windows.
20
20
  >
21
21
  > **๐Ÿงช Codex (OpenAI) support โ€” Coming Soon:** We are testing Codex integration in our lab. Installation is disabled for now, but the feature will be available shortly. Follow the repo for updates.
22
22
 
@@ -230,12 +230,12 @@ Baseline agents (`sr-architect`, `sr-developer`, `sr-reviewer`) MUST appear in `
230
230
 
231
231
  ### Reserved paths
232
232
 
233
- The following paths are **reserved** โ€” `update.sh` will never create, modify, or delete anything inside them:
233
+ The following paths are **reserved** โ€” `specrails-core update` will never create, modify, or delete anything inside them:
234
234
 
235
235
  - `.specrails/profiles/**` โ€” profile JSON files (yours and hub-authored).
236
236
  - `.claude/agents/custom-*.md` โ€” your custom agents. Use the `custom-` prefix to opt in to this protection.
237
237
 
238
- This contract is what lets you safely hand-author (or let specrails-hub author) profiles and custom agents without fear of the next `update` overwriting your work. Other paths managed by specrails-core (`.specrails/install-config.yaml`, `.specrails/specrails-version`, etc.) remain under `update.sh`'s control.
238
+ This contract is what lets you safely hand-author (or let specrails-hub author) profiles and custom agents without fear of the next `update` overwriting your work. Other paths managed by specrails-core (`.specrails/install-config.yaml`, `.specrails/specrails-version`, etc.) remain under update's control. Audited by `src/installer/__tests__/reserved-paths.test.ts` on every CI run.
239
239
 
240
240
  ---
241
241
 
@@ -284,7 +284,7 @@ Each persona scores features 0โ€“5. Features are ranked by score / effort ratio.
284
284
  | **Claude Code** | Yes | AI agent runtime |
285
285
  | **Codex CLI** _(coming soon โ€” in lab)_ | ๐Ÿงช Not yet | OpenAI Codex support is being tested in our lab and will be available shortly. |
286
286
  | **git** | Yes | Repository detection |
287
- | **Node 18+** | Yes | Needed for `npx specrails-core@latest init` |
287
+ | **Node 20+** | Yes | Needed for `npx specrails-core@latest init`. Cross-platform: macOS, Linux, Windows (10/11, x64 + ARM64 via emulation). |
288
288
  | **GitHub CLI** (`gh`) | Optional | Backlog sync to GitHub Issues, PR creation. Not needed with local tickets. |
289
289
  | **JIRA CLI** (`jira`) | Optional | Backlog sync to JIRA. Not needed with local tickets. |
290
290
 
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * specrails-core CLI โ€” thin ESM dispatcher.
4
+ *
5
+ * All installer subcommands (init, update, doctor, perf-check) execute
6
+ * in-process via the Node installer under dist/installer/. This file
7
+ * only keeps logic that is local to the dispatcher:
8
+ * - `profile validate` / `profile show` โ€” schema validation via ajv.
9
+ * - `enrich` โ€” spawns Claude Code with
10
+ * the /specrails:enrich command.
11
+ * - `init` TUI short-circuit โ€” spawns tui-installer.mjs
12
+ * then re-enters init with
13
+ * --from-config.
14
+ *
15
+ * Nothing in this file shells out to bash.
16
+ */
17
+
18
+ import { spawnSync } from 'node:child_process'
19
+ import { existsSync, readFileSync } from 'node:fs'
20
+ import { createRequire } from 'node:module'
21
+ import path from 'node:path'
22
+ import { fileURLToPath, pathToFileURL } from 'node:url'
23
+
24
+ const __filename = fileURLToPath(import.meta.url)
25
+ const __dirname = path.dirname(__filename)
26
+ const ROOT = path.resolve(__dirname, '..')
27
+ const require_ = createRequire(import.meta.url)
28
+
29
+ const args = process.argv.slice(2)
30
+ const subcommand = args[0]
31
+
32
+ // โ”€โ”€โ”€ Global --version / -V flag โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
33
+
34
+ if (args.includes('--version') || args.includes('-V')) {
35
+ const pkg = require_(path.resolve(ROOT, 'package.json'))
36
+ console.log(`specrails-core v${pkg.version}`)
37
+ process.exit(0)
38
+ }
39
+
40
+ // โ”€โ”€โ”€ Usage screen (no subcommand) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
41
+
42
+ if (!subcommand) {
43
+ printUsage()
44
+ process.exit(0)
45
+ }
46
+
47
+ // โ”€โ”€โ”€ Subcommand allowlist (kept for backwards compatibility) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
48
+
49
+ const KNOWN_SUBCOMMANDS = new Set([
50
+ 'init',
51
+ 'update',
52
+ 'doctor',
53
+ 'perf-check',
54
+ 'enrich',
55
+ 'version',
56
+ 'profile',
57
+ 'help',
58
+ ])
59
+
60
+ if (!KNOWN_SUBCOMMANDS.has(subcommand)) {
61
+ console.error(`Unknown command: ${subcommand}\n`)
62
+ console.error('Available commands: init, update, doctor, perf-check, enrich, version, profile, help')
63
+ process.exit(1)
64
+ }
65
+
66
+ const subargs = args.slice(1)
67
+
68
+ // โ”€โ”€โ”€ help / version handled by the dispatcher โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
69
+
70
+ if (subcommand === 'help') {
71
+ printUsage()
72
+ process.exit(0)
73
+ }
74
+
75
+ if (subcommand === 'version') {
76
+ const pkg = require_(path.resolve(ROOT, 'package.json'))
77
+ console.log(`specrails-core v${pkg.version}`)
78
+ process.exit(0)
79
+ }
80
+
81
+ // โ”€โ”€โ”€ profile validate / show โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
82
+ // Pure Node logic; no installer touch.
83
+
84
+ if (subcommand === 'profile') {
85
+ await runProfile(subargs)
86
+ process.exit(0)
87
+ }
88
+
89
+ // โ”€โ”€โ”€ enrich โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
90
+ // Launches Claude Code with the /specrails:enrich slash command.
91
+
92
+ if (subcommand === 'enrich') {
93
+ const enrichFlags = subargs.join(' ')
94
+ const claudeCmd = `/specrails:enrich${enrichFlags ? ' ' + enrichFlags : ''}`
95
+ const result = spawnSync(
96
+ 'claude',
97
+ ['--command', claudeCmd, '--dangerously-skip-permissions'],
98
+ {
99
+ stdio: 'inherit',
100
+ cwd: process.cwd(),
101
+ shell: process.platform === 'win32',
102
+ },
103
+ )
104
+ if (result.error) {
105
+ console.error(
106
+ '\nFailed to launch Claude CLI for enrich:',
107
+ result.error.message,
108
+ '\nEnsure Claude Code is installed: npm install -g @anthropic-ai/claude-code\n',
109
+ )
110
+ process.exit(1)
111
+ }
112
+ process.exit(result.status ?? (result.error ? 1 : 0))
113
+ }
114
+
115
+ // โ”€โ”€โ”€ init: optional TUI short-circuit โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
116
+ // Default flow runs the TUI to collect agent/model configuration and
117
+ // write .specrails/install-config.yaml, then falls through to the Node
118
+ // init command with --from-config. Skipped when --no-tui / --from-config
119
+ // / --yes is passed.
120
+
121
+ if (subcommand === 'init') {
122
+ const hasNoTui =
123
+ subargs.includes('--no-tui') ||
124
+ subargs.includes('--no-direct') ||
125
+ subargs.includes('--from-config')
126
+ const autoYes = subargs.includes('--yes') || subargs.includes('-y')
127
+ const useTui = !hasNoTui && !autoYes
128
+
129
+ if (useTui) {
130
+ const rootDirIdx = subargs.indexOf('--root-dir')
131
+ const rootDir =
132
+ rootDirIdx >= 0 && subargs[rootDirIdx + 1]
133
+ ? path.resolve(subargs[rootDirIdx + 1])
134
+ : process.cwd()
135
+
136
+ const tuiArgs = [path.resolve(ROOT, 'bin', 'tui-installer.mjs'), rootDir]
137
+ const tuiResult = spawnSync('node', tuiArgs, {
138
+ stdio: 'inherit',
139
+ cwd: process.cwd(),
140
+ shell: process.platform === 'win32',
141
+ })
142
+
143
+ if (tuiResult.error) {
144
+ console.error(
145
+ '\nFailed to launch TUI installer:',
146
+ tuiResult.error.message,
147
+ '\nRun: npm install or pass --no-tui to skip the TUI.\n',
148
+ )
149
+ process.exit(1)
150
+ }
151
+ if (tuiResult.status !== 0) {
152
+ process.exit(tuiResult.status ?? 1)
153
+ }
154
+
155
+ // TUI succeeded โ€” re-enter init with --from-config so the Node
156
+ // command reads provider/tier/agent_teams from install-config.yaml.
157
+ const nextArgs = subargs
158
+ .filter((a) => a !== '--no-tui' && a !== '--no-direct')
159
+ .concat(['--yes', '--from-config'])
160
+ await runNodeCli(['init', ...nextArgs])
161
+ // runNodeCli calls process.exit itself; the following line is
162
+ // defensive.
163
+ process.exit(0)
164
+ }
165
+
166
+ // Direct mode โ€” strip internal --no-tui / --no-direct flags and go.
167
+ const cleanArgs = subargs.filter((a) => a !== '--no-tui' && a !== '--no-direct')
168
+ await runNodeCli(['init', ...cleanArgs])
169
+ process.exit(0)
170
+ }
171
+
172
+ // โ”€โ”€โ”€ update / doctor / perf-check โ€” direct dispatch to Node CLI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
173
+
174
+ await runNodeCli([subcommand, ...subargs])
175
+ process.exit(0)
176
+
177
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
178
+ // Helpers
179
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
180
+
181
+ function printUsage() {
182
+ console.log(`specrails-core โ€” Agent Workflow System for Claude Code
183
+
184
+ Usage:
185
+ specrails-core init [--root-dir <path>] [--yes|-y] [--no-tui] Install into a repository
186
+ specrails-core update [--only <component>] [--dry-run] Update an existing installation
187
+ specrails-core doctor Run health checks
188
+ specrails-core perf-check [--files <list>] Performance regression check (CI)
189
+ specrails-core enrich [--from-config <path>] Run /specrails:enrich via Claude CLI
190
+ specrails-core profile <validate|show> [<path>] Validate or pretty-print a profile JSON
191
+ specrails-core version Show installed version
192
+
193
+ Flags for init:
194
+ --root-dir <path> Target repository path (default: current directory)
195
+ --yes | -y Non-interactive; use defaults, skip TUI
196
+ --provider <value> Force provider: claude (codex coming soon)
197
+ --no-tui Skip TUI; use defaults / flags directly
198
+ --from-config Skip TUI; use existing .specrails/install-config.yaml
199
+
200
+ Global flags:
201
+ --version | -V Show installed version
202
+
203
+ More info: https://github.com/fjpulidop/specrails-core`)
204
+ }
205
+
206
+ /**
207
+ * Loads the compiled Node CLI (dist/installer/cli.js) and invokes its
208
+ * `main` function with the given argv array. Propagates the returned
209
+ * exit code via process.exit.
210
+ */
211
+ async function runNodeCli(argv) {
212
+ const cliPath = path.resolve(ROOT, 'dist', 'installer', 'cli.js')
213
+ if (!existsSync(cliPath)) {
214
+ console.error(
215
+ `Installer runtime not found at ${cliPath}. ` +
216
+ `If you are running from a source checkout, run: npm run build`,
217
+ )
218
+ process.exit(1)
219
+ }
220
+ const mod = await import(pathToFileURL(cliPath).href)
221
+ if (typeof mod.main !== 'function') {
222
+ console.error('Installer runtime is missing the expected main() export โ€” corrupt install?')
223
+ process.exit(1)
224
+ }
225
+ const code = await mod.main(argv)
226
+ process.exit(code)
227
+ }
228
+
229
+ async function runProfile(subargs) {
230
+ const action = subargs[0]
231
+ const pathArg = subargs[1]
232
+ if (!action || (action !== 'validate' && action !== 'show')) {
233
+ console.error('Usage: specrails-core profile validate [<path>]')
234
+ console.error(' specrails-core profile show [<path>]')
235
+ process.exit(1)
236
+ }
237
+
238
+ const resolveProfilePath = () => {
239
+ if (pathArg) return path.resolve(pathArg)
240
+ if (process.env.SPECRAILS_PROFILE_PATH) return path.resolve(process.env.SPECRAILS_PROFILE_PATH)
241
+ const projectDefault = path.resolve(process.cwd(), '.specrails', 'profiles', 'project-default.json')
242
+ if (existsSync(projectDefault)) return projectDefault
243
+ return null
244
+ }
245
+
246
+ const profilePath = resolveProfilePath()
247
+ if (!profilePath) {
248
+ console.error('No profile path given and none could be resolved.')
249
+ console.error('Pass an explicit path or set SPECRAILS_PROFILE_PATH, or place a profile at')
250
+ console.error(' .specrails/profiles/project-default.json')
251
+ process.exit(1)
252
+ }
253
+ if (!existsSync(profilePath)) {
254
+ console.error(`Profile file not found: ${profilePath}`)
255
+ process.exit(1)
256
+ }
257
+
258
+ let profile
259
+ try {
260
+ profile = JSON.parse(readFileSync(profilePath, 'utf8'))
261
+ } catch (e) {
262
+ console.error(`Profile is not valid JSON: ${e.message}`)
263
+ process.exit(1)
264
+ }
265
+
266
+ if (action === 'show') {
267
+ console.log(JSON.stringify(profile, null, 2))
268
+ process.exit(0)
269
+ }
270
+
271
+ // action === 'validate'
272
+ const schemaPath = path.resolve(ROOT, 'schemas', 'profile.v1.json')
273
+ if (!existsSync(schemaPath)) {
274
+ console.error(`Schema not found at ${schemaPath} โ€” install may be corrupt`)
275
+ process.exit(1)
276
+ }
277
+
278
+ let Ajv
279
+ try {
280
+ const mod = await import('ajv/dist/2020.js')
281
+ Ajv = mod.default
282
+ } catch {
283
+ console.error(
284
+ "'ajv' is not installed. Run `npm install` in the specrails-core package directory first.",
285
+ )
286
+ process.exit(1)
287
+ }
288
+
289
+ const schema = JSON.parse(readFileSync(schemaPath, 'utf8'))
290
+ const ajv = new Ajv({ allErrors: true, strict: false })
291
+ const validate = ajv.compile(schema)
292
+
293
+ if (validate(profile)) {
294
+ console.log(`โœ“ ${profilePath} is a valid v1 profile.`)
295
+ process.exit(0)
296
+ }
297
+ console.error(`โœ— ${profilePath} failed validation:`)
298
+ for (const err of validate.errors || []) {
299
+ console.error(` ${err.instancePath || '/'} ${err.message} (${JSON.stringify(err.params)})`)
300
+ }
301
+ process.exit(1)
302
+ }
@@ -10,23 +10,23 @@ Run the specrails health check to validate that all prerequisites are correctly
10
10
  |-------|---------------|
11
11
  | Claude Code CLI | `claude` binary found in PATH |
12
12
  | Claude API key | `claude config list` shows a key OR `ANTHROPIC_API_KEY` env var set |
13
- | Agent files | `agents/` directory exists with at least 1 `AGENTS.md` file |
13
+ | Agent files | Generated agent files exist under `.claude/agents/` |
14
14
  | CLAUDE.md | `CLAUDE.md` present in the repo root |
15
15
  | Git initialized | `.git/` directory present |
16
16
  | npm | `npm` binary found in PATH |
17
17
 
18
18
  ## How to run
19
19
 
20
- This command delegates to the standalone health check script installed at `.specrails/bin/doctor.sh`. Run it directly:
20
+ This command uses the Node-native doctor runtime. Run it directly with:
21
21
 
22
22
  ```
23
- Bash tool: bash .specrails/bin/doctor.sh
23
+ npx specrails-core@latest doctor
24
24
  ```
25
25
 
26
- Or via the npm CLI wrapper:
26
+ If `specrails-core` is already on your `PATH`, this works too:
27
27
 
28
28
  ```
29
- npx specrails-core@latest doctor
29
+ specrails-core doctor
30
30
  ```
31
31
 
32
32
  ## Output
@@ -2,7 +2,7 @@
2
2
 
3
3
  Interactive wizard to configure the full agent workflow system for this repository. Analyzes the codebase, discovers target users, generates VPC personas, and creates all agents, commands, rules, and configuration adapted to this project. Supports config-driven mode for direct installation from a pre-built config file.
4
4
 
5
- **Prerequisites:** Run `specrails/install.sh` first to install templates.
5
+ **Prerequisites:** Ensure this repo was initialized with `npx specrails-core@latest init` (or via specrails-hub) so `.specrails/setup-templates/` exists.
6
6
 
7
7
  ### Hub Checkpoint Protocol
8
8
 
@@ -69,7 +69,7 @@ To resolve the model for any given agent:
69
69
 
70
70
  ### FC2: Provider Setup
71
71
 
72
- Read `.specrails/setup-templates/.provider-detection.json` if present, to get `cli_provider` and `specrails_dir`. If `FC_provider` differs from the detected value, use `FC_provider` (config takes precedence).
72
+ Use `FC_provider` from the config as the source of truth. Set `CLI_PROVIDER = FC_provider`, and set `SPECRAILS_DIR` to `.codex` when `CLI_PROVIDER == "codex"`, otherwise `.claude`.
73
73
 
74
74
  Set `SPECRAILS_DIR` and `CLI_PROVIDER` from resolved provider.
75
75
 
@@ -139,12 +139,12 @@ Read the following files to understand the current installation state:
139
139
  }
140
140
  ```
141
141
  If this file does not exist, inform the user:
142
- > "No `.specrails/specrails-manifest.json` found. This looks like a pre-versioning installation. Run `update.sh` first to initialize the manifest, then re-run `/specrails:enrich --update`."
142
+ > "No `.specrails/specrails-manifest.json` found. This repo predates the Node-native installer. Re-run `npx specrails-core@latest init` to refresh the install, then re-run `/specrails:enrich --update`."
143
143
  Then stop.
144
144
 
145
145
  2. Read `.specrails/specrails-version` โ€” contains the current version string (e.g., `0.2.0`). If it does not exist, treat version as `0.1.0 (legacy)`.
146
146
 
147
- 3. Determine `$SPECRAILS_DIR` by reading `.specrails/setup-templates/.provider-detection.json`. Extract `cli_provider` and `specrails_dir`. If not found, default to `cli_provider = "claude"`, `specrails_dir = ".claude"`.
147
+ 3. Determine `$SPECRAILS_DIR` from the existing install layout. If `.codex/` exists, use `cli_provider = "codex"` and `specrails_dir = ".codex"`. Otherwise use `cli_provider = "claude"` and `specrails_dir = ".claude"`.
148
148
 
149
149
  4. List all template files in `.specrails/setup-templates/agents/` โ€” these are the NEW agent templates from the update:
150
150
  ```bash
@@ -964,7 +964,7 @@ How should we interact with JIRA?
964
964
 
965
965
  Set `BACKLOG_WRITE=true/false`.
966
966
 
967
- <!-- no separate template โ€” this file IS the source (install.sh copies commands/setup.md directly) -->
967
+ <!-- This command is also mirrored into templates/commands/specrails/enrich.md for staged installs. -->
968
968
 
969
969
  #### Project Label
970
970
 
@@ -1090,7 +1090,7 @@ Wait for final confirmation.
1090
1090
 
1091
1091
  Read each template from `.specrails/setup-templates/` and generate the final files adapted to this project. Use the codebase analysis from Phase 1, personas from Phase 2, and configuration from Phase 3.
1092
1092
 
1093
- **Provider detection (required before any file generation):** Read `.specrails/setup-templates/.provider-detection.json` to determine `cli_provider` (`"claude"` or `"codex"`) and `specrails_dir` (`.claude` or `.codex`). All output paths in Phase 4 use `$SPECRAILS_DIR` as the base directory. If the file is missing, fall back to `cli_provider = "claude"` and `specrails_dir = ".claude"`.
1093
+ **Provider detection (required before any file generation):** Determine `cli_provider` from the existing install layout: use `"codex"` when `.codex/` exists, otherwise `"claude"`. Set `specrails_dir` to `.codex` or `.claude` accordingly. All output paths in Phase 4 use `$SPECRAILS_DIR` as the base directory.
1094
1094
 
1095
1095
  ### 4.1 Generate agents
1096
1096
 
@@ -1316,7 +1316,7 @@ Each rule file must:
1316
1316
 
1317
1317
  ### 4.6 Generate settings
1318
1318
 
1319
- Read `.specrails/setup-templates/.provider-detection.json` (written by `install.sh`) to determine `cli_provider` (`"claude"` or `"codex"`).
1319
+ Resolve `cli_provider` from the existing install layout (`.codex/` => `codex`, otherwise `claude`).
1320
1320
 
1321
1321
  **If `cli_provider == "claude"` (default):**
1322
1322
 
@@ -1376,7 +1376,7 @@ rm -rf .specrails/setup-templates/
1376
1376
  rm -f .claude/commands/enrich.md
1377
1377
 
1378
1378
  # 3. Remove the specrails/ directory from the repo if it exists at the root
1379
- # (it was only needed for install.sh and templates โ€” everything is now in .claude/)
1379
+ # (it was only needed for the retired shell installer and staging templates โ€” everything is now in .claude/)
1380
1380
  # NOTE: Only remove if it's inside this repo. Ask the user if unsure.
1381
1381
  ```
1382
1382
 
@@ -1413,7 +1413,7 @@ Apply the user's choice.
1413
1413
  After cleanup, verify that only the intended files remain:
1414
1414
 
1415
1415
  ```bash
1416
- # These should exist (the actual system) โ€” use $SPECRAILS_DIR from .provider-detection.json:
1416
+ # These should exist (the actual system) โ€” use the resolved $SPECRAILS_DIR:
1417
1417
  # If cli_provider == "claude":
1418
1418
  ls .claude/agents/sr-*.md
1419
1419
  ls .claude/agents/personas/*.md
@@ -0,0 +1,167 @@
1
+ /**
2
+ * specrails-core CLI entry point (Node / TypeScript).
3
+ *
4
+ * Replaces the legacy bash-dispatch path in `bin/specrails-core.js`.
5
+ * During the port, `bin/specrails-core.js` keeps shelling out to the
6
+ * shell scripts while this module carries the in-progress Node
7
+ * implementation. Once Phases 2โ€“4 ship the command handlers and
8
+ * Phase 5 deletes the shell scripts, the `bin/` dispatcher collapses
9
+ * to a single `import { main } from './dist/installer/cli.js'` call.
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { runDoctor } from './commands/doctor.js';
15
+ import { runInit } from './commands/init.js';
16
+ import { runPerfCheck } from './commands/perf-check.js';
17
+ import { runUpdate } from './commands/update.js';
18
+ import { isInstallerError } from './util/errors.js';
19
+ import { fatal } from './util/logger.js';
20
+ /**
21
+ * Minimal arg parser, no external dep. Handles:
22
+ * subcommand (first bare positional)
23
+ * --flag (boolean true)
24
+ * --flag=value (string)
25
+ * --flag value (string, consumes next token)
26
+ * -h / --help (help alias)
27
+ * -v / --version (version alias)
28
+ * positionals (everything else)
29
+ */
30
+ export function parseArgs(argv) {
31
+ const flags = {};
32
+ const positionals = [];
33
+ let subcommand = null;
34
+ for (let i = 0; i < argv.length; i++) {
35
+ const token = argv[i];
36
+ if (token.startsWith('--')) {
37
+ const rest = token.slice(2);
38
+ const eq = rest.indexOf('=');
39
+ if (eq >= 0) {
40
+ flags[rest.slice(0, eq)] = rest.slice(eq + 1);
41
+ }
42
+ else {
43
+ const peek = argv[i + 1];
44
+ if (peek !== undefined && !peek.startsWith('-')) {
45
+ flags[rest] = peek;
46
+ i++;
47
+ }
48
+ else {
49
+ flags[rest] = true;
50
+ }
51
+ }
52
+ }
53
+ else if (token === '-h') {
54
+ flags.help = true;
55
+ }
56
+ else if (token === '-v') {
57
+ flags.version = true;
58
+ }
59
+ else if (token.startsWith('-') && token.length > 1) {
60
+ flags[token.slice(1)] = true;
61
+ }
62
+ else {
63
+ if (subcommand === null) {
64
+ subcommand = token;
65
+ }
66
+ else {
67
+ positionals.push(token);
68
+ }
69
+ }
70
+ }
71
+ return { subcommand, flags, positionals };
72
+ }
73
+ function usageText() {
74
+ return [
75
+ '',
76
+ 'specrails-core โ€” AI agent workflow system',
77
+ '',
78
+ 'Usage:',
79
+ ' specrails-core <command> [options]',
80
+ '',
81
+ 'Commands:',
82
+ ' init Install specrails into a repository',
83
+ ' update Update an existing specrails installation',
84
+ ' doctor Diagnose the health of an existing installation',
85
+ ' perf-check Run the agent-workflow micro-benchmark',
86
+ ' help Show this help message',
87
+ ' version Print the installed version',
88
+ '',
89
+ 'Global options:',
90
+ ' -h, --help Show this help',
91
+ ' -v, --version Print version',
92
+ '',
93
+ ].join('\n');
94
+ }
95
+ function readVersion() {
96
+ // Resolve VERSION relative to the compiled module location.
97
+ // In src: src/installer/cli.ts โ†’ ../../VERSION
98
+ // In dist: dist/installer/cli.js โ†’ ../../VERSION
99
+ const here = path.dirname(fileURLToPath(import.meta.url));
100
+ const versionPath = path.resolve(here, '..', '..', 'VERSION');
101
+ try {
102
+ return readFileSync(versionPath, 'utf8').trim();
103
+ }
104
+ catch {
105
+ return 'unknown';
106
+ }
107
+ }
108
+ /**
109
+ * Sentinel exit code used during the port: when a Node command is not
110
+ * yet implemented, the CLI exits with this code and the outer bash
111
+ * dispatcher (bin/specrails-core.js) may choose to fall through to
112
+ * the shell path. Removed entirely in Phase 5 when no shell scripts
113
+ * remain.
114
+ */
115
+ export const NOT_IMPLEMENTED = 2;
116
+ async function dispatch(subcommand, flags, _positionals) {
117
+ switch (subcommand) {
118
+ case 'init':
119
+ await runInit(flags);
120
+ return 0;
121
+ case 'update':
122
+ await runUpdate(flags);
123
+ return 0;
124
+ case 'doctor': {
125
+ const result = await runDoctor(flags);
126
+ return result.failed === 0 ? 0 : 1;
127
+ }
128
+ case 'perf-check':
129
+ await runPerfCheck(flags);
130
+ return 0;
131
+ case 'help':
132
+ case '':
133
+ process.stdout.write(usageText());
134
+ return 0;
135
+ case 'version':
136
+ process.stdout.write(`${readVersion()}\n`);
137
+ return 0;
138
+ default:
139
+ process.stderr.write(`Unknown command: ${subcommand}\n`);
140
+ process.stdout.write(usageText());
141
+ return 1;
142
+ }
143
+ }
144
+ export async function main(argv = process.argv.slice(2)) {
145
+ const { subcommand, flags, positionals } = parseArgs(argv);
146
+ if (flags.help === true) {
147
+ process.stdout.write(usageText());
148
+ return 0;
149
+ }
150
+ if (flags.version === true) {
151
+ process.stdout.write(`${readVersion()}\n`);
152
+ return 0;
153
+ }
154
+ try {
155
+ return await dispatch(subcommand ?? '', flags, positionals);
156
+ }
157
+ catch (err) {
158
+ if (isInstallerError(err)) {
159
+ fatal(err.message);
160
+ return err.exitCode;
161
+ }
162
+ const e = err;
163
+ fatal(e.message || 'unexpected error', e.stack);
164
+ return 1;
165
+ }
166
+ }
167
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/installer/cli.ts"],"names":[],"mappings":"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"}