specrails-core 4.1.0 → 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 (67) hide show
  1. package/README.md +4 -4
  2. package/VERSION +1 -1
  3. package/bin/specrails-core.mjs +302 -0
  4. package/commands/doctor.md +5 -5
  5. package/commands/enrich.md +9 -9
  6. package/dist/installer/cli.js +167 -0
  7. package/dist/installer/cli.js.map +1 -0
  8. package/dist/installer/commands/doctor.js +144 -0
  9. package/dist/installer/commands/doctor.js.map +1 -0
  10. package/dist/installer/commands/init.js +182 -0
  11. package/dist/installer/commands/init.js.map +1 -0
  12. package/dist/installer/commands/perf-check.js +16 -0
  13. package/dist/installer/commands/perf-check.js.map +1 -0
  14. package/dist/installer/commands/update.js +170 -0
  15. package/dist/installer/commands/update.js.map +1 -0
  16. package/dist/installer/phases/install-config.js +120 -0
  17. package/dist/installer/phases/install-config.js.map +1 -0
  18. package/dist/installer/phases/manifest.js +93 -0
  19. package/dist/installer/phases/manifest.js.map +1 -0
  20. package/dist/installer/phases/prereqs.js +116 -0
  21. package/dist/installer/phases/prereqs.js.map +1 -0
  22. package/dist/installer/phases/provider-detect.js +111 -0
  23. package/dist/installer/phases/provider-detect.js.map +1 -0
  24. package/dist/installer/phases/scaffold.js +373 -0
  25. package/dist/installer/phases/scaffold.js.map +1 -0
  26. package/dist/installer/util/errors.js +79 -0
  27. package/dist/installer/util/errors.js.map +1 -0
  28. package/dist/installer/util/exec.js +151 -0
  29. package/dist/installer/util/exec.js.map +1 -0
  30. package/dist/installer/util/fs.js +153 -0
  31. package/dist/installer/util/fs.js.map +1 -0
  32. package/dist/installer/util/git.js +113 -0
  33. package/dist/installer/util/git.js.map +1 -0
  34. package/dist/installer/util/logger.js +55 -0
  35. package/dist/installer/util/logger.js.map +1 -0
  36. package/dist/installer/util/paths.js +66 -0
  37. package/dist/installer/util/paths.js.map +1 -0
  38. package/dist/installer/util/prompts.js +49 -0
  39. package/dist/installer/util/prompts.js.map +1 -0
  40. package/dist/installer/util/template.js +60 -0
  41. package/dist/installer/util/template.js.map +1 -0
  42. package/docs/deployment.md +2 -1
  43. package/docs/installation.md +6 -3
  44. package/docs/testing/test-matrix-codex.md +19 -11
  45. package/docs/updating.md +24 -49
  46. package/docs/user-docs/faq.md +1 -1
  47. package/docs/windows.md +53 -0
  48. package/{templates/settings/integration-contract.json → integration-contract.json} +2 -2
  49. package/package.json +25 -10
  50. package/pinned-versions.json +4 -0
  51. package/schemas/profile.v1.json +11 -3
  52. package/templates/agents/sr-architect.md +1 -1
  53. package/templates/agents/sr-reviewer.md +1 -1
  54. package/templates/commands/specrails/compat-check.md +3 -3
  55. package/templates/commands/specrails/doctor.md +5 -5
  56. package/templates/commands/specrails/enrich.md +9 -9
  57. package/templates/commands/specrails/reconfig.md +2 -2
  58. package/templates/commands/specrails/refactor-recommender.md +2 -2
  59. package/templates/commands/specrails/vpc-drift.md +1 -1
  60. package/templates/skills/sr-compat-check/SKILL.md +3 -3
  61. package/templates/skills/sr-refactor-recommender/SKILL.md +2 -2
  62. package/bin/doctor.sh +0 -127
  63. package/bin/perf-check.sh +0 -21
  64. package/bin/specrails-core.js +0 -262
  65. package/commands/setup.md +0 -1461
  66. package/install.sh +0 -1231
  67. package/update.sh +0 -870
