specrails-core 4.11.3 → 5.0.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.
- package/README.md +96 -89
- package/bin/specrails-core.mjs +282 -39
- package/bin/tui-installer.mjs +117 -149
- package/commands/doctor.md +1 -1
- package/dist/installer/cli.js +13 -3
- package/dist/installer/cli.js.map +1 -1
- package/dist/installer/commands/doctor.js +487 -27
- package/dist/installer/commands/doctor.js.map +1 -1
- package/dist/installer/commands/framework.js +49 -7
- package/dist/installer/commands/framework.js.map +1 -1
- package/dist/installer/commands/init.js +443 -41
- package/dist/installer/commands/init.js.map +1 -1
- package/dist/installer/commands/update.js +51 -23
- package/dist/installer/commands/update.js.map +1 -1
- package/dist/installer/commands/v5-migration.js +119 -0
- package/dist/installer/commands/v5-migration.js.map +1 -0
- package/dist/installer/phases/framework-lifecycle.js +125 -0
- package/dist/installer/phases/framework-lifecycle.js.map +1 -0
- package/dist/installer/phases/install-config.js +160 -11
- package/dist/installer/phases/install-config.js.map +1 -1
- package/dist/installer/phases/manifest.js +29 -8
- package/dist/installer/phases/manifest.js.map +1 -1
- package/dist/installer/phases/prereqs.js +57 -3
- package/dist/installer/phases/prereqs.js.map +1 -1
- package/dist/installer/phases/provider-detect.js +116 -6
- package/dist/installer/phases/provider-detect.js.map +1 -1
- package/dist/installer/phases/scaffold.js +1217 -117
- package/dist/installer/phases/scaffold.js.map +1 -1
- package/dist/installer/runtime/kimi.js +255 -0
- package/dist/installer/runtime/kimi.js.map +1 -0
- package/dist/installer/util/paths.js +12 -0
- package/dist/installer/util/paths.js.map +1 -1
- package/dist/installer/util/registry.js +234 -14
- package/dist/installer/util/registry.js.map +1 -1
- package/docs/README.md +1 -0
- package/docs/deployment.md +6 -7
- package/docs/getting-started.md +11 -7
- package/docs/installation.md +34 -16
- package/docs/plugin-architecture.md +11 -8
- package/docs/updating.md +21 -3
- package/docs/user-docs/cli-reference.md +43 -22
- package/docs/user-docs/codex-vs-claude-code.md +11 -9
- package/docs/user-docs/faq.md +1 -1
- package/docs/user-docs/getting-started-codex.md +5 -8
- package/docs/user-docs/getting-started-kimi.md +423 -0
- package/docs/user-docs/installation.md +49 -14
- package/docs/user-docs/quick-start.md +11 -8
- package/docs/windows.md +29 -4
- package/integration-contract.json +85 -13
- package/package.json +9 -5
- package/schemas/profile.v1.json +68 -6
- package/templates/agents/sr-architect.md +30 -0
- package/templates/agents/sr-developer.md +21 -8
- package/templates/agents/sr-reviewer.md +44 -31
- package/templates/codex-skills/batch-implement/SKILL.md +9 -32
- package/templates/codex-skills/implement/SKILL.md +61 -143
- package/templates/codex-skills/rails/sr-architect/SKILL.md +38 -20
- package/templates/codex-skills/rails/sr-developer/SKILL.md +29 -10
- package/templates/codex-skills/rails/sr-reviewer/SKILL.md +21 -10
- package/templates/commands/specrails/doctor.md +1 -1
- package/templates/commands/specrails/implement.md +117 -288
- package/templates/commands/specrails/memory-inspect.md +6 -4
- package/templates/commands/specrails/propose-spec.md +1 -1
- package/templates/commands/specrails/refactor-recommender.md +8 -51
- package/templates/commands/specrails/retry.md +12 -48
- package/templates/commands/specrails/telemetry.md +1 -1
- package/templates/gemini-commands/implement.toml +9 -0
- package/templates/kimi/specrails/run-skill.mjs +3005 -0
- package/templates/kimi/specrails/vendor/js-yaml/LICENSE +21 -0
- package/templates/kimi/specrails/vendor/js-yaml/NOTICE.md +16 -0
- package/templates/kimi/specrails/vendor/js-yaml/js-yaml.mjs +3856 -0
- package/templates/profiles/default.json +5 -18
- package/templates/profiles/kimi-default.json +15 -0
- package/commands/enrich.md +0 -1456
- package/templates/agents/sr-backend-developer.md +0 -91
- package/templates/agents/sr-backend-reviewer.md +0 -152
- package/templates/agents/sr-doc-sync.md +0 -247
- package/templates/agents/sr-frontend-developer.md +0 -85
- package/templates/agents/sr-frontend-reviewer.md +0 -145
- package/templates/agents/sr-merge-resolver.md +0 -195
- package/templates/agents/sr-performance-reviewer.md +0 -186
- package/templates/agents/sr-product-analyst.md +0 -36
- package/templates/agents/sr-product-manager.md +0 -148
- package/templates/agents/sr-security-reviewer.md +0 -191
- package/templates/agents/sr-test-writer.md +0 -176
- package/templates/codex-skills/enrich/SKILL.md +0 -191
- package/templates/codex-skills/merge-resolve/SKILL.md +0 -88
- package/templates/codex-skills/rails/sr-backend-developer/SKILL.md +0 -93
- package/templates/codex-skills/rails/sr-backend-reviewer/SKILL.md +0 -120
- package/templates/codex-skills/rails/sr-doc-sync/SKILL.md +0 -124
- package/templates/codex-skills/rails/sr-frontend-developer/SKILL.md +0 -106
- package/templates/codex-skills/rails/sr-frontend-reviewer/SKILL.md +0 -111
- package/templates/codex-skills/rails/sr-merge-resolver/SKILL.md +0 -156
- package/templates/codex-skills/rails/sr-performance-reviewer/SKILL.md +0 -109
- package/templates/codex-skills/rails/sr-product-analyst/SKILL.md +0 -85
- package/templates/codex-skills/rails/sr-product-manager/SKILL.md +0 -131
- package/templates/codex-skills/rails/sr-security-reviewer/SKILL.md +0 -121
- package/templates/codex-skills/rails/sr-test-writer/SKILL.md +0 -115
- package/templates/commands/specrails/auto-propose-backlog-specs.md +0 -312
- package/templates/commands/specrails/enrich.md +0 -1456
- package/templates/commands/specrails/get-backlog-specs.md +0 -226
- package/templates/commands/specrails/merge-resolve.md +0 -172
- package/templates/commands/specrails/reconfig.md +0 -80
- package/templates/commands/specrails/vpc-drift.md +0 -405
- package/templates/commands/test.md +0 -58
- package/templates/personas/persona.md +0 -43
- package/templates/personas/the-maintainer.md +0 -98
- package/templates/settings/perf-thresholds.yml +0 -25
|
@@ -2,6 +2,53 @@ import path from 'node:path';
|
|
|
2
2
|
import yaml from 'js-yaml';
|
|
3
3
|
import { FilesystemError, InstallerError } from '../util/errors.js';
|
|
4
4
|
import { pathExists, readTextFile, writeFileLf } from '../util/fs.js';
|
|
5
|
+
const PROVIDER_DEFAULT_MODELS = {
|
|
6
|
+
claude: 'sonnet',
|
|
7
|
+
codex: 'gpt-5.5-mini',
|
|
8
|
+
gemini: 'gemini-3.5-flash',
|
|
9
|
+
kimi: 'k3',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Agent ids become provider-native file/directory names. Keep them to the
|
|
13
|
+
* portable direct-child skill grammar so config cannot escape an install root
|
|
14
|
+
* or create names that Kimi's managed runner will later reject.
|
|
15
|
+
*/
|
|
16
|
+
const SAFE_AGENT_ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
17
|
+
/**
|
|
18
|
+
* Must remain byte-for-byte equivalent to the canonical Kimi runtime and the
|
|
19
|
+
* public integration contract. Config validation happens before any framework
|
|
20
|
+
* write so an unsafe model can never create an install that only fails later at
|
|
21
|
+
* process launch.
|
|
22
|
+
*/
|
|
23
|
+
const SAFE_KIMI_MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$/;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a preset into provider-native identifiers. Kimi currently exposes no
|
|
26
|
+
* Core-defined cost/capability tier mapping, so every named preset resolves to
|
|
27
|
+
* its explicit `k3` default. User-supplied exact identifiers are handled later
|
|
28
|
+
* and are never interpreted as Claude aliases.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveProviderModelConfig(provider, preset = 'balanced') {
|
|
31
|
+
if (provider === 'claude') {
|
|
32
|
+
if (preset === 'budget') {
|
|
33
|
+
return { preset, defaults: { model: 'haiku' }, overrides: {} };
|
|
34
|
+
}
|
|
35
|
+
if (preset === 'max') {
|
|
36
|
+
return {
|
|
37
|
+
preset,
|
|
38
|
+
defaults: { model: 'sonnet' },
|
|
39
|
+
overrides: {
|
|
40
|
+
'sr-architect': 'opus',
|
|
41
|
+
'sr-product-manager': 'opus',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
preset,
|
|
48
|
+
defaults: { model: PROVIDER_DEFAULT_MODELS[provider] },
|
|
49
|
+
overrides: {},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
5
52
|
export class InvalidConfigError extends InstallerError {
|
|
6
53
|
errors;
|
|
7
54
|
constructor(errors) {
|
|
@@ -57,13 +104,18 @@ export function validateInstallConfig(raw) {
|
|
|
57
104
|
if (doc.provider === undefined) {
|
|
58
105
|
errors.push(`missing required 'provider' field`);
|
|
59
106
|
}
|
|
60
|
-
else if (doc.provider !== 'claude' &&
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
errors.push(`unsupported
|
|
107
|
+
else if (doc.provider !== 'claude' &&
|
|
108
|
+
doc.provider !== 'codex' &&
|
|
109
|
+
doc.provider !== 'gemini' &&
|
|
110
|
+
doc.provider !== 'kimi') {
|
|
111
|
+
errors.push(`unsupported provider '${String(doc.provider)}' (expected: claude, codex, gemini, or kimi)`);
|
|
65
112
|
}
|
|
113
|
+
// NOTE: a legacy `tier` field (`full`/`quick`) may still be present in pre-v5
|
|
114
|
+
// configs. It is intentionally ignored (not rejected and not carried forward)
|
|
115
|
+
// — v5 has no install tiers.
|
|
66
116
|
const agents = doc.agents;
|
|
117
|
+
let selectedAgents = [];
|
|
118
|
+
let excludedAgents;
|
|
67
119
|
if (!agents || typeof agents !== 'object') {
|
|
68
120
|
errors.push(`missing required 'agents' section with 'selected' list`);
|
|
69
121
|
}
|
|
@@ -71,6 +123,23 @@ export function validateInstallConfig(raw) {
|
|
|
71
123
|
if (!Array.isArray(agents.selected)) {
|
|
72
124
|
errors.push(`'agents.selected' must be a list`);
|
|
73
125
|
}
|
|
126
|
+
else {
|
|
127
|
+
selectedAgents = validateAgentIdList(agents.selected, 'agents.selected', errors);
|
|
128
|
+
}
|
|
129
|
+
if (agents.excluded !== undefined && !Array.isArray(agents.excluded)) {
|
|
130
|
+
errors.push(`'agents.excluded' must be a list`);
|
|
131
|
+
}
|
|
132
|
+
else if (Array.isArray(agents.excluded)) {
|
|
133
|
+
excludedAgents = validateAgentIdList(agents.excluded, 'agents.excluded', errors);
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(agents.selected) && Array.isArray(agents.excluded)) {
|
|
136
|
+
const excluded = new Set(excludedAgents ?? []);
|
|
137
|
+
for (const agent of selectedAgents) {
|
|
138
|
+
if (excluded.has(agent)) {
|
|
139
|
+
errors.push(`'agents.selected' and 'agents.excluded' must not overlap: ${agent}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
74
143
|
if (agents.preset !== undefined &&
|
|
75
144
|
agents.preset !== 'balanced' &&
|
|
76
145
|
agents.preset !== 'budget' &&
|
|
@@ -78,27 +147,107 @@ export function validateInstallConfig(raw) {
|
|
|
78
147
|
errors.push(`unsupported preset '${String(agents.preset)}' (expected: balanced, budget, or max)`);
|
|
79
148
|
}
|
|
80
149
|
}
|
|
150
|
+
const models = doc.models;
|
|
151
|
+
if (doc.models !== undefined && (!models || typeof models !== 'object')) {
|
|
152
|
+
errors.push(`'models' must be a mapping`);
|
|
153
|
+
}
|
|
154
|
+
const modelPreset = models?.preset;
|
|
155
|
+
if (modelPreset !== undefined &&
|
|
156
|
+
modelPreset !== 'balanced' &&
|
|
157
|
+
modelPreset !== 'budget' &&
|
|
158
|
+
modelPreset !== 'max') {
|
|
159
|
+
errors.push(`unsupported model preset '${String(modelPreset)}' (expected: balanced, budget, or max)`);
|
|
160
|
+
}
|
|
161
|
+
const provider = doc.provider;
|
|
162
|
+
const modelDefaults = models?.defaults;
|
|
163
|
+
if (models?.defaults !== undefined &&
|
|
164
|
+
(!modelDefaults || typeof modelDefaults !== 'object')) {
|
|
165
|
+
errors.push(`'models.defaults' must be a mapping`);
|
|
166
|
+
}
|
|
167
|
+
else if (modelDefaults?.model !== undefined &&
|
|
168
|
+
!isValidModelIdentifier(provider, modelDefaults.model)) {
|
|
169
|
+
errors.push(modelIdentifierError('models.defaults.model', provider));
|
|
170
|
+
}
|
|
171
|
+
const modelOverrides = models?.overrides;
|
|
172
|
+
if (modelOverrides !== undefined &&
|
|
173
|
+
(!modelOverrides || typeof modelOverrides !== 'object' || Array.isArray(modelOverrides))) {
|
|
174
|
+
errors.push(`'models.overrides' must be a mapping`);
|
|
175
|
+
}
|
|
176
|
+
else if (modelOverrides && typeof modelOverrides === 'object') {
|
|
177
|
+
for (const [agent, model] of Object.entries(modelOverrides)) {
|
|
178
|
+
if (!SAFE_AGENT_ID.test(agent)) {
|
|
179
|
+
errors.push(`'models.overrides' key '${agent}' must be a lowercase kebab-case agent id (1-64 characters)`);
|
|
180
|
+
}
|
|
181
|
+
if (!isValidModelIdentifier(provider, model)) {
|
|
182
|
+
errors.push(modelIdentifierError(`models.overrides.${agent}`, provider));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
81
186
|
if (errors.length > 0) {
|
|
82
187
|
throw new InvalidConfigError(errors);
|
|
83
188
|
}
|
|
84
|
-
const
|
|
189
|
+
const preset = (modelPreset ?? agents?.preset ?? 'balanced');
|
|
190
|
+
const resolvedModels = resolveProviderModelConfig(provider, preset);
|
|
191
|
+
if (typeof modelDefaults?.model === 'string') {
|
|
192
|
+
resolvedModels.defaults.model = modelDefaults.model;
|
|
193
|
+
}
|
|
194
|
+
if (modelOverrides && typeof modelOverrides === 'object') {
|
|
195
|
+
resolvedModels.overrides = { ...modelOverrides };
|
|
196
|
+
}
|
|
85
197
|
const result = {
|
|
86
198
|
version: 1,
|
|
87
|
-
provider
|
|
199
|
+
provider,
|
|
88
200
|
agents: {
|
|
89
|
-
selected,
|
|
201
|
+
selected: selectedAgents,
|
|
90
202
|
},
|
|
203
|
+
models: resolvedModels,
|
|
91
204
|
};
|
|
92
205
|
// NOTE: a legacy `agent_teams` field may still be present in older configs.
|
|
93
206
|
// It is intentionally ignored (not rejected) for backward compatibility.
|
|
94
|
-
if (doc.tier !== undefined) {
|
|
95
|
-
result.tier = doc.tier;
|
|
96
|
-
}
|
|
97
207
|
if (agents?.preset !== undefined) {
|
|
98
208
|
result.agents.preset = agents.preset;
|
|
99
209
|
}
|
|
210
|
+
if (excludedAgents !== undefined) {
|
|
211
|
+
result.agents.excluded = excludedAgents;
|
|
212
|
+
}
|
|
100
213
|
return result;
|
|
101
214
|
}
|
|
215
|
+
function validateAgentIdList(value, field, errors) {
|
|
216
|
+
const valid = [];
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
for (const [index, entry] of value.entries()) {
|
|
219
|
+
if (typeof entry !== 'string' || !SAFE_AGENT_ID.test(entry)) {
|
|
220
|
+
errors.push(`'${field}[${index}]' must be a lowercase kebab-case agent id (1-64 characters)`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (seen.has(entry)) {
|
|
224
|
+
errors.push(`'${field}' must not contain duplicate agent id '${entry}'`);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
seen.add(entry);
|
|
228
|
+
valid.push(entry);
|
|
229
|
+
}
|
|
230
|
+
return valid;
|
|
231
|
+
}
|
|
232
|
+
function isValidModelIdentifier(provider, value) {
|
|
233
|
+
if (provider === 'kimi') {
|
|
234
|
+
return typeof value === 'string' && SAFE_KIMI_MODEL_ID.test(value);
|
|
235
|
+
}
|
|
236
|
+
return isExactModelIdentifier(value);
|
|
237
|
+
}
|
|
238
|
+
function modelIdentifierError(field, provider) {
|
|
239
|
+
if (provider === 'kimi') {
|
|
240
|
+
return (`'${field}' must be a safe Kimi model id: 1-128 characters matching ` +
|
|
241
|
+
`[A-Za-z0-9][A-Za-z0-9._/:-]*`);
|
|
242
|
+
}
|
|
243
|
+
return `'${field}' must be a nonblank provider model identifier`;
|
|
244
|
+
}
|
|
245
|
+
function isExactModelIdentifier(value) {
|
|
246
|
+
return (typeof value === 'string' &&
|
|
247
|
+
value.length > 0 &&
|
|
248
|
+
value.trim().length > 0 &&
|
|
249
|
+
value === value.trim());
|
|
250
|
+
}
|
|
102
251
|
/**
|
|
103
252
|
* Serialises an {@link InstallConfig} back to YAML and writes it with
|
|
104
253
|
* LF line endings. Used by `npx specrails-core update --reset-config`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-config.js","sourceRoot":"","sources":["../../../src/installer/phases/install-config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,IAAI,MAAM,SAAS,CAAA;AAE1B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"install-config.js","sourceRoot":"","sources":["../../../src/installer/phases/install-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { FilesystemError } from '../util/errors.js';
|
|
5
|
-
import { readBytes, writeFileLf } from '../util/fs.js';
|
|
5
|
+
import { pathExists, readBytes, readTextFile, writeFileLf } from '../util/fs.js';
|
|
6
6
|
/**
|
|
7
7
|
* Computes a stable sha256 digest for a file, returned as `sha256:<hex>`.
|
|
8
8
|
*/
|
|
@@ -12,10 +12,8 @@ export function sha256Of(filePath) {
|
|
|
12
12
|
return `sha256:${hash}`;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Walks `templates/**` plus the
|
|
16
|
-
* produces a stable-sorted manifest.
|
|
17
|
-
* the retired `generate_manifest` bash helper so the byte-diff CI
|
|
18
|
-
* fixture passes without modification.
|
|
15
|
+
* Walks `templates/**` plus the bundled doctor command file and
|
|
16
|
+
* produces a stable-sorted manifest.
|
|
19
17
|
*
|
|
20
18
|
* Stable-sort rule: artifact keys are sorted ascending by POSIX path.
|
|
21
19
|
*/
|
|
@@ -27,15 +25,38 @@ export function buildManifest(input) {
|
|
|
27
25
|
const rel = path.relative(input.scriptDir, absFile).split(path.sep).join('/');
|
|
28
26
|
artifacts[rel] = sha256Of(absFile);
|
|
29
27
|
});
|
|
30
|
-
const enrichPath = path.join(input.scriptDir, 'commands', 'enrich.md');
|
|
31
28
|
const doctorPath = path.join(input.scriptDir, 'commands', 'doctor.md');
|
|
32
|
-
artifacts['commands/specrails/enrich.md'] = sha256Of(enrichPath);
|
|
33
29
|
artifacts['commands/specrails/doctor.md'] = sha256Of(doctorPath);
|
|
34
|
-
|
|
30
|
+
const manifest = {
|
|
35
31
|
version: input.version,
|
|
36
32
|
installed_at,
|
|
37
33
|
artifacts: sortKeys(artifacts),
|
|
38
34
|
};
|
|
35
|
+
if (input.providers && input.providers.length > 0) {
|
|
36
|
+
const existing = readExistingProviderInventory(input.repoRoot);
|
|
37
|
+
manifest.providers = [...new Set([...existing.providers, ...input.providers])];
|
|
38
|
+
manifest.primary_provider =
|
|
39
|
+
existing.primaryProvider ?? input.primaryProvider ?? manifest.providers[0];
|
|
40
|
+
}
|
|
41
|
+
return manifest;
|
|
42
|
+
}
|
|
43
|
+
function readExistingProviderInventory(repoRoot) {
|
|
44
|
+
const manifestPath = path.join(repoRoot, '.specrails', 'specrails-manifest.json');
|
|
45
|
+
if (!pathExists(manifestPath))
|
|
46
|
+
return { providers: [] };
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(readTextFile(manifestPath));
|
|
49
|
+
const providers = Array.isArray(parsed.providers)
|
|
50
|
+
? parsed.providers.filter((provider) => typeof provider === 'string')
|
|
51
|
+
: [];
|
|
52
|
+
return {
|
|
53
|
+
providers,
|
|
54
|
+
primaryProvider: typeof parsed.primary_provider === 'string' ? parsed.primary_provider : undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return { providers: [] };
|
|
59
|
+
}
|
|
39
60
|
}
|
|
40
61
|
/**
|
|
41
62
|
* Writes the manifest JSON and the companion version file under
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/installer/phases/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/installer/phases/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,13 +4,41 @@ import { commandExists, runCommand } from '../util/exec.js';
|
|
|
4
4
|
import { isDir, isFile, listDir } from '../util/fs.js';
|
|
5
5
|
import { info, ok, warn } from '../util/logger.js';
|
|
6
6
|
import { gitInstalled, initRepo, isGitRepo, repoRoot } from '../util/git.js';
|
|
7
|
-
import { assertClaudeAuthenticated, claudeVersion, detectAvailability, resolveProvider, } from './provider-detect.js';
|
|
7
|
+
import { assertClaudeAuthenticated, assertKimiAuthenticated, claudeVersion, detectAvailability, isSupportedKimiVersion, kimiVersion, MIN_KIMI_VERSION, resolveProvider, } from './provider-detect.js';
|
|
8
|
+
/**
|
|
9
|
+
* OpenSpec 1.4.1, which the installer invokes during the default init/update
|
|
10
|
+
* flow, requires Node >=20.19.0. Keep this floor independent from Kimi's npm
|
|
11
|
+
* package requirement: SpecRails launches an externally installed Kimi CLI and
|
|
12
|
+
* does not require the Kimi npm distribution.
|
|
13
|
+
*/
|
|
14
|
+
export const MIN_NODE_VERSION = '20.19.0';
|
|
15
|
+
export function isSupportedNodeVersion(version) {
|
|
16
|
+
const parsed = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version.trim());
|
|
17
|
+
if (!parsed)
|
|
18
|
+
return false;
|
|
19
|
+
const actual = parsed.slice(1, 4).map(Number);
|
|
20
|
+
const minimum = MIN_NODE_VERSION.split('.').map(Number);
|
|
21
|
+
for (let index = 0; index < minimum.length; index++) {
|
|
22
|
+
if (actual[index] > minimum[index])
|
|
23
|
+
return true;
|
|
24
|
+
if (actual[index] < minimum[index])
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
8
29
|
/**
|
|
9
30
|
* Orchestrates every prerequisite check that must pass before the
|
|
10
31
|
* installer proceeds to Phase 3 (scaffolding). Emits ok/warn/info
|
|
11
32
|
* lines matching the retired bash output.
|
|
12
33
|
*/
|
|
13
34
|
export async function checkPrerequisites(options) {
|
|
35
|
+
const nodeVersion = process.versions.node;
|
|
36
|
+
if (!isSupportedNodeVersion(nodeVersion)) {
|
|
37
|
+
throw new PrerequisiteError(`Node.js ${nodeVersion} is unsupported. SpecRails requires Node.js ` +
|
|
38
|
+
`${MIN_NODE_VERSION} or newer because OpenSpec 1.4.1 uses that runtime floor. ` +
|
|
39
|
+
'Install a supported Node.js release from https://nodejs.org/');
|
|
40
|
+
}
|
|
41
|
+
ok(`Node.js: ${nodeVersion}`);
|
|
14
42
|
if (!(await gitInstalled())) {
|
|
15
43
|
throw new PrerequisiteError('git is required but not on PATH. Install git: https://git-scm.com/');
|
|
16
44
|
}
|
|
@@ -40,18 +68,45 @@ export async function checkPrerequisites(options) {
|
|
|
40
68
|
ok(`Provider: claude (--provider flag)`);
|
|
41
69
|
}
|
|
42
70
|
}
|
|
71
|
+
if (provider === 'kimi') {
|
|
72
|
+
if (availability.kimi) {
|
|
73
|
+
const v = await kimiVersion();
|
|
74
|
+
if (!isSupportedKimiVersion(v) && !options.skipPrereqs) {
|
|
75
|
+
throw new PrerequisiteError(`Kimi Code ${v} is unsupported. Upgrade to ${MIN_KIMI_VERSION} or newer: ` +
|
|
76
|
+
'https://www.kimi.com/code/docs/en/kimi-code-cli/guides/getting-started.html');
|
|
77
|
+
}
|
|
78
|
+
ok(`Kimi Code CLI: ${v}`);
|
|
79
|
+
}
|
|
80
|
+
else if (options.explicitProvider === 'kimi') {
|
|
81
|
+
if (!options.skipPrereqs) {
|
|
82
|
+
throw new PrerequisiteError('Kimi Code CLI is not installed. Install it from https://www.kimi.com/code/docs/en/ ' +
|
|
83
|
+
'and retry, or run with SPECRAILS_SKIP_PREREQS=1 for fixture generation.');
|
|
84
|
+
}
|
|
85
|
+
ok('Provider: kimi (--provider flag)');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
43
88
|
// 1.3 Authentication for the selected provider.
|
|
44
89
|
if (provider === 'claude') {
|
|
45
90
|
await assertClaudeAuthenticated({ skipPrereqs: options.skipPrereqs });
|
|
46
91
|
ok('Claude: authenticated');
|
|
47
92
|
}
|
|
93
|
+
if (provider === 'kimi' && availability.kimi) {
|
|
94
|
+
const status = await assertKimiAuthenticated({ skipPrereqs: options.skipPrereqs });
|
|
95
|
+
if (status === 'authenticated') {
|
|
96
|
+
ok('Kimi: authentication evidence found');
|
|
97
|
+
}
|
|
98
|
+
else if (status === 'unknown') {
|
|
99
|
+
info('Kimi authentication could not be proven without a billable prompt; ' +
|
|
100
|
+
'the first workflow will report 401/login errors. Run `kimi login` if needed.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
48
103
|
// 1.4 npm — required for running the TUI and for the `update` command.
|
|
49
104
|
if (!(await commandExists('npm'))) {
|
|
50
105
|
if (options.skipPrereqs) {
|
|
51
106
|
warn('npm not found (skipped — SPECRAILS_SKIP_PREREQS=1)');
|
|
52
107
|
}
|
|
53
108
|
else {
|
|
54
|
-
throw new PrerequisiteError(
|
|
109
|
+
throw new PrerequisiteError(`npm is required but not on PATH. Install Node.js ${MIN_NODE_VERSION}+ from https://nodejs.org/`);
|
|
55
110
|
}
|
|
56
111
|
}
|
|
57
112
|
else {
|
|
@@ -81,7 +136,6 @@ export async function checkPrerequisites(options) {
|
|
|
81
136
|
if (ossSignals.isOss) {
|
|
82
137
|
ok('OSS project detected (public repo + CI + CONTRIBUTING.md)');
|
|
83
138
|
}
|
|
84
|
-
// 1.8 JIRA CLI — silently skipped when missing (only relevant in enrich).
|
|
85
139
|
return { availability, provider, ossSignals };
|
|
86
140
|
}
|
|
87
141
|
async function detectOssSignals(repoRoot, hasGh) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prereqs.js","sourceRoot":"","sources":["../../../src/installer/phases/prereqs.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE5E,OAAO,EAGL,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"prereqs.js","sourceRoot":"","sources":["../../../src/installer/phases/prereqs.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,17 +3,20 @@ import path from 'node:path';
|
|
|
3
3
|
import { ProviderError } from '../util/errors.js';
|
|
4
4
|
import { commandExists, runCommand } from '../util/exec.js';
|
|
5
5
|
import { pathExists, readTextFile } from '../util/fs.js';
|
|
6
|
+
/** Oldest TypeScript Kimi Code CLI release covered by this integration. */
|
|
7
|
+
export const MIN_KIMI_VERSION = '0.27.0';
|
|
6
8
|
/**
|
|
7
9
|
* Detects which AI CLIs are on PATH. Runs `where` on Windows and
|
|
8
10
|
* `which` on POSIX via the cross-platform commandExists helper.
|
|
9
11
|
*/
|
|
10
12
|
export async function detectAvailability() {
|
|
11
|
-
const [claude, codex, gemini] = await Promise.all([
|
|
13
|
+
const [claude, codex, gemini, kimi] = await Promise.all([
|
|
12
14
|
commandExists('claude'),
|
|
13
15
|
commandExists('codex'),
|
|
14
16
|
commandExists('gemini'),
|
|
17
|
+
commandExists('kimi'),
|
|
15
18
|
]);
|
|
16
|
-
return { claude, codex, gemini };
|
|
19
|
+
return { claude, codex, gemini, kimi };
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* Returns the Claude CLI version string (stdout of `claude --version`)
|
|
@@ -28,13 +31,36 @@ export async function claudeVersion() {
|
|
|
28
31
|
return 'unknown';
|
|
29
32
|
}
|
|
30
33
|
}
|
|
34
|
+
/** Returns the raw Kimi Code CLI version or `unknown` when it cannot be probed. */
|
|
35
|
+
export async function kimiVersion() {
|
|
36
|
+
try {
|
|
37
|
+
const { stdout, stderr } = await runCommand('kimi', ['--version'], {
|
|
38
|
+
inherit: false,
|
|
39
|
+
timeoutMs: 10_000,
|
|
40
|
+
});
|
|
41
|
+
return stdout.trim() || stderr.trim() || 'unknown';
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return 'unknown';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Extracts a semver-like triple from version output such as `kimi-code 0.27.0`. */
|
|
48
|
+
export function parseCliVersion(raw) {
|
|
49
|
+
return /(?:^|[^\d])(\d+)\.(\d+)\.(\d+)(?:[^\d]|$)/.exec(raw)?.slice(1, 4).join('.') ?? null;
|
|
50
|
+
}
|
|
51
|
+
/** True when `raw` identifies a Kimi CLI at or above {@link MIN_KIMI_VERSION}. */
|
|
52
|
+
export function isSupportedKimiVersion(raw) {
|
|
53
|
+
const parsed = parseCliVersion(raw);
|
|
54
|
+
if (!parsed)
|
|
55
|
+
return false;
|
|
56
|
+
return compareVersionTriples(parsed, MIN_KIMI_VERSION) >= 0;
|
|
57
|
+
}
|
|
31
58
|
/**
|
|
32
59
|
* Resolves which provider the installer should use. Priority:
|
|
33
60
|
* 1. Explicit `--provider claude` flag (already passed through args).
|
|
34
61
|
* 2. Config file (read upstream in install-config.ts).
|
|
35
62
|
* 3. Whichever CLI is installed.
|
|
36
63
|
*
|
|
37
|
-
* Codex-only installs error out — see design: Codex is "coming soon".
|
|
38
64
|
* The caller may bypass prereq failures with `skipPrereqs: true`
|
|
39
65
|
* (env `SPECRAILS_SKIP_PREREQS=1`).
|
|
40
66
|
*/
|
|
@@ -45,26 +71,33 @@ export async function resolveProvider(availability, options = {}) {
|
|
|
45
71
|
return 'claude';
|
|
46
72
|
if (options.explicit === 'gemini')
|
|
47
73
|
return 'gemini';
|
|
74
|
+
if (options.explicit === 'kimi')
|
|
75
|
+
return 'kimi';
|
|
48
76
|
// No multi-provider auto-pick beyond the historical Claude-first default: when
|
|
49
77
|
// several CLIs are installed and no provider was requested, prefer Claude so
|
|
50
78
|
// existing projects don't get re-bootstrapped onto a different provider. Users
|
|
51
|
-
// pick codex/gemini via --provider or install-config.yaml.
|
|
79
|
+
// pick codex/gemini/kimi via --provider or install-config.yaml.
|
|
52
80
|
if (availability.claude)
|
|
53
81
|
return 'claude';
|
|
54
82
|
if (availability.codex)
|
|
55
83
|
return 'codex';
|
|
56
84
|
if (availability.gemini)
|
|
57
85
|
return 'gemini';
|
|
86
|
+
if (availability.kimi)
|
|
87
|
+
return 'kimi';
|
|
58
88
|
if (options.skipPrereqs)
|
|
59
89
|
return 'claude';
|
|
60
90
|
throw new ProviderError('No AI CLI found. Install Claude Code (https://claude.ai/download), ' +
|
|
61
91
|
'Codex CLI (https://developers.openai.com/codex), or ' +
|
|
62
|
-
'Gemini CLI (https://github.com/google-gemini/gemini-cli)
|
|
92
|
+
'Gemini CLI (https://github.com/google-gemini/gemini-cli), or ' +
|
|
93
|
+
'Kimi Code (https://www.kimi.com/code/docs/en/) before running specrails-core init.');
|
|
63
94
|
}
|
|
64
95
|
/**
|
|
65
96
|
* Directory / filename conventions the provider dictates.
|
|
66
97
|
* - Claude Code: .claude/ + CLAUDE.md
|
|
67
98
|
* - Codex: .codex/ + AGENTS.md
|
|
99
|
+
* - Gemini CLI: .gemini/ + GEMINI.md
|
|
100
|
+
* - Kimi Code: .kimi-code/ + AGENTS.md
|
|
68
101
|
*/
|
|
69
102
|
export function derivedPaths(provider) {
|
|
70
103
|
if (provider === 'codex') {
|
|
@@ -73,7 +106,13 @@ export function derivedPaths(provider) {
|
|
|
73
106
|
if (provider === 'gemini') {
|
|
74
107
|
return { providerDir: '.gemini', instructionsFile: 'GEMINI.md' };
|
|
75
108
|
}
|
|
76
|
-
|
|
109
|
+
if (provider === 'kimi') {
|
|
110
|
+
return { providerDir: '.kimi-code', instructionsFile: '.kimi-code/AGENTS.md' };
|
|
111
|
+
}
|
|
112
|
+
if (provider === 'claude') {
|
|
113
|
+
return { providerDir: '.claude', instructionsFile: 'CLAUDE.md' };
|
|
114
|
+
}
|
|
115
|
+
throw new ProviderError(`Unsupported provider: ${String(provider)}`);
|
|
77
116
|
}
|
|
78
117
|
/**
|
|
79
118
|
* Asserts that Claude Code is authenticated. Matches the three-path
|
|
@@ -96,6 +135,59 @@ export async function assertClaudeAuthenticated(options = {}) {
|
|
|
96
135
|
' Option 2 (OAuth): claude auth login',
|
|
97
136
|
].join('\n'));
|
|
98
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Bounded, non-billing Kimi authentication probe.
|
|
140
|
+
*
|
|
141
|
+
* Kimi 0.27 has no CLI command that proves managed OAuth readiness without
|
|
142
|
+
* starting a model request. We therefore recognise non-secret evidence (the
|
|
143
|
+
* managed credential file or a process-scoped model key), and honour explicit
|
|
144
|
+
* login failures emitted by `kimi doctor`. A successful `kimi doctor` only
|
|
145
|
+
* validates configuration, so it deliberately yields `unknown`.
|
|
146
|
+
*/
|
|
147
|
+
export async function probeKimiAuthentication(options = {}) {
|
|
148
|
+
const env = options.env ?? process.env;
|
|
149
|
+
if (typeof env.KIMI_MODEL_API_KEY === 'string' &&
|
|
150
|
+
env.KIMI_MODEL_API_KEY.length > 0 &&
|
|
151
|
+
typeof env.KIMI_MODEL_NAME === 'string' &&
|
|
152
|
+
env.KIMI_MODEL_NAME.length > 0) {
|
|
153
|
+
return 'authenticated';
|
|
154
|
+
}
|
|
155
|
+
const kimiHome = options.kimiCodeHome ??
|
|
156
|
+
(typeof env.KIMI_CODE_HOME === 'string' && env.KIMI_CODE_HOME.length > 0
|
|
157
|
+
? env.KIMI_CODE_HOME
|
|
158
|
+
: path.join(homedir(), '.kimi-code'));
|
|
159
|
+
if (pathExists(path.join(kimiHome, 'credentials', 'kimi-code.json'))) {
|
|
160
|
+
return 'authenticated';
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
const { stdout, stderr } = await runCommand('kimi', ['doctor'], {
|
|
164
|
+
inherit: false,
|
|
165
|
+
timeoutMs: 10_000,
|
|
166
|
+
env,
|
|
167
|
+
});
|
|
168
|
+
return textReportsMissingKimiLogin(`${stdout}\n${stderr}`) ? 'unauthenticated' : 'unknown';
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
const probe = error;
|
|
172
|
+
const text = `${probe.stdout ?? ''}\n${probe.stderr ?? ''}\n${probe.message ?? ''}`;
|
|
173
|
+
return textReportsMissingKimiLogin(text) ? 'unauthenticated' : 'unknown';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rejects only a conclusive login failure. `unknown` is allowed because setup
|
|
178
|
+
* must not spend quota merely to prove authentication.
|
|
179
|
+
*/
|
|
180
|
+
export async function assertKimiAuthenticated(options = {}) {
|
|
181
|
+
const status = await probeKimiAuthentication(options);
|
|
182
|
+
if (status !== 'unauthenticated' || options.skipPrereqs)
|
|
183
|
+
return status;
|
|
184
|
+
throw new ProviderError([
|
|
185
|
+
'Kimi Code is installed but login is required.',
|
|
186
|
+
'',
|
|
187
|
+
' Run: kimi login',
|
|
188
|
+
' Then retry specrails-core init.',
|
|
189
|
+
].join('\n'));
|
|
190
|
+
}
|
|
99
191
|
async function hasClaudeApiKeyConfigured() {
|
|
100
192
|
try {
|
|
101
193
|
const { stdout } = await runCommand('claude', ['config', 'list'], { inherit: false });
|
|
@@ -117,4 +209,22 @@ async function hasClaudeOauthJson() {
|
|
|
117
209
|
return false;
|
|
118
210
|
}
|
|
119
211
|
}
|
|
212
|
+
function textReportsMissingKimiLogin(raw) {
|
|
213
|
+
const text = raw.toLowerCase();
|
|
214
|
+
return (text.includes('login required') ||
|
|
215
|
+
text.includes('not logged in') ||
|
|
216
|
+
text.includes('not authenticated') ||
|
|
217
|
+
text.includes('please run kimi login') ||
|
|
218
|
+
text.includes('run `kimi login`'));
|
|
219
|
+
}
|
|
220
|
+
function compareVersionTriples(left, right) {
|
|
221
|
+
const a = left.split('.').map(Number);
|
|
222
|
+
const b = right.split('.').map(Number);
|
|
223
|
+
for (let i = 0; i < 3; i++) {
|
|
224
|
+
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
|
225
|
+
if (diff !== 0)
|
|
226
|
+
return diff;
|
|
227
|
+
}
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
120
230
|
//# sourceMappingURL=provider-detect.js.map
|