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
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { rmSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { copyDir, copyFile, isDir, listDir, mkdirp, pathExists, readTextFile, writeFileLf } from '../util/fs.js';
|
|
4
|
+
import { info, ok, warn } from '../util/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Agents excluded from the quick tier because they require a full
|
|
7
|
+
* /specrails:enrich persona pass to function correctly.
|
|
8
|
+
*/
|
|
9
|
+
const QUICK_EXCLUDED_AGENTS = new Set(['sr-product-manager', 'sr-product-analyst']);
|
|
10
|
+
/**
|
|
11
|
+
* Skills excluded from the quick tier because they depend on
|
|
12
|
+
* VPC-only agents (sr-product-manager, sr-product-analyst).
|
|
13
|
+
*/
|
|
14
|
+
const QUICK_EXCLUDED_SKILLS = new Set([
|
|
15
|
+
'sr-auto-propose-backlog-specs',
|
|
16
|
+
'sr-get-backlog-specs',
|
|
17
|
+
]);
|
|
18
|
+
const QUICK_REQUIRED_AGENTS = new Set([
|
|
19
|
+
'sr-architect',
|
|
20
|
+
'sr-developer',
|
|
21
|
+
'sr-reviewer',
|
|
22
|
+
'sr-merge-resolver',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Command → required agent dependency map. A command is excluded
|
|
26
|
+
* from the quick tier when its required agent was excluded (i.e.
|
|
27
|
+
* VPC-dependent) or when the feature flag (Agent Teams) is off.
|
|
28
|
+
*/
|
|
29
|
+
const COMMAND_AGENT_DEPENDENCIES = [
|
|
30
|
+
{ command: 'auto-propose-backlog-specs', requires: ['sr-product-manager'] },
|
|
31
|
+
{ command: 'vpc-drift', requires: ['sr-product-manager', 'sr-product-analyst'] },
|
|
32
|
+
{ command: 'get-backlog-specs', requires: ['sr-product-analyst'] },
|
|
33
|
+
{ command: 'merge-resolve', requires: ['sr-merge-resolver'] },
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Agents that write "explanation" memory records. When any of them
|
|
37
|
+
* ships we also need a shared `.claude/agent-memory/explanations/`
|
|
38
|
+
* directory alongside the per-agent memory dirs.
|
|
39
|
+
*/
|
|
40
|
+
const EXPLANATION_AUTHORS = new Set(['sr-architect', 'sr-reviewer']);
|
|
41
|
+
/**
|
|
42
|
+
* Returns true iff any of the provider directories already contains
|
|
43
|
+
* content. The hub-driven path skips the "merge existing?" prompt and
|
|
44
|
+
* assumes `--yes`; the CLI dispatcher (bin/specrails-core.cjs) should
|
|
45
|
+
* have prompted before entering this phase.
|
|
46
|
+
*/
|
|
47
|
+
export function detectExistingSetup(input) {
|
|
48
|
+
const roots = [
|
|
49
|
+
path.join(input.repoRoot, input.providerDir, 'agents'),
|
|
50
|
+
path.join(input.repoRoot, input.providerDir, 'commands'),
|
|
51
|
+
path.join(input.repoRoot, input.providerDir, 'rules'),
|
|
52
|
+
path.join(input.repoRoot, 'openspec'),
|
|
53
|
+
];
|
|
54
|
+
for (const r of roots) {
|
|
55
|
+
if (isDir(r) && listDir(r).length > 0)
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Entry point. Creates directories, copies templates, updates
|
|
62
|
+
* .gitignore. Returns a summary for logging / tests.
|
|
63
|
+
*/
|
|
64
|
+
export function scaffoldInstallation(input) {
|
|
65
|
+
const createdDirs = [];
|
|
66
|
+
let copiedFiles = 0;
|
|
67
|
+
const mk = (abs) => {
|
|
68
|
+
mkdirp(abs);
|
|
69
|
+
createdDirs.push(abs);
|
|
70
|
+
};
|
|
71
|
+
// --- Directory skeleton ---
|
|
72
|
+
mk(path.join(input.repoRoot, input.providerDir));
|
|
73
|
+
if (input.provider === 'codex') {
|
|
74
|
+
mk(path.join(input.repoRoot, '.agents', 'skills', 'enrich'));
|
|
75
|
+
mk(path.join(input.repoRoot, '.agents', 'skills', 'doctor'));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
mk(path.join(input.repoRoot, input.providerDir, 'commands', 'specrails'));
|
|
79
|
+
mk(path.join(input.repoRoot, input.providerDir, 'skills'));
|
|
80
|
+
}
|
|
81
|
+
const setupTemplates = path.join(input.repoRoot, '.specrails', 'setup-templates');
|
|
82
|
+
mk(path.join(setupTemplates, 'agents'));
|
|
83
|
+
mk(path.join(setupTemplates, 'commands'));
|
|
84
|
+
mk(path.join(setupTemplates, 'skills'));
|
|
85
|
+
mk(path.join(setupTemplates, 'rules'));
|
|
86
|
+
mk(path.join(setupTemplates, 'personas'));
|
|
87
|
+
mk(path.join(setupTemplates, 'claude-md'));
|
|
88
|
+
mk(path.join(setupTemplates, 'settings'));
|
|
89
|
+
// --- .gitignore hygiene ---
|
|
90
|
+
ensureGitignore(input.repoRoot, ['.claude/agent-memory/', '.specrails/']);
|
|
91
|
+
// --- Copy bundled templates into setup-templates/ ---
|
|
92
|
+
const templatesSrc = path.join(input.scriptDir, 'templates');
|
|
93
|
+
if (pathExists(templatesSrc)) {
|
|
94
|
+
copyDir(templatesSrc, setupTemplates, {
|
|
95
|
+
filter: (_src, rel) => {
|
|
96
|
+
// Skip node_modules + package-lock; manifest excludes them too.
|
|
97
|
+
if (rel.includes('node_modules'))
|
|
98
|
+
return false;
|
|
99
|
+
if (rel.endsWith('package-lock.json'))
|
|
100
|
+
return false;
|
|
101
|
+
return true;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// Count files copied (approximate — recount via a flat listDir walk).
|
|
105
|
+
copiedFiles = countFiles(setupTemplates);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
warn(`templates/ not found at ${templatesSrc} — skipping template copy`);
|
|
109
|
+
}
|
|
110
|
+
// --- Write bundled commands (enrich.md + doctor.md) ---
|
|
111
|
+
copyBundledCommands({ ...input, copiedIncrement: (n) => (copiedFiles += n) });
|
|
112
|
+
pruneLegacyArtifacts(input);
|
|
113
|
+
// --- Quick tier: direct-placement short-circuit ---
|
|
114
|
+
if (input.tier === 'quick') {
|
|
115
|
+
const placed = placeQuickTierArtefacts({ ...input });
|
|
116
|
+
copiedFiles += placed.agents + placed.commands + placed.rules;
|
|
117
|
+
const skippedNote = placed.skippedAgents > 0 ? ` (skipped ${placed.skippedAgents} VPC-dependent)` : '';
|
|
118
|
+
info(`Quick tier: placed ${placed.agents} agent(s) + ${placed.commands} command(s) + ` +
|
|
119
|
+
`${placed.rules} rule file(s) directly into ${input.providerDir}/${skippedNote}`);
|
|
120
|
+
}
|
|
121
|
+
// --- Skills direct-placement (both tiers, claude provider) ---
|
|
122
|
+
if (input.provider === 'claude') {
|
|
123
|
+
const skills = placeSkills(input);
|
|
124
|
+
copiedFiles += skills.filesCopied;
|
|
125
|
+
const skillSkipNote = skills.skipped > 0 ? ` (skipped ${skills.skipped} VPC-dependent)` : '';
|
|
126
|
+
info(`Placed ${skills.placed} skill(s) into ${input.providerDir}/skills/${skillSkipNote}`);
|
|
127
|
+
}
|
|
128
|
+
// --- Full-tier hint: enrich is required to generate VPC artefacts ---
|
|
129
|
+
if (input.tier === 'full') {
|
|
130
|
+
info('Full tier staged. Run `/specrails:enrich` in Claude Code to generate ' +
|
|
131
|
+
'VPC personas and adapt agents (including sr-product-manager and ' +
|
|
132
|
+
'sr-product-analyst) to this codebase.');
|
|
133
|
+
}
|
|
134
|
+
ok(`Created ${createdDirs.length} directories, copied ${copiedFiles} files`);
|
|
135
|
+
return {
|
|
136
|
+
existingSetup: detectExistingSetup({
|
|
137
|
+
repoRoot: input.repoRoot,
|
|
138
|
+
providerDir: input.providerDir,
|
|
139
|
+
}),
|
|
140
|
+
createdDirs,
|
|
141
|
+
copiedFiles,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function copyBundledCommands(input) {
|
|
145
|
+
const commandsSrc = path.join(input.scriptDir, 'commands');
|
|
146
|
+
if (!isDir(commandsSrc))
|
|
147
|
+
return;
|
|
148
|
+
if (input.provider === 'codex') {
|
|
149
|
+
// Codex: write enrich + doctor as Agent Skills.
|
|
150
|
+
copyFile(path.join(commandsSrc, 'enrich.md'), path.join(input.repoRoot, '.agents', 'skills', 'enrich', 'SKILL.md'));
|
|
151
|
+
copyFile(path.join(commandsSrc, 'doctor.md'), path.join(input.repoRoot, '.agents', 'skills', 'doctor', 'SKILL.md'));
|
|
152
|
+
input.copiedIncrement(2);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Claude: all commands land under <providerDir>/commands/specrails/.
|
|
156
|
+
const destDir = path.join(input.repoRoot, input.providerDir, 'commands', 'specrails');
|
|
157
|
+
let count = 0;
|
|
158
|
+
for (const entry of listDir(commandsSrc)) {
|
|
159
|
+
const name = path.basename(entry);
|
|
160
|
+
if (!name.endsWith('.md'))
|
|
161
|
+
continue;
|
|
162
|
+
if (name === 'setup.md')
|
|
163
|
+
continue;
|
|
164
|
+
// Agent Teams gating — skip team-* commands unless explicitly opted in.
|
|
165
|
+
if (!input.agentTeams && /^team-/.test(name))
|
|
166
|
+
continue;
|
|
167
|
+
copyFile(entry, path.join(destDir, name));
|
|
168
|
+
count++;
|
|
169
|
+
}
|
|
170
|
+
input.copiedIncrement(count);
|
|
171
|
+
}
|
|
172
|
+
function pruneLegacyArtifacts(input) {
|
|
173
|
+
const legacyPaths = [
|
|
174
|
+
path.join(input.repoRoot, '.specrails', 'bin', 'doctor.sh'),
|
|
175
|
+
path.join(input.repoRoot, '.specrails', 'setup-templates', '.provider-detection.json'),
|
|
176
|
+
path.join(input.repoRoot, '.specrails', 'setup-templates', 'settings', 'integration-contract.json'),
|
|
177
|
+
path.join(input.repoRoot, '.specrails-version'),
|
|
178
|
+
];
|
|
179
|
+
if (input.provider === 'codex') {
|
|
180
|
+
legacyPaths.push(path.join(input.repoRoot, '.agents', 'skills', 'setup'));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
legacyPaths.push(path.join(input.repoRoot, input.providerDir, 'commands', 'setup.md'));
|
|
184
|
+
legacyPaths.push(path.join(input.repoRoot, input.providerDir, 'commands', 'specrails', 'setup.md'));
|
|
185
|
+
}
|
|
186
|
+
for (const target of legacyPaths) {
|
|
187
|
+
try {
|
|
188
|
+
rmSync(target, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
warn(`failed to prune legacy artifact ${target}: ${err.message}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Quick-tier placement: copy agents / commands / rules from the
|
|
197
|
+
* .specrails/setup-templates/ staging directory into the live
|
|
198
|
+
* provider directory, substituting template placeholders and
|
|
199
|
+
* excluding agents + commands whose dependencies are not present.
|
|
200
|
+
*
|
|
201
|
+
* Source is setup-templates/ (not scriptDir/templates/) so the pipeline
|
|
202
|
+
* is: scriptDir/templates/ → setup-templates/ (earlier scaffold step)
|
|
203
|
+
* → <providerDir>/ (this function). The intermediate hop mirrors the
|
|
204
|
+
* retired bash installer and lets downstream consumers (specrails-hub's
|
|
205
|
+
* deployTemplates, /specrails:enrich, update flow) read from a single
|
|
206
|
+
* canonical staging dir.
|
|
207
|
+
*/
|
|
208
|
+
function placeQuickTierArtefacts(input) {
|
|
209
|
+
const setupTemplates = path.join(input.repoRoot, '.specrails', 'setup-templates');
|
|
210
|
+
const projectName = path.basename(input.repoRoot);
|
|
211
|
+
const providerDirAbs = path.join(input.repoRoot, input.providerDir);
|
|
212
|
+
const placeholders = {
|
|
213
|
+
PROJECT_NAME: projectName,
|
|
214
|
+
SECURITY_EXEMPTIONS_PATH: `${input.providerDir}/security-exemptions.yaml`,
|
|
215
|
+
PERSONA_DIR: `${input.providerDir}/agents/personas/`,
|
|
216
|
+
};
|
|
217
|
+
// --- Agents ---
|
|
218
|
+
const agentsSrc = path.join(setupTemplates, 'agents');
|
|
219
|
+
const agentsDest = path.join(providerDirAbs, 'agents');
|
|
220
|
+
let agentsPlaced = 0;
|
|
221
|
+
let agentsSkipped = 0;
|
|
222
|
+
const installedAgentNames = new Set();
|
|
223
|
+
const selectedAgents = input.selectedAgents
|
|
224
|
+
? new Set([...input.selectedAgents, ...QUICK_REQUIRED_AGENTS])
|
|
225
|
+
: null;
|
|
226
|
+
if (isDir(agentsSrc)) {
|
|
227
|
+
mkdirp(agentsDest);
|
|
228
|
+
for (const src of listDir(agentsSrc)) {
|
|
229
|
+
const name = path.basename(src);
|
|
230
|
+
if (!name.endsWith('.md'))
|
|
231
|
+
continue;
|
|
232
|
+
const agentId = name.slice(0, -3);
|
|
233
|
+
if (selectedAgents && !selectedAgents.has(agentId))
|
|
234
|
+
continue;
|
|
235
|
+
if (QUICK_EXCLUDED_AGENTS.has(agentId)) {
|
|
236
|
+
agentsSkipped++;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const dest = path.join(agentsDest, name);
|
|
240
|
+
const rendered = renderPlaceholders(readTextFile(src), {
|
|
241
|
+
...placeholders,
|
|
242
|
+
MEMORY_PATH: `.claude/agent-memory/${agentId}/`,
|
|
243
|
+
});
|
|
244
|
+
writeFileLf(dest, rendered);
|
|
245
|
+
agentsPlaced++;
|
|
246
|
+
installedAgentNames.add(agentId);
|
|
247
|
+
// Per-agent memory directory. Created even when empty so
|
|
248
|
+
// the first run of the agent doesn't error on ENOENT.
|
|
249
|
+
mkdirp(path.join(input.repoRoot, '.claude', 'agent-memory', agentId));
|
|
250
|
+
if (EXPLANATION_AUTHORS.has(agentId)) {
|
|
251
|
+
mkdirp(path.join(input.repoRoot, '.claude', 'agent-memory', 'explanations'));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// --- Commands ---
|
|
256
|
+
// Skip commands whose required agents were excluded.
|
|
257
|
+
const excludedCommands = new Set();
|
|
258
|
+
for (const dep of COMMAND_AGENT_DEPENDENCIES) {
|
|
259
|
+
const hasAllRequired = dep.requires.every((a) => installedAgentNames.has(a));
|
|
260
|
+
if (!hasAllRequired)
|
|
261
|
+
excludedCommands.add(dep.command);
|
|
262
|
+
}
|
|
263
|
+
if (!input.agentTeams) {
|
|
264
|
+
excludedCommands.add('team-debug');
|
|
265
|
+
excludedCommands.add('team-review');
|
|
266
|
+
}
|
|
267
|
+
const commandsSrc = path.join(setupTemplates, 'commands', 'specrails');
|
|
268
|
+
const commandsDest = path.join(providerDirAbs, 'commands', 'specrails');
|
|
269
|
+
let commandsPlaced = 0;
|
|
270
|
+
if (isDir(commandsSrc)) {
|
|
271
|
+
mkdirp(commandsDest);
|
|
272
|
+
for (const src of listDir(commandsSrc)) {
|
|
273
|
+
const name = path.basename(src);
|
|
274
|
+
if (!name.endsWith('.md'))
|
|
275
|
+
continue;
|
|
276
|
+
const cmdId = name.slice(0, -3);
|
|
277
|
+
if (excludedCommands.has(cmdId))
|
|
278
|
+
continue;
|
|
279
|
+
const dest = path.join(commandsDest, name);
|
|
280
|
+
const rendered = renderPlaceholders(readTextFile(src), {
|
|
281
|
+
...placeholders,
|
|
282
|
+
MEMORY_PATH: '.claude/agent-memory/',
|
|
283
|
+
});
|
|
284
|
+
writeFileLf(dest, rendered);
|
|
285
|
+
commandsPlaced++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// --- Rules ---
|
|
289
|
+
const rulesSrc = path.join(setupTemplates, 'rules');
|
|
290
|
+
const rulesDest = path.join(providerDirAbs, 'rules');
|
|
291
|
+
let rulesPlaced = 0;
|
|
292
|
+
if (isDir(rulesSrc)) {
|
|
293
|
+
mkdirp(rulesDest);
|
|
294
|
+
for (const src of listDir(rulesSrc)) {
|
|
295
|
+
const name = path.basename(src);
|
|
296
|
+
if (!name.endsWith('.md'))
|
|
297
|
+
continue;
|
|
298
|
+
const dest = path.join(rulesDest, name);
|
|
299
|
+
const rendered = renderPlaceholders(readTextFile(src), placeholders);
|
|
300
|
+
writeFileLf(dest, rendered);
|
|
301
|
+
rulesPlaced++;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { agents: agentsPlaced, commands: commandsPlaced, rules: rulesPlaced, skippedAgents: agentsSkipped };
|
|
305
|
+
}
|
|
306
|
+
// Copy each `sr-*` subfolder from `.specrails/setup-templates/skills/`
|
|
307
|
+
// into `<providerDir>/skills/`. Skills are full directories
|
|
308
|
+
// (SKILL.md + optional assets), so the whole folder is copied.
|
|
309
|
+
// Quick tier excludes VPC-dependent skills; full tier copies all.
|
|
310
|
+
function placeSkills(input) {
|
|
311
|
+
const setupSkills = path.join(input.repoRoot, '.specrails', 'setup-templates', 'skills');
|
|
312
|
+
const destBase = path.join(input.repoRoot, input.providerDir, 'skills');
|
|
313
|
+
const result = { placed: 0, skipped: 0, filesCopied: 0 };
|
|
314
|
+
if (!isDir(setupSkills))
|
|
315
|
+
return result;
|
|
316
|
+
mkdirp(destBase);
|
|
317
|
+
for (const entry of listDir(setupSkills)) {
|
|
318
|
+
if (!isDir(entry))
|
|
319
|
+
continue;
|
|
320
|
+
const skillId = path.basename(entry);
|
|
321
|
+
if (input.tier === 'quick' && QUICK_EXCLUDED_SKILLS.has(skillId)) {
|
|
322
|
+
result.skipped++;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const dest = path.join(destBase, skillId);
|
|
326
|
+
copyDir(entry, dest);
|
|
327
|
+
result.placed++;
|
|
328
|
+
result.filesCopied += countFiles(dest);
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Substitutes `{{KEY}}` tokens in the input text with the provided
|
|
334
|
+
* values, then strips any remaining `{{UNKNOWN}}` tokens (replacing
|
|
335
|
+
* them with the empty string). Matches the retired bash installer's
|
|
336
|
+
* `sed` pipeline byte-for-byte for the documented token set.
|
|
337
|
+
*/
|
|
338
|
+
function renderPlaceholders(text, values) {
|
|
339
|
+
let out = text;
|
|
340
|
+
for (const [k, v] of Object.entries(values)) {
|
|
341
|
+
out = out.split(`{{${k}}}`).join(v);
|
|
342
|
+
}
|
|
343
|
+
return out.replace(/\{\{[A-Z_]*\}\}/g, '');
|
|
344
|
+
}
|
|
345
|
+
function ensureGitignore(repoRoot, entries) {
|
|
346
|
+
const p = path.join(repoRoot, '.gitignore');
|
|
347
|
+
let current = '';
|
|
348
|
+
if (pathExists(p)) {
|
|
349
|
+
current = readTextFile(p);
|
|
350
|
+
}
|
|
351
|
+
const needed = entries.filter((e) => !lineInFile(current, e));
|
|
352
|
+
if (needed.length === 0)
|
|
353
|
+
return;
|
|
354
|
+
const prefix = current.endsWith('\n') || current.length === 0 ? '' : '\n';
|
|
355
|
+
const block = ['', '# specrails', ...needed, ''].join('\n');
|
|
356
|
+
writeFileLf(p, `${current}${prefix}${block}`);
|
|
357
|
+
}
|
|
358
|
+
function lineInFile(contents, line) {
|
|
359
|
+
return contents.split(/\r?\n/).some((l) => l.trim() === line.trim());
|
|
360
|
+
}
|
|
361
|
+
function countFiles(dir) {
|
|
362
|
+
if (!isDir(dir))
|
|
363
|
+
return 0;
|
|
364
|
+
let n = 0;
|
|
365
|
+
for (const entry of listDir(dir)) {
|
|
366
|
+
if (isDir(entry))
|
|
367
|
+
n += countFiles(entry);
|
|
368
|
+
else
|
|
369
|
+
n++;
|
|
370
|
+
}
|
|
371
|
+
return n;
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/installer/phases/scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error hierarchy for the installer. Each subclass carries an
|
|
3
|
+
* `exitCode` which the CLI dispatcher translates to the process exit
|
|
4
|
+
* code so callers (shells, CI, specrails-hub wizard) can distinguish
|
|
5
|
+
* failure modes without string-parsing stdout.
|
|
6
|
+
*
|
|
7
|
+
* Exit-code ranges:
|
|
8
|
+
* 0 — success
|
|
9
|
+
* 1 — generic runtime error
|
|
10
|
+
* 10-19 — prerequisite failures (missing tools, auth, etc.)
|
|
11
|
+
* 20-29 — filesystem / I/O errors
|
|
12
|
+
* 30-39 — git errors
|
|
13
|
+
* 40-49 — provider detection / resolution errors
|
|
14
|
+
* 50-59 — child-process errors (spawned command failed)
|
|
15
|
+
* 60 — user aborted a prompt (Ctrl+C or non-TTY with no default)
|
|
16
|
+
*/
|
|
17
|
+
export class InstallerError extends Error {
|
|
18
|
+
exitCode;
|
|
19
|
+
constructor(message, exitCode = 1) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'InstallerError';
|
|
22
|
+
this.exitCode = exitCode;
|
|
23
|
+
// Preserve prototype chain across transpilation.
|
|
24
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class PrerequisiteError extends InstallerError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message, 10);
|
|
30
|
+
this.name = 'PrerequisiteError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class FilesystemError extends InstallerError {
|
|
34
|
+
path;
|
|
35
|
+
constructor(message, path) {
|
|
36
|
+
super(message, 20);
|
|
37
|
+
this.name = 'FilesystemError';
|
|
38
|
+
this.path = path;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class GitError extends InstallerError {
|
|
42
|
+
command;
|
|
43
|
+
constructor(message, command) {
|
|
44
|
+
super(message, 30);
|
|
45
|
+
this.name = 'GitError';
|
|
46
|
+
this.command = command;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export class ProviderError extends InstallerError {
|
|
50
|
+
constructor(message) {
|
|
51
|
+
super(message, 40);
|
|
52
|
+
this.name = 'ProviderError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class ExecError extends InstallerError {
|
|
56
|
+
command;
|
|
57
|
+
code;
|
|
58
|
+
stdout;
|
|
59
|
+
stderr;
|
|
60
|
+
constructor(command, code, stdout, stderr) {
|
|
61
|
+
super(`command "${command}" exited with code ${code}`, 50);
|
|
62
|
+
this.name = 'ExecError';
|
|
63
|
+
this.command = command;
|
|
64
|
+
this.code = code;
|
|
65
|
+
this.stdout = stdout;
|
|
66
|
+
this.stderr = stderr;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class PromptAbortError extends InstallerError {
|
|
70
|
+
constructor(message = 'prompt aborted by user or non-TTY environment') {
|
|
71
|
+
super(message, 60);
|
|
72
|
+
this.name = 'PromptAbortError';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Runtime check used by the CLI to map a caught unknown to a typed error. */
|
|
76
|
+
export function isInstallerError(err) {
|
|
77
|
+
return err instanceof InstallerError;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/installer/util/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,QAAQ,CAAQ;IAEzB,YAAY,OAAe,EAAE,WAAmB,CAAC;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,iDAAiD;QACjD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACnD,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,cAAc;IACnD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;IACjC,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,cAAc;IACxC,IAAI,CAAoB;IAEjC,YAAY,OAAe,EAAE,IAAa;QACxC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF;AAED,MAAM,OAAO,QAAS,SAAQ,cAAc;IACjC,OAAO,CAAoB;IAEpC,YAAY,OAAe,EAAE,OAAgB;QAC3C,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,cAAc;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,SAAU,SAAQ,cAAc;IAClC,OAAO,CAAQ;IACf,IAAI,CAAe;IACnB,MAAM,CAAQ;IACd,MAAM,CAAQ;IAEvB,YACE,OAAe,EACf,IAAmB,EACnB,MAAc,EACd,MAAc;QAEd,KAAK,CAAC,YAAY,OAAO,sBAAsB,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QAC1D,IAAI,CAAC,IAAI,GAAG,WAAW,CAAA;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,cAAc;IAClD,YAAY,UAAkB,+CAA+C;QAC3E,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;IAChC,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,GAAG,YAAY,cAAc,CAAA;AACtC,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
2
|
+
import { ExecError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Cross-platform spawn wrapper.
|
|
5
|
+
*
|
|
6
|
+
* - On Windows sets `shell: true` so `.cmd` / `.bat` shims (claude.cmd,
|
|
7
|
+
* npm.cmd, gh.cmd) are executable. Required by Node.js since
|
|
8
|
+
* CVE-2024-27980; without it, spawning a `.cmd` throws.
|
|
9
|
+
* - On POSIX sets `shell: false` to keep argv boundaries clean and
|
|
10
|
+
* avoid shell-injection surface on user-provided args.
|
|
11
|
+
* - Streams stdio by default so long commands show progress. Set
|
|
12
|
+
* `inherit: false` to capture stdout/stderr into the returned
|
|
13
|
+
* {@link RunResult}.
|
|
14
|
+
* - Throws {@link ExecError} on non-zero exit (captured stdout/stderr
|
|
15
|
+
* attached) unless the caller passed `inherit: true` (stream mode).
|
|
16
|
+
*/
|
|
17
|
+
export async function runCommand(cmd, args, opts = {}) {
|
|
18
|
+
const inherit = opts.inherit ?? true;
|
|
19
|
+
const useShell = process.platform === 'win32';
|
|
20
|
+
const spawnOpts = {
|
|
21
|
+
cwd: opts.cwd,
|
|
22
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
23
|
+
shell: useShell,
|
|
24
|
+
stdio: inherit ? 'inherit' : ['ignore', 'pipe', 'pipe'],
|
|
25
|
+
};
|
|
26
|
+
// When shell:true, Node hands argv to cmd.exe / sh which re-tokenises
|
|
27
|
+
// the command line. Args containing spaces (commit messages, paths
|
|
28
|
+
// with whitespace, JSON strings) get split unless we quote them.
|
|
29
|
+
// POSIX double-quotes work; Windows cmd.exe also accepts them when
|
|
30
|
+
// we escape inner double quotes as `""`.
|
|
31
|
+
const finalArgs = useShell ? args.map(quoteArgForShell) : args;
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const child = spawn(cmd, finalArgs, spawnOpts);
|
|
34
|
+
let stdout = '';
|
|
35
|
+
let stderr = '';
|
|
36
|
+
let timer = null;
|
|
37
|
+
if (!inherit) {
|
|
38
|
+
child.stdout?.on('data', (chunk) => {
|
|
39
|
+
stdout += chunk.toString('utf8');
|
|
40
|
+
});
|
|
41
|
+
child.stderr?.on('data', (chunk) => {
|
|
42
|
+
stderr += chunk.toString('utf8');
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (opts.timeoutMs && opts.timeoutMs > 0) {
|
|
46
|
+
timer = setTimeout(() => {
|
|
47
|
+
terminateProcessTree(child.pid);
|
|
48
|
+
}, opts.timeoutMs);
|
|
49
|
+
}
|
|
50
|
+
child.on('error', (err) => {
|
|
51
|
+
if (timer)
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
reject(err);
|
|
54
|
+
});
|
|
55
|
+
child.on('close', (code) => {
|
|
56
|
+
if (timer)
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
const exitCode = code ?? -1;
|
|
59
|
+
if (exitCode === 0) {
|
|
60
|
+
resolve({ code: exitCode, stdout, stderr });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (inherit) {
|
|
64
|
+
// In stream mode we did not capture output; surface an ExecError
|
|
65
|
+
// with empty stdout/stderr so callers still get a typed failure.
|
|
66
|
+
reject(new ExecError(`${cmd} ${args.join(' ')}`.trim(), exitCode, '', ''));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
reject(new ExecError(`${cmd} ${args.join(' ')}`.trim(), exitCode, stdout, stderr));
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Runs a command and returns whether it exited zero. Never throws.
|
|
75
|
+
* Useful for feature-detection style probes (`commandExists`,
|
|
76
|
+
* `git --version`).
|
|
77
|
+
*/
|
|
78
|
+
export async function tryRunCommand(cmd, args, opts = {}) {
|
|
79
|
+
try {
|
|
80
|
+
await runCommand(cmd, args, { ...opts, inherit: false });
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Cross-platform check for whether a command is on PATH. Uses `where`
|
|
89
|
+
* on Windows and `which` on POSIX.
|
|
90
|
+
*/
|
|
91
|
+
export async function commandExists(cmd) {
|
|
92
|
+
const probe = process.platform === 'win32' ? 'where' : 'which';
|
|
93
|
+
return tryRunCommand(probe, [cmd], { inherit: false });
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Terminates a child process tree, not just the immediate child.
|
|
97
|
+
*
|
|
98
|
+
* On POSIX, `child.kill('SIGKILL')` to the immediate child is enough
|
|
99
|
+
* because Node sends the signal to the process and (when detached)
|
|
100
|
+
* its group. We don't detach, so a SIGKILL to the leader works for
|
|
101
|
+
* the common case.
|
|
102
|
+
*
|
|
103
|
+
* On Windows, when shell:true is in effect (always, in this module),
|
|
104
|
+
* the immediate child is cmd.exe. Terminating cmd.exe leaves its
|
|
105
|
+
* spawned children orphaned with stdio pipes still open, and Node's
|
|
106
|
+
* 'close' event never fires on the parent handle. The fix is to ask
|
|
107
|
+
* Windows to terminate the entire descendant tree via taskkill /T /F.
|
|
108
|
+
*
|
|
109
|
+
* Falls back silently when the PID is unavailable or taskkill fails —
|
|
110
|
+
* the caller's promise will eventually reject either way (timeout
|
|
111
|
+
* source unchanged), so the worst-case outcome is the original
|
|
112
|
+
* "orphan child holds the pipe" behaviour.
|
|
113
|
+
*/
|
|
114
|
+
function terminateProcessTree(pid) {
|
|
115
|
+
if (pid === undefined)
|
|
116
|
+
return;
|
|
117
|
+
if (process.platform === 'win32') {
|
|
118
|
+
try {
|
|
119
|
+
// /T = include child processes, /F = force.
|
|
120
|
+
spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* taskkill missing or failed — fall through to single-process kill */
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// POSIX: SIGKILL the leader. Node delivers the signal directly.
|
|
128
|
+
try {
|
|
129
|
+
process.kill(pid, 'SIGKILL');
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
/* process already exited */
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Quotes a single argv entry so it survives re-tokenisation by cmd.exe
|
|
137
|
+
* (Windows shell:true) or sh (POSIX shell:true). Already-quoted
|
|
138
|
+
* arguments and arguments with no whitespace are returned untouched.
|
|
139
|
+
*
|
|
140
|
+
* The quoting strategy is deliberately simple: wrap in double quotes,
|
|
141
|
+
* escape inner double quotes as `\"`. cmd.exe accepts this dialect
|
|
142
|
+
* uniformly and POSIX sh does too.
|
|
143
|
+
*/
|
|
144
|
+
function quoteArgForShell(arg) {
|
|
145
|
+
if (arg.length === 0)
|
|
146
|
+
return '""';
|
|
147
|
+
if (!/\s|[<>|&^"`$\\]/.test(arg))
|
|
148
|
+
return arg;
|
|
149
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/installer/util/exec.ts"],"names":[],"mappings":"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"}
|