package/install.sh DELETED
@@ -1,1231 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
-
4
- # specrails installer
5
- # Installs the agent workflow system into any repository.
6
- # Step 1 of 2: Prerequisites + scaffold. Step 2: Run /specrails:enrich inside Claude Code.
7
- #
8
- # ─────────────────────────────────────────────────────────────────────────────
9
- # Reserved paths (MUST NOT be created, modified, or deleted by this script):
10
- # - <repo>/.specrails/profiles/** (project + hub-authored profile JSON)
11
- # - <repo>/.claude/agents/custom-*.md (user-authored custom agents)
12
- #
13
- # Rationale: these paths hold user/team configuration that must survive
14
- # re-running the installer. specrails-hub writes profile files here.
15
- # Audited by tests/test-profiles.sh.
16
- #
17
- # Other paths under .specrails/ (install-config.yaml, specrails-version,
18
- # specrails-manifest.json, setup-templates/) ARE managed by this script.
19
- # ─────────────────────────────────────────────────────────────────────────────
20
-
21
- # Detect pipe mode (curl | bash) vs local execution
22
- if [[ -z "${BASH_SOURCE[0]:-}" || "${BASH_SOURCE[0]:-}" == "bash" ]]; then
23
- # Running via pipe — clone repo to temp dir
24
- SPECRAILS_TMPDIR="$(mktemp -d)"
25
- trap 'rm -rf "$SPECRAILS_TMPDIR"' EXIT
26
- git clone --depth 1 https://github.com/fjpulidop/specrails.git "$SPECRAILS_TMPDIR/specrails" 2>/dev/null || {
27
- echo "Error: failed to clone specrails repository." >&2
28
- exit 1
29
- }
30
- SCRIPT_DIR="$SPECRAILS_TMPDIR/specrails"
31
- else
32
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
33
- fi
34
- REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
35
-
36
- # Colors
37
- RED='\033[0;31m'
38
- GREEN='\033[0;32m'
39
- YELLOW='\033[1;33m'
40
- BLUE='\033[0;34m'
41
- CYAN='\033[0;36m'
42
- BOLD='\033[1m'
43
- NC='\033[0m'
44
-
45
- # ─────────────────────────────────────────────
46
- # Argument parsing
47
- # ─────────────────────────────────────────────
48
-
49
- CUSTOM_ROOT_DIR=""
50
- AUTO_YES=false
51
- # Set SPECRAILS_SKIP_PREREQS=1 to bypass hard-exit prerequisite checks (for CI/testing).
52
- SKIP_PREREQS="${SPECRAILS_SKIP_PREREQS:-0}"
53
-
54
- # Provider detection results (set in Phase 1)
55
- CLI_PROVIDER=""
56
- SPECRAILS_DIR=""
57
- INSTRUCTIONS_FILE=""
58
- HAS_CLAUDE=false
59
- HAS_CODEX=false
60
- AGENT_TEAMS=false
61
-
62
- # Direct-mode flags (set by bin/specrails-core.js after TUI completes)
63
- FROM_CONFIG=false # read provider/agent_teams from .specrails/install-config.yaml
64
- CONFIG_PATH="" # explicit config file path (passed after --from-config)
65
- HAS_GUM=false # set to true if gum CLI is available (optional UI enhancement)
66
- TIER="full" # install tier: full (default) or quick (template-only, no enrich)
67
- HUB_JSON=false # emit JSON checkpoint lines for programmatic consumption (specrails-hub)
68
-
69
- while [[ $# -gt 0 ]]; do
70
- case "$1" in
71
- --root-dir)
72
- if [[ -z "${2:-}" ]]; then
73
- echo "Error: --root-dir requires a path argument." >&2
74
- exit 1
75
- fi
76
- CUSTOM_ROOT_DIR="$2"
77
- shift 2
78
- ;;
79
- --yes|-y)
80
- AUTO_YES=true
81
- shift
82
- ;;
83
- --provider)
84
- if [[ -z "${2:-}" ]]; then
85
- echo "Error: --provider requires a value (claude)." >&2
86
- exit 1
87
- fi
88
- if [[ "$2" == "codex" ]]; then
89
- echo "" >&2
90
- echo " ⚠ Codex (OpenAI) support: Coming Soon" >&2
91
- echo " Currently being tested in our lab. Please use --provider claude for now." >&2
92
- echo "" >&2
93
- exit 1
94
- fi
95
- if [[ "$2" != "claude" ]]; then
96
- echo "Error: --provider value must be 'claude', got: $2" >&2
97
- exit 1
98
- fi
99
- CLI_PROVIDER="$2"
100
- shift 2
101
- ;;
102
- --from-config)
103
- # Config was written by the TUI; skip interactive prompts and read
104
- # provider + agent_teams from install-config.yaml.
105
- # Optional: --from-config <path> (defaults to .specrails/install-config.yaml)
106
- FROM_CONFIG=true
107
- if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
108
- CONFIG_PATH="${2}"
109
- shift 2
110
- else
111
- shift
112
- fi
113
- ;;
114
- --agent-teams)
115
- # Explicitly enable agent teams commands (alternative to config file).
116
- AGENT_TEAMS=true
117
- shift
118
- ;;
119
- --quick)
120
- # Template-only install; sets tier=quick in the generated config.
121
- TIER="quick"
122
- shift
123
- ;;
124
- --hub-json)
125
- # Emit JSON checkpoint lines for programmatic consumption by specrails-hub.
126
- HUB_JSON=true
127
- shift
128
- ;;
129
- *)
130
- echo "Unknown argument: $1" >&2
131
- echo "Usage: install.sh [--root-dir <path>] [--yes|-y] [--provider <claude>] [--from-config [<path>]] [--agent-teams] [--quick] [--hub-json]" >&2
132
- exit 1
133
- ;;
134
- esac
135
- done
136
-
137
- # Override REPO_ROOT if --root-dir was provided
138
- if [[ -n "$CUSTOM_ROOT_DIR" ]]; then
139
- REPO_ROOT="$(cd "$CUSTOM_ROOT_DIR" 2>/dev/null && pwd)" || {
140
- echo "Error: --root-dir path does not exist or is not accessible: $CUSTOM_ROOT_DIR" >&2
141
- exit 1
142
- }
143
- if [[ ! -d "$REPO_ROOT" ]]; then
144
- echo "Error: --root-dir path is not a directory: $CUSTOM_ROOT_DIR" >&2
145
- exit 1
146
- fi
147
- fi
148
-
149
- # Detect if running from within the specrails source repo itself
150
- # Note: REPO_ROOT must be non-empty for the glob match — when empty, * matches everything.
151
- if [[ -z "$CUSTOM_ROOT_DIR" && -n "$REPO_ROOT" && -f "$SCRIPT_DIR/install.sh" && -d "$SCRIPT_DIR/templates" && "$SCRIPT_DIR" == "$REPO_ROOT"* ]]; then
152
- # We're inside the specrails source — ask for target repo
153
- echo ""
154
- echo -e "${YELLOW}⚠${NC} You're running the installer from inside the specrails source repo."
155
- echo -e " specrails installs into a ${BOLD}target${NC} repository, not into itself."
156
- echo ""
157
- read -p " Enter the path to the target repo (or 'q' to quit): " TARGET_PATH || TARGET_PATH="q"
158
- if [[ "$TARGET_PATH" == "q" || -z "$TARGET_PATH" ]]; then
159
- echo " Aborted. No changes made."
160
- exit 0
161
- fi
162
- # Expand ~ and resolve path
163
- TARGET_PATH="${TARGET_PATH/#\~/$HOME}"
164
- REPO_ROOT="$(cd "$TARGET_PATH" 2>/dev/null && pwd)" || {
165
- echo "Error: path does not exist or is not accessible: $TARGET_PATH" >&2
166
- exit 1
167
- }
168
- fi
169
-
170
- # Auto-init git if the target directory is not a git repository
171
- if [[ -z "$REPO_ROOT" || ! -d "$REPO_ROOT/.git" ]]; then
172
- # Resolve REPO_ROOT to cwd if still empty (no git found anywhere)
173
- if [[ -z "$REPO_ROOT" ]]; then
174
- REPO_ROOT="$(pwd)"
175
- fi
176
- if [[ "$AUTO_YES" == true ]]; then
177
- git -C "$REPO_ROOT" init -q 2>/dev/null
178
- else
179
- echo ""
180
- echo -e "${YELLOW}⚠${NC} ${REPO_ROOT} is not a git repository."
181
- read -p " Initialize git? (y/n): " INIT_GIT || INIT_GIT="n"
182
- if [[ "$INIT_GIT" == "y" || "$INIT_GIT" == "Y" ]]; then
183
- git -C "$REPO_ROOT" init -q 2>/dev/null
184
- else
185
- echo " Aborted. specrails requires a git repository."
186
- exit 0
187
- fi
188
- fi
189
- fi
190
-
191
- print_header() {
192
- echo ""
193
- echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════╗${NC}"
194
- echo -e "${BOLD}${CYAN}║ specrails installer v0.1 ║${NC}"
195
- echo -e "${BOLD}${CYAN}║ Agent Workflow System (AI-native) ║${NC}"
196
- echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════╝${NC}"
197
- echo ""
198
- }
199
-
200
- ok() { echo -e " ${GREEN}✓${NC} $1"; }
201
- warn() { echo -e " ${YELLOW}⚠${NC} $1"; }
202
- fail() { echo -e " ${RED}✗${NC} $1"; }
203
- info() { echo -e " ${BLUE}→${NC} $1"; }
204
- step() { echo -e "\n${BOLD}$1${NC}"; }
205
-
206
- generate_manifest() {
207
- local version
208
- version="$(cat "$SCRIPT_DIR/VERSION")"
209
-
210
- local installed_at
211
- installed_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
212
-
213
- # Write version file
214
- printf '%s\n' "$version" > "$REPO_ROOT/.specrails/specrails-version"
215
-
216
- # Build artifact checksums for all files under templates/
217
- local artifacts_json=""
218
- local first=true
219
- while IFS= read -r -d '' filepath; do
220
- local relpath
221
- relpath="templates/${filepath#"$SCRIPT_DIR/templates/"}"
222
- local checksum
223
- checksum="sha256:$(shasum -a 256 "$filepath" | awk '{print $1}')"
224
- if [ "$first" = true ]; then
225
- first=false
226
- else
227
- artifacts_json="${artifacts_json},"
228
- fi
229
- artifacts_json="${artifacts_json}
230
- \"${relpath}\": \"${checksum}\""
231
- done < <(find "$SCRIPT_DIR/templates" -type f -not -path '*/node_modules/*' -not -name 'package-lock.json' -print0 | sort -z)
232
-
233
- # Include commands/enrich.md
234
- local enrich_checksum
235
- enrich_checksum="sha256:$(shasum -a 256 "$SCRIPT_DIR/commands/enrich.md" | awk '{print $1}')"
236
- if [ -n "$artifacts_json" ]; then
237
- artifacts_json="${artifacts_json},"
238
- fi
239
- artifacts_json="${artifacts_json}
240
- \"commands/specrails/enrich.md\": \"${enrich_checksum}\""
241
-
242
- # Include commands/doctor.md
243
- local doctor_checksum
244
- doctor_checksum="sha256:$(shasum -a 256 "$SCRIPT_DIR/commands/doctor.md" | awk '{print $1}')"
245
- artifacts_json="${artifacts_json},
246
- \"commands/specrails/doctor.md\": \"${doctor_checksum}\""
247
-
248
- cat > "$REPO_ROOT/.specrails/specrails-manifest.json" << EOF
249
- {
250
- "version": "${version}",
251
- "installed_at": "${installed_at}",
252
- "artifacts": {${artifacts_json}
253
- }
254
- }
255
- EOF
256
- }
257
-
258
- # ─────────────────────────────────────────────
259
- # Phase 1: Prerequisites
260
- # ─────────────────────────────────────────────
261
-
262
- print_header
263
-
264
- step "Phase 1: Checking prerequisites"
265
-
266
- # 1.1 Git repository (should be resolved by now — early init or --root-dir)
267
- if [[ -z "$REPO_ROOT" ]]; then
268
- fail "Could not determine target directory."
269
- echo " Usage: install.sh [--root-dir <path>]"
270
- exit 1
271
- fi
272
- if [[ -n "$CUSTOM_ROOT_DIR" ]]; then
273
- ok "Install root (--root-dir): $REPO_ROOT"
274
- else
275
- ok "Git repository root: $REPO_ROOT"
276
- fi
277
-
278
- # 1.2 Provider detection (Claude Code vs Codex)
279
- if command -v claude &> /dev/null; then
280
- HAS_CLAUDE=true
281
- fi
282
- if command -v codex &> /dev/null; then
283
- HAS_CODEX=true
284
- fi
285
-
286
- # 1.2a Gum detection (optional UI enhancement — used in non-TUI bash paths)
287
- if command -v gum &> /dev/null; then
288
- HAS_GUM=true
289
- ok "gum CLI: found (enhanced UI available)"
290
- else
291
- # Attempt a lightweight auto-install on supported platforms (best-effort)
292
- _GUM_INSTALLED=false
293
- if command -v brew &> /dev/null; then
294
- info "Installing gum CLI via Homebrew (optional — adds nicer prompts)..."
295
- if brew install gum &>/dev/null; then
296
- HAS_GUM=true
297
- _GUM_INSTALLED=true
298
- ok "gum installed via Homebrew"
299
- fi
300
- fi
301
- if [[ "$_GUM_INSTALLED" == false ]]; then
302
- info "gum CLI not found — install via 'brew install gum' for enhanced prompts (optional)"
303
- fi
304
- fi
305
-
306
- if [[ "$FROM_CONFIG" == true ]]; then
307
- # Config was written by the TUI — read provider and agent_teams from it.
308
- # Resolve config path: explicit path > default location.
309
- if [[ -z "$CONFIG_PATH" ]]; then
310
- CONFIG_PATH="${REPO_ROOT}/.specrails/install-config.yaml"
311
- fi
312
- if [[ -f "$CONFIG_PATH" ]]; then
313
- _cfg_version=$(grep '^version:' "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]' || true)
314
- _cfg_provider=$(grep '^provider:' "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]' || true)
315
- _cfg_agent_teams=$(grep '^agent_teams:' "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]' || true)
316
- _cfg_tier=$(grep '^tier:' "$CONFIG_PATH" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]' || true)
317
- _cfg_preset=$(grep '^\s*preset:' "$CONFIG_PATH" 2>/dev/null | head -1 | awk '{print $2}' | tr -d '[:space:]' || true)
318
- _cfg_has_agents=$(grep -c '^\s*selected:' "$CONFIG_PATH" 2>/dev/null || true)
319
-
320
- _config_errors=0
321
- if [[ -z "$_cfg_version" ]]; then
322
- fail "Invalid config: missing required 'version' field"
323
- _config_errors=$(( _config_errors + 1 ))
324
- elif [[ "$_cfg_version" != "1" ]]; then
325
- fail "Invalid config: unsupported version '$_cfg_version' (expected: 1)"
326
- _config_errors=$(( _config_errors + 1 ))
327
- fi
328
- if [[ -z "$_cfg_provider" ]]; then
329
- fail "Invalid config: missing required 'provider' field"
330
- _config_errors=$(( _config_errors + 1 ))
331
- elif [[ "$_cfg_provider" == "codex" ]]; then
332
- fail "Codex (OpenAI) support is coming soon — currently being tested in our lab."
333
- fail "Please edit install-config.yaml and set: provider: claude"
334
- _config_errors=$(( _config_errors + 1 ))
335
- elif [[ "$_cfg_provider" != "claude" ]]; then
336
- fail "Invalid config: unsupported provider '$_cfg_provider' (expected: claude)"
337
- _config_errors=$(( _config_errors + 1 ))
338
- fi
339
- if [[ "$_cfg_has_agents" -eq 0 ]]; then
340
- fail "Invalid config: missing required 'agents' section with 'selected' list"
341
- _config_errors=$(( _config_errors + 1 ))
342
- fi
343
- if [[ -n "$_cfg_tier" && "$_cfg_tier" != "full" && "$_cfg_tier" != "quick" ]]; then
344
- fail "Invalid config: unsupported tier '$_cfg_tier' (expected: full or quick)"
345
- _config_errors=$(( _config_errors + 1 ))
346
- fi
347
- if [[ -n "$_cfg_preset" && "$_cfg_preset" != "balanced" && "$_cfg_preset" != "budget" && "$_cfg_preset" != "max" ]]; then
348
- fail "Invalid config: unsupported preset '$_cfg_preset' (expected: balanced, budget, or max)"
349
- _config_errors=$(( _config_errors + 1 ))
350
- fi
351
-
352
- # Warn about unknown agents in selected list
353
- if [[ "$_cfg_has_agents" -gt 0 ]]; then
354
- _selected_line=$(grep '^\s*selected:' "$CONFIG_PATH" 2>/dev/null | head -1 || true)
355
- if echo "$_selected_line" | grep -q '\[' 2>/dev/null; then
356
- _selected_agents=$(echo "$_selected_line" | sed 's/.*\[//;s/\].*//;s/,/\n/g' | tr -d ' ')
357
- else
358
- _selected_agents=$(grep -A100 '^\s*selected:' "$CONFIG_PATH" 2>/dev/null \
359
- | tail -n +2 | grep '^\s*-\s*' | sed 's/^\s*-\s*//' | tr -d ' ' || true)
360
- fi
361
- while IFS= read -r _agent_name; do
362
- [[ -z "$_agent_name" ]] && continue
363
- if [[ ! -f "$SCRIPT_DIR/templates/agents/${_agent_name}.md" ]]; then
364
- warn "Unknown agent in selected list: ${_agent_name}"
365
- fi
366
- done <<< "$_selected_agents"
367
- fi
368
-
369
- if [[ "$_config_errors" -gt 0 ]]; then
370
- fail "Config validation failed with ${_config_errors} error(s) — aborting"
371
- exit 1
372
- fi
373
-
374
- if [[ -n "$_cfg_provider" ]]; then
375
- CLI_PROVIDER="$_cfg_provider"
376
- ok "Provider: $CLI_PROVIDER (from install-config.yaml)"
377
- fi
378
- if [[ "$_cfg_agent_teams" == "true" ]]; then
379
- AGENT_TEAMS=true
380
- fi
381
- if [[ -n "$_cfg_tier" ]]; then
382
- TIER="$_cfg_tier"
383
- fi
384
- else
385
- warn "install-config.yaml not found at $CONFIG_PATH — falling back to auto-detection"
386
- FROM_CONFIG=false
387
- fi
388
- fi
389
-
390
- if [[ "$FROM_CONFIG" != true ]]; then
391
- if [[ -n "$CLI_PROVIDER" ]]; then
392
- # --provider flag was set explicitly — skip interactive detection
393
- ok "Provider: $CLI_PROVIDER (--provider flag)"
394
- elif [ "$HAS_CLAUDE" = true ] && [ "$HAS_CODEX" = true ]; then
395
- CLI_PROVIDER="claude"
396
- info "Claude Code and Codex both detected — Codex support coming soon (in lab), using Claude Code."
397
- ok "Provider: $CLI_PROVIDER"
398
- elif [ "$HAS_CLAUDE" = true ]; then
399
- CLI_PROVIDER="claude"
400
- CLAUDE_VERSION=$(claude --version 2>/dev/null || echo "unknown")
401
- ok "Claude Code CLI: $CLAUDE_VERSION"
402
- elif [ "$HAS_CODEX" = true ]; then
403
- fail "Only Codex detected — Codex (OpenAI) support is coming soon (currently in our lab)."
404
- echo ""
405
- echo " Please install Claude Code to continue: https://claude.ai/download"
406
- exit 1
407
- elif [[ "$SKIP_PREREQS" == "1" ]]; then
408
- CLI_PROVIDER="claude"
409
- warn "No AI CLI found (skipped — SPECRAILS_SKIP_PREREQS=1)"
410
- else
411
- fail "No AI CLI found (claude)."
412
- echo ""
413
- echo " Install Claude Code: https://claude.ai/download"
414
- echo " Codex (OpenAI) support: coming soon — currently in our lab."
415
- exit 1
416
- fi
417
- fi
418
-
419
- # Derive output directory and instruction file from provider
420
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
421
- SPECRAILS_DIR=".codex"
422
- INSTRUCTIONS_FILE="AGENTS.md"
423
- else
424
- SPECRAILS_DIR=".claude"
425
- INSTRUCTIONS_FILE="CLAUDE.md"
426
- fi
427
-
428
- # 1.2b Agent Teams opt-in (Claude Code only)
429
- if [[ "$CLI_PROVIDER" == "claude" ]]; then
430
- if [[ "$FROM_CONFIG" == true || "$AGENT_TEAMS" == true ]]; then
431
- # Already resolved from config or --agent-teams flag
432
- if [[ "$AGENT_TEAMS" == true ]]; then
433
- ok "Agent Teams commands: enabled (from install-config.yaml)"
434
- echo ""
435
- info "Remember to set the feature flag before using these commands:"
436
- echo ""
437
- echo " export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"
438
- echo ""
439
- else
440
- info "Agent Teams commands: skipped (from install-config.yaml)"
441
- fi
442
- elif [ "$AUTO_YES" = true ]; then
443
- # --yes flag: default to NOT installing (opt-in, not opt-out)
444
- AGENT_TEAMS=false
445
- info "Agent Teams commands: skipped (opt-in, use interactive mode to enable)"
446
- else
447
- echo ""
448
- echo -e " ${BOLD}Agent Teams commands are available (experimental):${NC}"
449
- echo " /specrails:team-review — Multi-perspective code review with AI reviewers"
450
- echo " /specrails:team-debug — Collaborative debugging with competing hypotheses"
451
- echo ""
452
- echo " These require Claude Code Agent Teams (experimental feature)."
453
- read -p " Install Agent Teams commands? (y/n, default: n): " INSTALL_AGENT_TEAMS || INSTALL_AGENT_TEAMS="n"
454
- if [[ "$INSTALL_AGENT_TEAMS" == "y" || "$INSTALL_AGENT_TEAMS" == "Y" ]]; then
455
- AGENT_TEAMS=true
456
- ok "Agent Teams commands will be installed"
457
- echo ""
458
- info "Remember to set the feature flag before using these commands:"
459
- echo ""
460
- echo " export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"
461
- echo ""
462
- else
463
- AGENT_TEAMS=false
464
- info "Agent Teams commands: skipped"
465
- fi
466
- fi
467
- fi
468
-
469
- # 1.3 API key / authentication (provider-specific)
470
- if [[ "$CLI_PROVIDER" == "claude" ]]; then
471
- CLAUDE_AUTHED=false
472
- if claude config list 2>/dev/null | grep -q "api_key"; then
473
- CLAUDE_AUTHED=true
474
- elif [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then
475
- CLAUDE_AUTHED=true
476
- elif [[ -f "${HOME}/.claude.json" ]] && grep -q '"oauthAccount"' "${HOME}/.claude.json" 2>/dev/null; then
477
- CLAUDE_AUTHED=true
478
- fi
479
-
480
- if [[ "$CLAUDE_AUTHED" == "true" ]]; then
481
- ok "Claude: authenticated"
482
- elif [[ "$SKIP_PREREQS" == "1" ]]; then
483
- warn "Claude authentication not found (skipped — SPECRAILS_SKIP_PREREQS=1)"
484
- else
485
- fail "No Claude authentication found."
486
- echo ""
487
- echo " Option 1 (API key): claude config set api_key <your-key>"
488
- echo " Option 2 (OAuth): claude auth login"
489
- exit 1
490
- fi
491
- else
492
- # Codex
493
- CODEX_AUTHED=false
494
- if [[ -n "${OPENAI_API_KEY:-}" ]]; then
495
- CODEX_AUTHED=true
496
- elif codex login status 2>/dev/null | grep -qi "logged in"; then
497
- CODEX_AUTHED=true
498
- elif [[ -f "${HOME}/.codex/auth.json" ]] && grep -q '"access_token"' "${HOME}/.codex/auth.json" 2>/dev/null; then
499
- CODEX_AUTHED=true
500
- fi
501
-
502
- if [[ "$CODEX_AUTHED" == "true" ]]; then
503
- ok "Codex: authenticated"
504
- elif [[ "$SKIP_PREREQS" == "1" ]]; then
505
- warn "Codex authentication not found (skipped — SPECRAILS_SKIP_PREREQS=1)"
506
- else
507
- fail "No Codex authentication found."
508
- echo ""
509
- echo " Option 1 (API key): export OPENAI_API_KEY=<your-key>"
510
- echo " Option 2 (OAuth): codex login"
511
- exit 1
512
- fi
513
- fi
514
-
515
- # 1.4 npm
516
- if command -v npm &> /dev/null; then
517
- NPM_VERSION=$(npm --version 2>/dev/null)
518
- ok "npm: v$NPM_VERSION"
519
- HAS_NPM=true
520
- else
521
- warn "npm not found. OpenSpec CLI will be unavailable."
522
- echo " Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm"
523
- HAS_NPM=false
524
- fi
525
-
526
- # 1.5 OpenSpec CLI
527
- # Read pinned version from package.json (specrails.openspecVersion field)
528
- _openspec_pkg_version=""
529
- if [[ -f "$SCRIPT_DIR/package.json" ]]; then
530
- _openspec_pkg_version=$(python3 -c "import json; d=json.load(open('$SCRIPT_DIR/package.json')); print(d.get('specrails',{}).get('openspecVersion',''))" 2>/dev/null || true)
531
- fi
532
- _openspec_install_spec="@fission-ai/openspec"
533
- if [[ -n "$_openspec_pkg_version" ]]; then
534
- _openspec_install_spec="@fission-ai/openspec@${_openspec_pkg_version}"
535
- fi
536
-
537
- if command -v openspec &> /dev/null; then
538
- OPENSPEC_VERSION=$(openspec --version 2>/dev/null || echo "unknown")
539
- ok "OpenSpec CLI: $OPENSPEC_VERSION"
540
- HAS_OPENSPEC=true
541
- elif [ -f "$REPO_ROOT/node_modules/.bin/openspec" ]; then
542
- ok "OpenSpec CLI: found in node_modules"
543
- HAS_OPENSPEC=true
544
- else
545
- if [ "$HAS_NPM" = true ]; then
546
- # Auto-install in --yes mode; ask otherwise
547
- if [ "$AUTO_YES" = true ]; then
548
- INSTALL_OPENSPEC="y"
549
- else
550
- read -p " OpenSpec CLI not found. Install ${_openspec_install_spec}? (y/n): " INSTALL_OPENSPEC || INSTALL_OPENSPEC="n"
551
- fi
552
- if [ "$INSTALL_OPENSPEC" = "y" ] || [ "$INSTALL_OPENSPEC" = "Y" ]; then
553
- info "Installing ${_openspec_install_spec}..."
554
- _npm_log="$(mktemp)"
555
- if npm install -g "${_openspec_install_spec}" >"$_npm_log" 2>&1; then
556
- ok "OpenSpec CLI installed ($(openspec --version 2>/dev/null || echo "${_openspec_pkg_version}"))"
557
- HAS_OPENSPEC=true
558
- rm -f "$_npm_log"
559
- else
560
- warn "Global install failed (often EACCES on fresh macOS). Trying local install in ${REPO_ROOT}..."
561
- # Show last few lines of npm error for context
562
- tail -n 5 "$_npm_log" | sed 's/^/ /' >&2 || true
563
- if ( cd "$REPO_ROOT" && npm install "${_openspec_install_spec}" >"$_npm_log" 2>&1 ); then
564
- ok "OpenSpec CLI installed locally (${REPO_ROOT}/node_modules/.bin/openspec)"
565
- HAS_OPENSPEC=true
566
- else
567
- fail "Could not install OpenSpec CLI. npm output:"
568
- tail -n 10 "$_npm_log" | sed 's/^/ /' >&2 || true
569
- echo " Manual fix: npm install -g ${_openspec_install_spec}" >&2
570
- HAS_OPENSPEC=false
571
- fi
572
- rm -f "$_npm_log"
573
- fi
574
- else
575
- warn "Skipping OpenSpec install. Spec-driven workflow will be limited."
576
- HAS_OPENSPEC=false
577
- fi
578
- else
579
- warn "Cannot install OpenSpec without npm."
580
- HAS_OPENSPEC=false
581
- fi
582
- fi
583
-
584
- # 1.6 GitHub CLI (optional)
585
- if command -v gh &> /dev/null; then
586
- if gh auth status &> /dev/null; then
587
- ok "GitHub CLI: authenticated"
588
- HAS_GH=true
589
- else
590
- warn "GitHub CLI installed but not authenticated. Run: gh auth login"
591
- HAS_GH=false
592
- fi
593
- else
594
- warn "GitHub CLI (gh) not found. GitHub Issues backlog will be unavailable."
595
- HAS_GH=false
596
- fi
597
-
598
- # 1.7 OSS detection (requires gh auth; degrades gracefully)
599
- IS_OSS=false
600
- HAS_PUBLIC_REPO=false
601
- HAS_CI=false
602
- HAS_CONTRIBUTING=false
603
-
604
- if [ "$HAS_GH" = true ]; then
605
- _REPO_PRIVATE=$(gh repo view --json isPrivate --jq '.isPrivate' 2>/dev/null || echo "unknown")
606
- if [ "$_REPO_PRIVATE" = "false" ]; then
607
- HAS_PUBLIC_REPO=true
608
- fi
609
- if ls "$REPO_ROOT/.github/workflows/"*.yml > /dev/null 2>&1; then
610
- HAS_CI=true
611
- fi
612
- if [ -f "$REPO_ROOT/CONTRIBUTING.md" ] || [ -f "$REPO_ROOT/.github/CONTRIBUTING.md" ]; then
613
- HAS_CONTRIBUTING=true
614
- fi
615
- if [ "$HAS_PUBLIC_REPO" = true ] && [ "$HAS_CI" = true ] && [ "$HAS_CONTRIBUTING" = true ]; then
616
- IS_OSS=true
617
- ok "OSS project detected (public repo + CI + CONTRIBUTING.md)"
618
- fi
619
- fi
620
-
621
- # 1.8 JIRA CLI (optional)
622
- if command -v jira &> /dev/null; then
623
- ok "JIRA CLI: found"
624
- HAS_JIRA=true
625
- else
626
- HAS_JIRA=false
627
- # Don't warn here — JIRA is only relevant if chosen during /specrails:enrich.
628
- # If the user selects JIRA in /specrails:enrich and it's not installed, the enrich
629
- # wizard will offer to install it (go-jira via brew/go, or Atlassian CLI).
630
- fi
631
-
632
- # ─────────────────────────────────────────────
633
- # Phase 2: Detect existing setup
634
- # ─────────────────────────────────────────────
635
-
636
- step "Phase 2: Detecting existing setup"
637
-
638
- EXISTING_SETUP=false
639
-
640
- if [ -d "$REPO_ROOT/$SPECRAILS_DIR" ]; then
641
- if [ -d "$REPO_ROOT/$SPECRAILS_DIR/agents" ] && [ "$(ls -A "$REPO_ROOT/$SPECRAILS_DIR/agents" 2>/dev/null)" ]; then
642
- warn "Existing $SPECRAILS_DIR/agents/ found with content"
643
- EXISTING_SETUP=true
644
- fi
645
- if [ -d "$REPO_ROOT/$SPECRAILS_DIR/commands" ] && [ "$(ls -A "$REPO_ROOT/$SPECRAILS_DIR/commands" 2>/dev/null)" ]; then
646
- warn "Existing $SPECRAILS_DIR/commands/ found with content"
647
- EXISTING_SETUP=true
648
- fi
649
- if [ -d "$REPO_ROOT/$SPECRAILS_DIR/rules" ] && [ "$(ls -A "$REPO_ROOT/$SPECRAILS_DIR/rules" 2>/dev/null)" ]; then
650
- warn "Existing $SPECRAILS_DIR/rules/ found with content"
651
- EXISTING_SETUP=true
652
- fi
653
- fi
654
-
655
- if [ -d "$REPO_ROOT/openspec" ]; then
656
- warn "Existing openspec/ directory found"
657
- EXISTING_SETUP=true
658
- fi
659
-
660
- if [ "$EXISTING_SETUP" = true ]; then
661
- echo ""
662
- warn "This repo already has some agent/command/openspec artifacts."
663
- if [ "$AUTO_YES" = true ]; then CONTINUE="y"; else read -p " Continue and merge with existing setup? (y/n): " CONTINUE || CONTINUE="n"; fi
664
- if [ "$CONTINUE" != "y" ] && [ "$CONTINUE" != "Y" ]; then
665
- info "Aborted. No changes made."
666
- exit 0
667
- fi
668
- else
669
- ok "Clean repo — no existing .claude/ or openspec/ artifacts"
670
- fi
671
-
672
- # ─────────────────────────────────────────────
673
- # Phase 3: Install artifacts
674
- # ─────────────────────────────────────────────
675
-
676
- step "Phase 3: Installing specrails artifacts"
677
-
678
- # Create directory structure
679
- mkdir -p "$REPO_ROOT/$SPECRAILS_DIR"
680
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
681
- # Codex: install as Agent Skills (Codex doesn't support .codex/commands/)
682
- mkdir -p "$REPO_ROOT/.agents/skills/enrich"
683
- mkdir -p "$REPO_ROOT/.agents/skills/doctor"
684
- else
685
- mkdir -p "$REPO_ROOT/$SPECRAILS_DIR/commands/specrails"
686
- fi
687
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/agents"
688
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/commands"
689
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/skills"
690
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/rules"
691
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/personas"
692
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/claude-md"
693
- mkdir -p "$REPO_ROOT/.specrails/setup-templates/settings"
694
-
695
- # Ensure .gitignore excludes local runtime artifacts
696
- _gitignore="${REPO_ROOT}/.gitignore"
697
- _gitignore_entries=(".claude/agent-memory/" ".specrails/")
698
- for _entry in "${_gitignore_entries[@]}"; do
699
- if ! grep -qF "$_entry" "$_gitignore" 2>/dev/null; then
700
- echo "$_entry" >> "$_gitignore"
701
- fi
702
- done
703
-
704
- # Copy the /specrails:enrich and /specrails:doctor commands (or skills for Codex)
705
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
706
- # Codex uses Agent Skills in .agents/skills/<name>/SKILL.md
707
- {
708
- echo '---'
709
- echo 'name: enrich'
710
- echo 'description: "Interactive wizard to configure the full specrails agent workflow system for this repository. Supports config-driven mode for direct installation from a pre-built config file."'
711
- echo 'license: MIT'
712
- echo 'compatibility: "Requires npm, git."'
713
- echo 'metadata:'
714
- echo ' author: specrails'
715
- echo ' version: "1.0"'
716
- echo '---'
717
- echo ''
718
- cat "$SCRIPT_DIR/commands/enrich.md"
719
- } > "$REPO_ROOT/.agents/skills/enrich/SKILL.md"
720
- ok "Installed \$enrich skill"
721
-
722
- {
723
- echo '---'
724
- echo 'name: doctor'
725
- echo 'description: "Health check for the specrails agent workflow system — validates agents, commands, rules, and configuration."'
726
- echo 'license: MIT'
727
- echo 'compatibility: "Requires npm, git."'
728
- echo 'metadata:'
729
- echo ' author: specrails'
730
- echo ' version: "1.0"'
731
- echo '---'
732
- echo ''
733
- cat "$SCRIPT_DIR/commands/doctor.md"
734
- } > "$REPO_ROOT/.agents/skills/doctor/SKILL.md"
735
- ok "Installed \$doctor skill"
736
- else
737
- # Claude Code uses commands in .claude/commands/specrails/ (namespaced as /specrails:*)
738
- cp "$SCRIPT_DIR/commands/enrich.md" "$REPO_ROOT/$SPECRAILS_DIR/commands/specrails/enrich.md"
739
- ok "Installed /specrails:enrich command"
740
-
741
- cp "$SCRIPT_DIR/commands/doctor.md" "$REPO_ROOT/$SPECRAILS_DIR/commands/specrails/doctor.md"
742
- ok "Installed /specrails:doctor command"
743
- fi
744
-
745
- # Install bin/doctor.sh for standalone use
746
- mkdir -p "$REPO_ROOT/.specrails/bin"
747
- cp "$SCRIPT_DIR/bin/doctor.sh" "$REPO_ROOT/.specrails/bin/doctor.sh"
748
- chmod +x "$REPO_ROOT/.specrails/bin/doctor.sh"
749
- ok "Installed specrails doctor (bin/doctor.sh)"
750
-
751
- # Write install-config.yaml: copy from --from-config source if provided,
752
- # otherwise write defaults. Ensures a config file always exists after install
753
- # so /specrails:enrich --from-config works regardless of how the installer was invoked.
754
- _install_config="${REPO_ROOT}/.specrails/install-config.yaml"
755
- if [[ "$FROM_CONFIG" == true && -n "$CONFIG_PATH" && -f "$CONFIG_PATH" && "$CONFIG_PATH" != "$_install_config" ]]; then
756
- cp "$CONFIG_PATH" "$_install_config"
757
- ok "Copied install-config.yaml from ${CONFIG_PATH}"
758
- elif [[ ! -f "$_install_config" ]]; then
759
- _ic_provider="${CLI_PROVIDER:-claude}"
760
- _ic_tier="${TIER:-full}"
761
- _ic_agent_teams="${AGENT_TEAMS:-false}"
762
- cat > "$_install_config" << YAML
763
- # specrails install config — generated during install (defaults)
764
- # Re-run: npx specrails-core@latest init to regenerate with TUI
765
- version: 1
766
- provider: ${_ic_provider}
767
- tier: ${_ic_tier}
768
- agents:
769
- selected: [sr-architect, sr-developer, sr-reviewer, sr-test-writer, sr-product-manager]
770
- excluded: [sr-frontend-developer, sr-backend-developer, sr-frontend-reviewer, sr-backend-reviewer, sr-security-reviewer, sr-performance-reviewer, sr-product-analyst, sr-doc-sync, sr-merge-resolver]
771
- models:
772
- preset: balanced
773
- defaults: { model: sonnet }
774
- overrides: {}
775
- agent_teams: ${_ic_agent_teams}
776
- YAML
777
- ok "Written .specrails/install-config.yaml (defaults)"
778
- fi
779
-
780
- # Copy templates (includes commands, skills, agents, rules, personas, settings)
781
- # Use tar to exclude node_modules and package-lock.json for performance
782
- tar -C "$SCRIPT_DIR/templates" --exclude='node_modules' --exclude='package-lock.json' -cf - . \
783
- | tar -C "$REPO_ROOT/.specrails/setup-templates/" -xf -
784
- ok "Installed setup templates (commands + skills)"
785
-
786
- # Filter agent templates to only those listed in agents.selected (when --from-config is active).
787
- # This allows hub or the TUI to pre-select a subset of agents before enrichment.
788
- if [[ "$FROM_CONFIG" == true ]]; then
789
- _cfg_to_read="${CONFIG_PATH:-${REPO_ROOT}/.specrails/install-config.yaml}"
790
- if [[ -f "$_cfg_to_read" ]]; then
791
- # Parse selected agents from inline YAML list: selected: [sr-architect, sr-developer, ...]
792
- _selected_raw=$(grep '^ selected:' "$_cfg_to_read" | sed 's/.*\[//;s/\].*//;s/,/ /g' || true)
793
- if [[ -n "$_selected_raw" ]]; then
794
- # Core agents are always kept — the pipeline depends on them
795
- _core_agents="sr-architect sr-developer sr-reviewer sr-merge-resolver"
796
- _agents_dir="${REPO_ROOT}/.specrails/setup-templates/agents"
797
- _removed=0
798
- for _agent_file in "$_agents_dir/"*.md; do
799
- [[ -f "$_agent_file" ]] || continue
800
- _agent_name="$(basename "$_agent_file" .md)"
801
-
802
- # Never remove core agents
803
- if echo " $_core_agents " | grep -q " ${_agent_name} "; then
804
- continue
805
- fi
806
-
807
- _in_selected=false
808
- for _sel in $_selected_raw; do
809
- # Strip whitespace/commas from parsed token
810
- _sel="${_sel//,/}"
811
- _sel="${_sel// /}"
812
- if [[ "$_sel" == "$_agent_name" ]]; then
813
- _in_selected=true
814
- break
815
- fi
816
- done
817
- if [[ "$_in_selected" == false ]]; then
818
- rm -f "$_agent_file"
819
- (( _removed++ )) || true
820
- fi
821
- done
822
- if (( _removed > 0 )); then
823
- ok "Agent templates filtered: removed ${_removed} excluded agent(s)"
824
- fi
825
- fi
826
-
827
- # Apply defaults.model to all agent templates when present.
828
- # Handles both inline "defaults: { model: haiku }" and block "defaults:\n model: haiku"
829
- _cfg_default_model=""
830
- _defaults_line=$(grep '^\s*defaults:' "$_cfg_to_read" 2>/dev/null | head -1 || true)
831
- if echo "$_defaults_line" | grep -q 'model:' 2>/dev/null; then
832
- _cfg_default_model=$(echo "$_defaults_line" | sed 's/.*model:[[:space:]]*//' | awk '{print $1}' | tr -d '}[:space:]')
833
- else
834
- _cfg_default_model=$(sed -n '/^\s*defaults:/,/^\s*[a-z]/{ /^\s*model:/p; }' "$_cfg_to_read" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]' || true)
835
- fi
836
- if [[ -n "$_cfg_default_model" ]]; then
837
- for _agent_file in "$_agents_dir/"*.md; do
838
- [[ -f "$_agent_file" ]] || continue
839
- sed -i.bak "s/^model: .*/model: ${_cfg_default_model}/" "$_agent_file" && rm -f "${_agent_file}.bak"
840
- done
841
- fi
842
-
843
- # Apply model overrides to agent frontmatter when present.
844
- # Overrides YAML format: " overrides:\n sr-architect: opus"
845
- _in_overrides=false
846
- while IFS= read -r _line; do
847
- if [[ "$_line" =~ ^[[:space:]]*overrides: ]]; then
848
- # Check if overrides are on same line: overrides: { sr-architect: opus }
849
- if [[ "$_line" =~ \{.*\} ]]; then
850
- _inline=$(echo "$_line" | sed 's/.*{//;s/}.*//;s/,/ /g')
851
- for _pair in $_inline; do
852
- _key="${_pair%%:*}"
853
- _val="${_pair##*:}"
854
- _agent_file="${REPO_ROOT}/.specrails/setup-templates/agents/${_key}.md"
855
- if [[ -f "$_agent_file" ]]; then
856
- sed -i.bak "s/^model: .*/model: ${_val}/" "$_agent_file" && rm -f "${_agent_file}.bak"
857
- fi
858
- done
859
- _in_overrides=false
860
- else
861
- _in_overrides=true
862
- fi
863
- elif [[ "$_in_overrides" == true ]]; then
864
- if [[ "$_line" =~ ^[[:space:]]{4}([a-z0-9_-]+):[[:space:]]*([a-z]+) ]]; then
865
- _key="${BASH_REMATCH[1]}"
866
- _val="${BASH_REMATCH[2]}"
867
- _agent_file="${REPO_ROOT}/.specrails/setup-templates/agents/${_key}.md"
868
- if [[ -f "$_agent_file" ]]; then
869
- sed -i.bak "s/^model: .*/model: ${_val}/" "$_agent_file" && rm -f "${_agent_file}.bak"
870
- fi
871
- elif [[ ! "$_line" =~ ^[[:space:]]{4} ]]; then
872
- _in_overrides=false
873
- fi
874
- fi
875
- done < "$_cfg_to_read"
876
- fi
877
- fi
878
-
879
- # Write OSS detection results for /specrails:enrich
880
- cat > "$REPO_ROOT/.specrails/setup-templates/.oss-detection.json" << EOF
881
- {
882
- "is_oss": $IS_OSS,
883
- "signals": {
884
- "public_repo": $HAS_PUBLIC_REPO,
885
- "has_ci": $HAS_CI,
886
- "has_contributing": $HAS_CONTRIBUTING
887
- }
888
- }
889
- EOF
890
- ok "OSS detection results written"
891
-
892
- # Write provider detection results for /specrails:enrich
893
- cat > "$REPO_ROOT/.specrails/setup-templates/.provider-detection.json" << EOF
894
- {
895
- "cli_provider": "$CLI_PROVIDER",
896
- "specrails_dir": "$SPECRAILS_DIR",
897
- "instructions_file": "$INSTRUCTIONS_FILE",
898
- "agent_teams": $AGENT_TEAMS
899
- }
900
- EOF
901
- ok "Provider detection results written ($CLI_PROVIDER → .specrails/setup-templates/)"
902
-
903
- # Copy security exemptions config (skip if already exists — preserve user exemptions)
904
- if [ ! -f "${REPO_ROOT}/$SPECRAILS_DIR/security-exemptions.yaml" ]; then
905
- cp "${SCRIPT_DIR}/templates/security/security-exemptions.yaml" "${REPO_ROOT}/$SPECRAILS_DIR/security-exemptions.yaml"
906
- ok "Created $SPECRAILS_DIR/security-exemptions.yaml"
907
- fi
908
-
909
-
910
- # ─────────────────────────────────────────────
911
- # Phase 3c: Quick-tier direct placement
912
- # ─────────────────────────────────────────────
913
- # For Quick tier, copy agents/commands/rules/personas/settings from
914
- # setup-templates directly to their final locations under $SPECRAILS_DIR.
915
- # Placeholders are stripped (enrich not required).
916
-
917
- if [[ "$TIER" == "quick" ]]; then
918
- step "Phase 3c: Placing agents and commands (quick install)"
919
-
920
- _templates="${REPO_ROOT}/.specrails/setup-templates"
921
- _project_name="$(basename "$REPO_ROOT")"
922
-
923
- # VPC/persona-dependent agents are excluded from Quick tier (require enrichment)
924
- _quick_excluded_agents="sr-product-manager sr-product-analyst"
925
-
926
- # --- Agents ---
927
- if [[ -d "$_templates/agents" ]] && ls "$_templates/agents/"*.md &>/dev/null; then
928
- mkdir -p "${REPO_ROOT}/${SPECRAILS_DIR}/agents"
929
- _agent_count=0
930
- _agent_skipped=0
931
- for _src in "$_templates/agents/"*.md; do
932
- [[ -f "$_src" ]] || continue
933
- _name="$(basename "$_src" .md)"
934
-
935
- # Skip VPC-dependent agents in Quick tier
936
- if echo " $_quick_excluded_agents " | grep -q " ${_name} "; then
937
- (( _agent_skipped++ )) || true
938
- continue
939
- fi
940
-
941
- _dest="${REPO_ROOT}/${SPECRAILS_DIR}/agents/${_name}.md"
942
- cp "$_src" "$_dest"
943
-
944
- # Substitute known placeholders
945
- sed -i.bak \
946
- -e "s|{{PROJECT_NAME}}|${_project_name}|g" \
947
- -e "s|{{MEMORY_PATH}}|.claude/agent-memory/${_name}/|g" \
948
- -e "s|{{SECURITY_EXEMPTIONS_PATH}}|${SPECRAILS_DIR}/security-exemptions.yaml|g" \
949
- -e "s|{{PERSONA_DIR}}|${SPECRAILS_DIR}/agents/personas/|g" \
950
- "$_dest" && rm -f "${_dest}.bak"
951
-
952
- # Strip remaining {{PLACEHOLDER}} lines (leave blank line)
953
- sed -i.bak 's/{{[A-Z_]*}}//g' "$_dest" && rm -f "${_dest}.bak"
954
-
955
- # Create agent memory directory
956
- mkdir -p "${REPO_ROOT}/.claude/agent-memory/${_name}"
957
-
958
- # Agents that write explanation records need the shared explanations dir
959
- if [[ "$_name" == "sr-architect" || "$_name" == "sr-reviewer" ]]; then
960
- mkdir -p "${REPO_ROOT}/.claude/agent-memory/explanations"
961
- fi
962
-
963
- (( _agent_count++ )) || true
964
- done
965
- if (( _agent_skipped > 0 )); then
966
- ok "Installed ${_agent_count} agent(s) to ${SPECRAILS_DIR}/agents/ (skipped ${_agent_skipped} VPC-dependent)"
967
- else
968
- ok "Installed ${_agent_count} agent(s) to ${SPECRAILS_DIR}/agents/"
969
- fi
970
- fi
971
-
972
- # Dynamic command exclusion based on installed agents.
973
- # Each command maps to the agent(s) it depends on — if the agent
974
- # wasn't installed, the command is excluded automatically.
975
- #
976
- # Dependency map (command → required agent):
977
- # auto-propose-backlog-specs → sr-product-manager
978
- # get-backlog-specs → sr-product-analyst
979
- # vpc-drift → sr-product-manager, sr-product-analyst
980
- # merge-resolve → sr-merge-resolver
981
- # team-debug, team-review → AGENT_TEAMS flag
982
- _quick_excluded_cmds=""
983
- _installed_agents_dir="${REPO_ROOT}/${SPECRAILS_DIR}/agents"
984
-
985
- # Agent-dependent commands
986
- if ! ls "$_installed_agents_dir/sr-product-manager.md" &>/dev/null; then
987
- _quick_excluded_cmds="$_quick_excluded_cmds auto-propose-backlog-specs vpc-drift"
988
- fi
989
- if ! ls "$_installed_agents_dir/sr-product-analyst.md" &>/dev/null; then
990
- _quick_excluded_cmds="$_quick_excluded_cmds get-backlog-specs"
991
- # vpc-drift needs both product agents
992
- if ! echo " $_quick_excluded_cmds " | grep -q " vpc-drift "; then
993
- _quick_excluded_cmds="$_quick_excluded_cmds vpc-drift"
994
- fi
995
- fi
996
- if ! ls "$_installed_agents_dir/sr-merge-resolver.md" &>/dev/null; then
997
- _quick_excluded_cmds="$_quick_excluded_cmds merge-resolve"
998
- fi
999
-
1000
- # Agent Teams commands (flag-dependent, not agent-dependent)
1001
- if [[ "$AGENT_TEAMS" != "true" ]]; then
1002
- _quick_excluded_cmds="$_quick_excluded_cmds team-debug team-review"
1003
- fi
1004
-
1005
- # --- Commands ---
1006
- if [[ -d "$_templates/commands/specrails" ]]; then
1007
- mkdir -p "${REPO_ROOT}/${SPECRAILS_DIR}/commands/specrails"
1008
- _cmd_count=0
1009
- for _src in "$_templates/commands/specrails/"*.md; do
1010
- [[ -f "$_src" ]] || continue
1011
- _cmd_name="$(basename "$_src" .md)"
1012
-
1013
- # Skip commands whose required agents are not installed
1014
- if echo " $_quick_excluded_cmds " | grep -q " ${_cmd_name} "; then
1015
- continue
1016
- fi
1017
-
1018
- _dest="${REPO_ROOT}/${SPECRAILS_DIR}/commands/specrails/${_cmd_name}.md"
1019
- cp "$_src" "$_dest"
1020
- sed -i.bak \
1021
- -e "s|{{PROJECT_NAME}}|${_project_name}|g" \
1022
- -e "s|{{MEMORY_PATH}}|.claude/agent-memory/|g" \
1023
- -e "s|{{PERSONA_DIR}}|${SPECRAILS_DIR}/agents/personas/|g" \
1024
- -e "s|{{SECURITY_EXEMPTIONS_PATH}}|${SPECRAILS_DIR}/security-exemptions.yaml|g" \
1025
- "$_dest" && rm -f "${_dest}.bak"
1026
- sed -i.bak 's/{{[A-Z_]*}}//g' "$_dest" && rm -f "${_dest}.bak"
1027
- (( _cmd_count++ )) || true
1028
- done
1029
- ok "Installed ${_cmd_count} command(s) to ${SPECRAILS_DIR}/commands/specrails/"
1030
- fi
1031
-
1032
- # --- Rules ---
1033
- if [[ -d "$_templates/rules" ]]; then
1034
- mkdir -p "${REPO_ROOT}/${SPECRAILS_DIR}/rules"
1035
- for _src in "$_templates/rules/"*.md; do
1036
- [[ -f "$_src" ]] || continue
1037
- cp "$_src" "${REPO_ROOT}/${SPECRAILS_DIR}/rules/$(basename "$_src")"
1038
- done
1039
- ok "Installed rules to ${SPECRAILS_DIR}/rules/"
1040
- fi
1041
-
1042
- # Personas are NOT installed in Quick tier (require VPC enrichment)
1043
-
1044
- # --- Settings ---
1045
- if [[ -f "$_templates/settings/settings.json" && ! -f "${REPO_ROOT}/${SPECRAILS_DIR}/settings.json" ]]; then
1046
- cp "$_templates/settings/settings.json" "${REPO_ROOT}/${SPECRAILS_DIR}/settings.json"
1047
- ok "Installed settings.json"
1048
- fi
1049
- if [[ -f "$_templates/settings/integration-contract.json" ]]; then
1050
- cp "$_templates/settings/integration-contract.json" "${REPO_ROOT}/.specrails/integration-contract.json"
1051
- ok "Installed integration-contract.json"
1052
- fi
1053
-
1054
- # --- Backlog config (default: local provider) ---
1055
- _backlog_cfg="${REPO_ROOT}/.specrails/backlog-config.json"
1056
- if [[ ! -f "$_backlog_cfg" ]]; then
1057
- mkdir -p "${REPO_ROOT}/.specrails"
1058
- cat > "$_backlog_cfg" <<'BCEOF'
1059
- {
1060
- "provider": "local",
1061
- "write_access": true,
1062
- "git_auto": true
1063
- }
1064
- BCEOF
1065
- ok "Created .specrails/backlog-config.json (provider: local, git_auto: true)"
1066
- fi
1067
-
1068
- # --- Local tickets store ---
1069
- _tickets_file="${REPO_ROOT}/.specrails/local-tickets.json"
1070
- if [[ ! -f "$_tickets_file" ]]; then
1071
- _now="$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
1072
- cat > "$_tickets_file" <<LTEOF
1073
- {
1074
- "schema_version": "1.0",
1075
- "revision": 0,
1076
- "last_updated": "${_now}",
1077
- "next_id": 1,
1078
- "tickets": {}
1079
- }
1080
- LTEOF
1081
- ok "Created .specrails/local-tickets.json"
1082
- fi
1083
-
1084
- # --- Skills (OpenSpec) ---
1085
- # Dynamic skill exclusion based on installed agents (mirrors command logic).
1086
- # Dependency map (skill → required agent):
1087
- # sr-auto-propose-backlog-specs → sr-product-manager
1088
- # sr-get-backlog-specs → sr-product-analyst
1089
- _quick_excluded_skills=""
1090
- if ! ls "$_installed_agents_dir/sr-product-manager.md" &>/dev/null; then
1091
- _quick_excluded_skills="$_quick_excluded_skills sr-auto-propose-backlog-specs"
1092
- fi
1093
- if ! ls "$_installed_agents_dir/sr-product-analyst.md" &>/dev/null; then
1094
- _quick_excluded_skills="$_quick_excluded_skills sr-get-backlog-specs"
1095
- fi
1096
- if [[ -d "$_templates/skills" ]]; then
1097
- mkdir -p "${REPO_ROOT}/${SPECRAILS_DIR}/skills"
1098
- _skill_count=0
1099
- for _skill_dir in "$_templates/skills/"*/; do
1100
- [[ -d "$_skill_dir" ]] || continue
1101
- _skill_name="$(basename "$_skill_dir")"
1102
- if echo " $_quick_excluded_skills " | grep -q " ${_skill_name} "; then
1103
- continue
1104
- fi
1105
- cp -r "$_skill_dir" "${REPO_ROOT}/${SPECRAILS_DIR}/skills/${_skill_name}"
1106
- (( _skill_count++ )) || true
1107
- done
1108
- ok "Installed ${_skill_count} skill(s) to ${SPECRAILS_DIR}/skills/"
1109
- fi
1110
-
1111
- if [[ "$HUB_JSON" == true ]]; then
1112
- echo "CHECKPOINT:quick_placement:done:Agents and commands placed"
1113
- fi
1114
- fi
1115
-
1116
- # Initialize OpenSpec if available and not already initialized
1117
- if [ "$HAS_OPENSPEC" = true ] && [ ! -d "$REPO_ROOT/openspec" ]; then
1118
- # Map specrails provider to openspec tool name
1119
- _openspec_tool="claude"
1120
- [[ "$CLI_PROVIDER" == "codex" ]] && _openspec_tool="codex"
1121
-
1122
- info "Initializing OpenSpec (--tools ${_openspec_tool} --force)..."
1123
- cd "$REPO_ROOT" && openspec init --tools "${_openspec_tool}" --force 2>/dev/null && {
1124
- ok "OpenSpec initialized"
1125
- } || {
1126
- warn "OpenSpec init failed — you can run 'openspec init' manually later"
1127
- }
1128
- fi
1129
-
1130
- # ─────────────────────────────────────────────
1131
- # Phase 3b: Write version and manifest
1132
- # ─────────────────────────────────────────────
1133
-
1134
- step "Phase 3b: Writing version and manifest"
1135
-
1136
- generate_manifest
1137
- ok "Written .specrails/specrails-version ($(cat "$REPO_ROOT/.specrails/specrails-version"))"
1138
- ok "Written .specrails/specrails-manifest.json"
1139
-
1140
- # ─────────────────────────────────────────────
1141
- # Phase 4: Summary & next steps
1142
- # ─────────────────────────────────────────────
1143
-
1144
- step "Phase 4: Installation complete"
1145
-
1146
- echo ""
1147
- echo -e "${BOLD}${GREEN}Installation summary:${NC}"
1148
- echo ""
1149
- echo " Provider: $CLI_PROVIDER → output to $SPECRAILS_DIR/"
1150
- if [[ "$AGENT_TEAMS" == "true" ]]; then
1151
- echo " Agent Teams: installed (/specrails:team-review, /specrails:team-debug)"
1152
- echo " Feature flag: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (required)"
1153
- fi
1154
- if [[ "$TIER" == "quick" ]]; then
1155
- # Quick tier: agents placed directly
1156
- _installed_agents=$(ls "${REPO_ROOT}/${SPECRAILS_DIR}/agents/"*.md 2>/dev/null | wc -l | tr -d ' ')
1157
- _installed_commands=$(ls "${REPO_ROOT}/${SPECRAILS_DIR}/commands/specrails/"*.md 2>/dev/null | wc -l | tr -d ' ')
1158
- echo ""
1159
- echo " Installed:"
1160
- echo " ${_installed_agents} agent(s) → ${SPECRAILS_DIR}/agents/"
1161
- echo " ${_installed_commands} command(s) → ${SPECRAILS_DIR}/commands/specrails/"
1162
- echo " .specrails/backlog-config.json (provider: local)"
1163
- echo " .specrails/local-tickets.json"
1164
- echo " .specrails/specrails-version"
1165
- echo " .specrails/specrails-manifest.json"
1166
- echo ""
1167
-
1168
- echo -e "${BOLD}Prerequisites:${NC}"
1169
- echo ""
1170
- [ "$HAS_NPM" = true ] && ok "npm" || warn "npm (optional)"
1171
- [ "$HAS_OPENSPEC" = true ] && ok "OpenSpec" || warn "OpenSpec (optional)"
1172
- [ "$HAS_GH" = true ] && ok "GitHub CLI" || warn "GitHub CLI (optional, for GitHub Issues backlog)"
1173
- [ "$HAS_JIRA" = true ] && ok "JIRA CLI" || info "JIRA CLI not found (optional, for JIRA backlog)"
1174
- echo ""
1175
-
1176
- echo -e "${BOLD}${CYAN}Next steps:${NC}"
1177
- echo ""
1178
- echo " 1. Open $CLI_PROVIDER in this repo:"
1179
- echo ""
1180
- echo -e " ${BOLD}cd $REPO_ROOT && $CLI_PROVIDER${NC}"
1181
- echo ""
1182
- echo " 2. Your agents are ready to use! Start working:"
1183
- echo ""
1184
- echo " Agents, commands, and rules are pre-configured."
1185
- echo " No additional setup required."
1186
- echo ""
1187
- else
1188
- echo ""
1189
- echo " Files installed:"
1190
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
1191
- echo " .agents/skills/enrich/SKILL.md ← The \$enrich skill"
1192
- echo " .agents/skills/doctor/SKILL.md ← The \$doctor skill"
1193
- else
1194
- echo " $SPECRAILS_DIR/commands/specrails/enrich.md ← The /specrails:enrich command"
1195
- fi
1196
- echo " .specrails/setup-templates/ ← Templates: commands + skills (temporary, removed after enrich)"
1197
- echo " .specrails/specrails-version ← Installed specrails version"
1198
- echo " .specrails/specrails-manifest.json ← Artifact checksums for update detection"
1199
- echo ""
1200
-
1201
- echo -e "${BOLD}Prerequisites:${NC}"
1202
- echo ""
1203
- [ "$HAS_NPM" = true ] && ok "npm" || warn "npm (optional)"
1204
- [ "$HAS_OPENSPEC" = true ] && ok "OpenSpec" || warn "OpenSpec (optional)"
1205
- [ "$HAS_GH" = true ] && ok "GitHub CLI" || warn "GitHub CLI (optional, for GitHub Issues backlog)"
1206
- [ "$HAS_JIRA" = true ] && ok "JIRA CLI" || info "JIRA CLI not found (optional, for JIRA backlog)"
1207
- echo ""
1208
-
1209
- echo -e "${BOLD}${CYAN}Next steps:${NC}"
1210
- echo ""
1211
- echo " 1. Open $CLI_PROVIDER in this repo:"
1212
- echo ""
1213
- echo -e " ${BOLD}cd $REPO_ROOT && $CLI_PROVIDER${NC}"
1214
- echo ""
1215
- echo " 2. Run the enrich wizard:"
1216
- echo ""
1217
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
1218
- echo -e " ${BOLD}\$enrich${NC}"
1219
- else
1220
- echo -e " ${BOLD}/specrails:enrich${NC}"
1221
- fi
1222
- echo ""
1223
- if [[ "$CLI_PROVIDER" == "codex" ]]; then
1224
- echo " Codex will analyze your codebase, ask about your users,"
1225
- else
1226
- echo " Claude will analyze your codebase, ask about your users,"
1227
- fi
1228
- echo " research the competitive landscape, and generate all agents,"
1229
- echo " commands, rules, and personas adapted to your project."
1230
- echo ""
1231
- fi