specrails-core 4.1.1 → 4.2.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 (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/bin/doctor.sh DELETED
@@ -1,127 +0,0 @@
1
- #!/bin/bash
2
- # specrails doctor — health check for specrails-core installations
3
- # Usage: specrails doctor
4
- # Exit 0 if all checks pass, 1 if any check fails.
5
-
6
- set -uo pipefail
7
-
8
- # Colors
9
- GREEN='\033[0;32m'
10
- RED='\033[0;31m'
11
- NC='\033[0m'
12
- BOLD='\033[1m'
13
-
14
- PASS=0
15
- FAIL=0
16
- RESULTS=()
17
-
18
- pass() { RESULTS+=("$(printf "${GREEN}✅${NC} $1")"); PASS=$((PASS + 1)); }
19
- fail() { RESULTS+=("$(printf "${RED}❌${NC} $1\n Fix: $2")"); FAIL=$((FAIL + 1)); }
20
-
21
- echo ""
22
- echo -e "${BOLD}specrails doctor${NC}"
23
- echo ""
24
-
25
- # Determine project root (current working directory)
26
- PROJECT_ROOT="${PWD}"
27
-
28
- # ─────────────────────────────────────────────
29
- # Check 1: Claude Code CLI
30
- # ─────────────────────────────────────────────
31
- if CLAUDE_PATH=$(command -v claude 2>/dev/null); then
32
- pass "Claude Code CLI: found (${CLAUDE_PATH})"
33
- else
34
- fail "Claude Code CLI: not found" "Install Claude Code: https://claude.ai/download"
35
- fi
36
-
37
- # ─────────────────────────────────────────────
38
- # Check 2: Claude authentication
39
- # ─────────────────────────────────────────────
40
- if command -v claude &>/dev/null; then
41
- _claude_authed=false
42
- if claude config list 2>/dev/null | grep -q "api_key"; then
43
- _claude_authed=true
44
- elif [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
45
- _claude_authed=true
46
- elif [[ -f "${HOME}/.claude.json" ]] && grep -q '"oauthAccount"' "${HOME}/.claude.json" 2>/dev/null; then
47
- _claude_authed=true
48
- fi
49
-
50
- if [[ "$_claude_authed" == "true" ]]; then
51
- pass "Claude: authenticated"
52
- else
53
- fail "Claude: not authenticated" "Option 1: claude config set api_key <your-key> | Option 2: claude auth login"
54
- fi
55
- fi
56
-
57
- # ─────────────────────────────────────────────
58
- # Check 3: Agent files present
59
- # ─────────────────────────────────────────────
60
- AGENTS_DIR="${PROJECT_ROOT}/agents"
61
- if [[ -d "${AGENTS_DIR}" ]]; then
62
- AGENT_COUNT=$(find "${AGENTS_DIR}" -name "AGENTS.md" 2>/dev/null | wc -l | tr -d ' ')
63
- if [[ "${AGENT_COUNT}" -ge 1 ]]; then
64
- AGENT_NAMES=$(find "${AGENTS_DIR}" -name "AGENTS.md" -exec dirname {} \; | xargs -I{} basename {} | tr '\n' ', ' | sed 's/,$//')
65
- pass "Agent files: ${AGENT_COUNT} agent(s) found (${AGENT_NAMES})"
66
- else
67
- fail "Agent files: agents/ exists but no AGENTS.md found" "Run specrails-core init to set up agents"
68
- fi
69
- else
70
- fail "Agent files: agents/ directory not found" "Run specrails-core init to set up agents"
71
- fi
72
-
73
- # ─────────────────────────────────────────────
74
- # Check 4: CLAUDE.md present
75
- # ─────────────────────────────────────────────
76
- if [[ -f "${PROJECT_ROOT}/CLAUDE.md" ]]; then
77
- pass "CLAUDE.md: present"
78
- else
79
- fail "CLAUDE.md: missing" "Run /specrails:setup inside Claude Code to regenerate"
80
- fi
81
-
82
- # ─────────────────────────────────────────────
83
- # Check 5: Git initialized
84
- # ─────────────────────────────────────────────
85
- if [[ -d "${PROJECT_ROOT}/.git" ]]; then
86
- pass "Git: initialized"
87
- else
88
- fail "Git: not a git repository" "Initialize with: git init"
89
- fi
90
-
91
- # ─────────────────────────────────────────────
92
- # Check 6: npm present
93
- # ─────────────────────────────────────────────
94
- if NPM_VERSION=$(npm --version 2>/dev/null); then
95
- pass "npm: found (v${NPM_VERSION})"
96
- else
97
- fail "npm: not found" "Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm"
98
- fi
99
-
100
- # ─────────────────────────────────────────────
101
- # Output results
102
- # ─────────────────────────────────────────────
103
- for line in "${RESULTS[@]}"; do
104
- echo -e "${line}"
105
- done
106
-
107
- echo ""
108
-
109
- if [[ "${FAIL}" -eq 0 ]]; then
110
- TOTAL=$((PASS + FAIL))
111
- echo -e "All ${TOTAL} checks passed. Run ${BOLD}/specrails:get-backlog-specs${NC} to get started."
112
- else
113
- echo "${FAIL} check(s) failed."
114
- fi
115
-
116
- # ─────────────────────────────────────────────
117
- # Append to ~/.specrails/doctor.log
118
- # ─────────────────────────────────────────────
119
- LOG_DIR="${HOME}/.specrails"
120
- mkdir -p "${LOG_DIR}"
121
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u)
122
- TOTAL=$((PASS + FAIL))
123
- echo "${TIMESTAMP} checks=${TOTAL} passed=${PASS} failed=${FAIL}" >> "${LOG_DIR}/doctor.log"
124
-
125
- echo ""
126
-
127
- exit $([[ "${FAIL}" -eq 0 ]] && echo 0 || echo 1)
package/bin/perf-check.sh DELETED
@@ -1,21 +0,0 @@
1
- #!/bin/bash
2
- # Performance regression check for specrails-core.
3
- # This is a template/installer repo — no runtime benchmarks apply.
4
- # Reports NO_PERF_IMPACT so the CI workflow exits cleanly.
5
- set -euo pipefail
6
-
7
- FILES="${MODIFIED_FILES_LIST:-}"
8
- CONTEXT="${2:-}"
9
-
10
- # Determine if any performance-sensitive files were modified.
11
- # For specrails-core (a shell installer + markdown template repo), there are
12
- # no runtime execution paths to benchmark. All changes are to installer scripts
13
- # or template files that have no measurable latency or throughput impact.
14
-
15
- echo "specrails-core performance check"
16
- echo "Modified files: ${FILES:-<none>}"
17
- echo ""
18
- echo "This repository contains shell installer scripts and Markdown templates."
19
- echo "No runtime benchmarks apply — skipping performance regression analysis."
20
- echo ""
21
- echo "PERF_STATUS: NO_PERF_IMPACT"
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { spawnSync } = require("child_process");
4
- const { resolve } = require("path");
5
-
6
- const ROOT = resolve(__dirname, "..");
7
- const COMMANDS = {
8
- init: "install.sh",
9
- update: "update.sh",
10
- doctor: "bin/doctor.sh",
11
- "perf-check": "bin/perf-check.sh",
12
- enrich: null,
13
- version: null,
14
- profile: null,
15
- };
16
-
17
- const args = process.argv.slice(2);
18
- const subcommand = args[0];
19
-
20
- // ─── Global --version / -V flag ──────────────────────────────────────────────
21
-
22
- if (args.includes("--version") || args.includes("-V")) {
23
- const pkg = require(resolve(ROOT, "package.json"));
24
- console.log(`specrails-core v${pkg.version}`);
25
- process.exit(0);
26
- }
27
-
28
- if (!subcommand) {
29
- console.log(`specrails-core — Agent Workflow System for Claude Code
30
-
31
- Usage:
32
- specrails-core init [--root-dir <path>] [--yes|-y] Install into a repository
33
- specrails-core update [--only <component>] Update an existing installation
34
- specrails-core doctor Run health checks
35
- specrails-core perf-check [--files <list>] Performance regression check (CI)
36
- specrails-core enrich [--from-config <path>] Run /specrails:enrich via Claude CLI
37
- specrails-core profile <validate|show> [<path>] Validate or pretty-print a profile JSON
38
- specrails-core version Show installed version
39
-
40
- Flags for init:
41
- --root-dir <path> Target repository path (default: current directory)
42
- --yes | -y Non-interactive; use defaults, skip TUI
43
- --provider <value> Force provider: claude or codex
44
- --no-direct Skip TUI; use the legacy interactive bash installer
45
- --from-config Skip TUI; use existing .specrails/install-config.yaml
46
-
47
- Global flags:
48
- --version | -V Show installed version
49
-
50
- More info: https://github.com/fjpulidop/specrails-core`);
51
- process.exit(0);
52
- }
53
-
54
- if (!(subcommand in COMMANDS)) {
55
- console.error(`Unknown command: ${subcommand}\n`);
56
- console.error("Available commands: init, update, doctor, perf-check, enrich, version");
57
- process.exit(1);
58
- }
59
-
60
- const script = COMMANDS[subcommand];
61
-
62
- // Allowlisted flags per subcommand
63
- const ALLOWED_FLAGS = {
64
- init: ["--root-dir", "--yes", "-y", "--provider", "--no-direct", "--from-config", "--quick", "--hub-json", "--agent-teams"],
65
- update: ["--only"],
66
- doctor: [],
67
- "perf-check": ["--files", "--context"],
68
- enrich: ["--from-config", "--quick"],
69
- version: [],
70
- profile: [],
71
- };
72
-
73
- const subargs = args.slice(1);
74
- const allowed = ALLOWED_FLAGS[subcommand] ?? [];
75
-
76
- for (const arg of subargs) {
77
- if (arg.startsWith("-") && !allowed.includes(arg)) {
78
- console.error(`Unknown flag: ${arg}`);
79
- process.exit(1);
80
- }
81
- }
82
-
83
- // ─── version subcommand ──────────────────────────────────────────────────────
84
-
85
- if (subcommand === "version") {
86
- const pkg = require(resolve(ROOT, "package.json"));
87
- console.log(`specrails-core v${pkg.version}`);
88
- process.exit(0);
89
- }
90
-
91
- // ─── profile subcommand ──────────────────────────────────────────────────────
92
- // `specrails-core profile validate [path]` — validate a profile JSON against v1 schema
93
- // `specrails-core profile show [path]` — pretty-print the resolved profile
94
- // Resolution order when no path: $SPECRAILS_PROFILE_PATH → .specrails/profiles/project-default.json
95
-
96
- if (subcommand === "profile") {
97
- const { existsSync, readFileSync } = require("fs");
98
- const action = subargs[0];
99
- const pathArg = subargs[1];
100
-
101
- if (!action || (action !== "validate" && action !== "show")) {
102
- console.error("Usage: specrails-core profile validate [<path>]");
103
- console.error(" specrails-core profile show [<path>]");
104
- process.exit(1);
105
- }
106
-
107
- const resolveProfilePath = () => {
108
- if (pathArg) return resolve(pathArg);
109
- if (process.env.SPECRAILS_PROFILE_PATH) return resolve(process.env.SPECRAILS_PROFILE_PATH);
110
- const projectDefault = resolve(process.cwd(), ".specrails/profiles/project-default.json");
111
- if (existsSync(projectDefault)) return projectDefault;
112
- return null;
113
- };
114
-
115
- const profilePath = resolveProfilePath();
116
- if (!profilePath) {
117
- console.error("No profile path given and none could be resolved.");
118
- console.error("Pass an explicit path or set SPECRAILS_PROFILE_PATH, or place a profile at");
119
- console.error(" .specrails/profiles/project-default.json");
120
- process.exit(1);
121
- }
122
- if (!existsSync(profilePath)) {
123
- console.error(`Profile file not found: ${profilePath}`);
124
- process.exit(1);
125
- }
126
-
127
- let profile;
128
- try {
129
- profile = JSON.parse(readFileSync(profilePath, "utf8"));
130
- } catch (e) {
131
- console.error(`Profile is not valid JSON: ${e.message}`);
132
- process.exit(1);
133
- }
134
-
135
- if (action === "show") {
136
- console.log(JSON.stringify(profile, null, 2));
137
- process.exit(0);
138
- }
139
-
140
- // action === "validate"
141
- const schemaPath = resolve(ROOT, "schemas/profile.v1.json");
142
- if (!existsSync(schemaPath)) {
143
- console.error(`Schema not found at ${schemaPath} — install may be corrupt`);
144
- process.exit(1);
145
- }
146
-
147
- let Ajv;
148
- try {
149
- Ajv = require("ajv/dist/2020.js").default;
150
- } catch {
151
- console.error("'ajv' is not installed. Run `npm install` in the specrails-core package directory first,");
152
- console.error("or rely on the hub's own validator for user-facing flows.");
153
- process.exit(1);
154
- }
155
-
156
- const schema = JSON.parse(readFileSync(schemaPath, "utf8"));
157
- const ajv = new Ajv({ allErrors: true, strict: false });
158
- const validate = ajv.compile(schema);
159
-
160
- if (validate(profile)) {
161
- console.log(`✓ ${profilePath} is a valid v1 profile.`);
162
- process.exit(0);
163
- } else {
164
- console.error(`✗ ${profilePath} failed validation:`);
165
- for (const err of validate.errors || []) {
166
- console.error(` ${err.instancePath || "/"} ${err.message} (${JSON.stringify(err.params)})`);
167
- }
168
- process.exit(1);
169
- }
170
- }
171
-
172
- // ─── enrich subcommand ───────────────────────────────────────────────────────
173
- // Launches `claude --command "/specrails:enrich [flags]"` so the AI-powered
174
- // enrichment runs inside Claude Code with full model access.
175
-
176
- if (subcommand === "enrich") {
177
- const enrichFlags = subargs.join(" ");
178
- const claudeCmd = `/specrails:enrich${enrichFlags ? " " + enrichFlags : ""}`;
179
- const claudeResult = spawnSync("claude", ["--command", claudeCmd, "--dangerously-skip-permissions"], {
180
- stdio: "inherit",
181
- cwd: process.cwd(),
182
- });
183
-
184
- if (claudeResult.error) {
185
- console.error(
186
- "\nFailed to launch Claude CLI for enrich:",
187
- claudeResult.error.message,
188
- "\nEnsure Claude Code is installed: npm install -g @anthropic-ai/claude-code\n"
189
- );
190
- process.exit(1);
191
- }
192
-
193
- process.exit(claudeResult.status ?? (claudeResult.error ? 1 : 0));
194
- }
195
-
196
- // ─── Direct mode (TUI) for `init` ─────────────────────────────────────────────
197
- //
198
- // Default behaviour: run the Node.js TUI to collect agent/model configuration,
199
- // write .specrails/install-config.yaml, then hand off to install.sh.
200
- //
201
- // Opt-out with: --no-direct (legacy interactive bash installer)
202
- // --from-config (config already on disk; skip TUI)
203
- // --yes / -y (write default config, no prompts)
204
-
205
- const isInit = subcommand === "init";
206
- const hasNoTui = subargs.includes("--no-direct") || subargs.includes("--from-config");
207
- const autoYes = subargs.includes("--yes") || subargs.includes("-y");
208
- const useTui = isInit && !hasNoTui;
209
-
210
- if (useTui) {
211
- // Resolve the target directory for the TUI
212
- const rootDirIdx = subargs.indexOf("--root-dir");
213
- const rootDir = rootDirIdx >= 0 ? resolve(subargs[rootDirIdx + 1]) : process.cwd();
214
-
215
- // Build TUI args: pass rootDir + --yes if set
216
- const tuiArgs = [resolve(ROOT, "bin/tui-installer.mjs"), rootDir];
217
- if (autoYes) tuiArgs.push("--yes");
218
-
219
- const tuiResult = spawnSync("node", tuiArgs, {
220
- stdio: "inherit",
221
- cwd: process.cwd(),
222
- });
223
-
224
- if (tuiResult.error) {
225
- // @inquirer/prompts not installed (e.g. during development without npm install)
226
- console.error(
227
- "\nFailed to launch TUI installer:",
228
- tuiResult.error.message,
229
- "\nRun: npm install or use --no-direct for the legacy installer.\n"
230
- );
231
- process.exit(1);
232
- }
233
-
234
- if (tuiResult.status !== 0) {
235
- process.exit(tuiResult.status ?? 1);
236
- }
237
-
238
- // TUI succeeded — run install.sh with --from-config so it reads provider/
239
- // agent_teams from install-config.yaml rather than prompting interactively.
240
- const installArgs = subargs
241
- .filter(a => a !== "--no-direct") // strip internal flags
242
- .concat(["--yes", "--from-config"]);
243
-
244
- const result = spawnSync("bash", [resolve(ROOT, script), ...installArgs], {
245
- stdio: "inherit",
246
- cwd: process.cwd(),
247
- });
248
-
249
- process.exit(result.status ?? (result.error ? 1 : 0));
250
- }
251
-
252
- // ─── Legacy / non-TUI path ────────────────────────────────────────────────────
253
-
254
- // Strip only --no-direct (internal flag) before passing args to the shell script
255
- const cleanArgs = subargs.filter(a => a !== "--no-direct");
256
-
257
- const result = spawnSync("bash", [resolve(ROOT, script), ...cleanArgs], {
258
- stdio: "inherit",
259
- cwd: process.cwd(),
260
- });
261
-
262
- process.exit(result.status ?? (result.error ? 1 : 0));