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.
- package/README.md +4 -4
- package/bin/specrails-core.mjs +302 -0
- package/commands/doctor.md +5 -5
- package/commands/enrich.md +9 -9
- package/dist/installer/cli.js +167 -0
- package/dist/installer/cli.js.map +1 -0
- package/dist/installer/commands/doctor.js +144 -0
- package/dist/installer/commands/doctor.js.map +1 -0
- package/dist/installer/commands/init.js +182 -0
- package/dist/installer/commands/init.js.map +1 -0
- package/dist/installer/commands/perf-check.js +16 -0
- package/dist/installer/commands/perf-check.js.map +1 -0
- package/dist/installer/commands/update.js +170 -0
- package/dist/installer/commands/update.js.map +1 -0
- package/dist/installer/phases/install-config.js +120 -0
- package/dist/installer/phases/install-config.js.map +1 -0
- package/dist/installer/phases/manifest.js +93 -0
- package/dist/installer/phases/manifest.js.map +1 -0
- package/dist/installer/phases/prereqs.js +116 -0
- package/dist/installer/phases/prereqs.js.map +1 -0
- package/dist/installer/phases/provider-detect.js +111 -0
- package/dist/installer/phases/provider-detect.js.map +1 -0
- package/dist/installer/phases/scaffold.js +373 -0
- package/dist/installer/phases/scaffold.js.map +1 -0
- package/dist/installer/util/errors.js +79 -0
- package/dist/installer/util/errors.js.map +1 -0
- package/dist/installer/util/exec.js +151 -0
- package/dist/installer/util/exec.js.map +1 -0
- package/dist/installer/util/fs.js +153 -0
- package/dist/installer/util/fs.js.map +1 -0
- package/dist/installer/util/git.js +113 -0
- package/dist/installer/util/git.js.map +1 -0
- package/dist/installer/util/logger.js +55 -0
- package/dist/installer/util/logger.js.map +1 -0
- package/dist/installer/util/paths.js +66 -0
- package/dist/installer/util/paths.js.map +1 -0
- package/dist/installer/util/prompts.js +49 -0
- package/dist/installer/util/prompts.js.map +1 -0
- package/dist/installer/util/template.js +60 -0
- package/dist/installer/util/template.js.map +1 -0
- package/docs/deployment.md +2 -1
- package/docs/installation.md +6 -3
- package/docs/testing/test-matrix-codex.md +19 -11
- package/docs/updating.md +24 -49
- package/docs/user-docs/faq.md +1 -1
- package/docs/windows.md +53 -0
- package/{templates/settings/integration-contract.json โ integration-contract.json} +2 -2
- package/package.json +25 -10
- package/pinned-versions.json +4 -0
- package/schemas/profile.v1.json +11 -3
- package/templates/agents/sr-architect.md +1 -1
- package/templates/agents/sr-reviewer.md +1 -1
- package/templates/commands/specrails/compat-check.md +3 -3
- package/templates/commands/specrails/doctor.md +5 -5
- package/templates/commands/specrails/enrich.md +9 -9
- package/templates/commands/specrails/reconfig.md +2 -2
- package/templates/commands/specrails/refactor-recommender.md +2 -2
- package/templates/commands/specrails/vpc-drift.md +1 -1
- package/templates/skills/sr-compat-check/SKILL.md +3 -3
- package/templates/skills/sr-refactor-recommender/SKILL.md +2 -2
- package/bin/doctor.sh +0 -127
- package/bin/perf-check.sh +0 -21
- package/bin/specrails-core.js +0 -262
- package/commands/setup.md +0 -1461
- package/install.sh +0 -1231
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|
package/commands/doctor.md
CHANGED
|
@@ -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 |
|
|
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
|
|
20
|
+
This command uses the Node-native doctor runtime. Run it directly with:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
|
|
23
|
+
npx specrails-core@latest doctor
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
If `specrails-core` is already on your `PATH`, this works too:
|
|
27
27
|
|
|
28
28
|
```
|
|
29
|
-
|
|
29
|
+
specrails-core doctor
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## Output
|
package/commands/enrich.md
CHANGED
|
@@ -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:**
|
|
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
|
-
|
|
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
|
|
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`
|
|
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
|
-
<!--
|
|
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):**
|
|
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
|
-
|
|
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
|
|
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
|
|
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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"}
|