@peterxiaoyang/superspec 0.1.26 → 0.1.28
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 +2 -1
- package/dist/cli.js +150 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ superspec install
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
这条命令的意思是:把 SuperSpec 当前可用的工作流入口安装到项目里。
|
|
84
|
+
安装前会检查全局 `openspec` CLI;缺失或版本不一致时,会自动执行 `npm install -g @fission-ai/openspec@1.4.1`,确保后续工作流能调用 OpenSpec。
|
|
84
85
|
当前 beta 会安装 `.superspec/` 引擎目录、`.codex/skills/superspec-*` 阶段入口、`.codex/prompts/*.md` 角色 prompt、`.codex/agents/*.toml` 子智能体配置,补齐 `.codex/config.toml` 的多 agent 开关,并在项目根 `AGENTS.md` 中维护 SuperSpec 轻量门禁片段。`superspec init --scope project` 仍作为兼容别名可用。
|
|
85
86
|
|
|
86
87
|
Windows PowerShell 如果拦截 npm 的 `.ps1` 脚本,请改用:
|
|
@@ -246,7 +247,7 @@ superspec status
|
|
|
246
247
|
superspec update
|
|
247
248
|
```
|
|
248
249
|
|
|
249
|
-
这条命令会先检查 npm 上的 latest 版本;如果有新版,会自动执行全局升级并用新版 CLI
|
|
250
|
+
这条命令会先检查 npm 上的 latest 版本;如果有新版,会自动执行全局升级并用新版 CLI 重新同步项目入口。随后会把全局 OpenSpec CLI 拉齐到 `@fission-ai/openspec@1.4.1`。同步内容包括补齐 `.superspec/changes` 运行时目录,把当前 CLI 内置的 `.codex/skills/superspec-*`、`.codex/prompts/*.md`、`.codex/agents/*.toml` 同步到项目里,并更新 `AGENTS.md` 中 marker 包裹的 SuperSpec 轻量门禁片段。
|
|
250
251
|
|
|
251
252
|
## 进阶信息
|
|
252
253
|
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,8 @@ import { recordTestRun, recordTestRunContent } from "./task.js";
|
|
|
13
13
|
import { probeOpenSpec, openspecStatus, changeRoot } from "./openspec.js";
|
|
14
14
|
import { SUPERSPEC_VERSION } from "./version.js";
|
|
15
15
|
const PACKAGE_NAME = "@peterxiaoyang/superspec";
|
|
16
|
+
const OPENSPEC_PACKAGE_NAME = "@fission-ai/openspec";
|
|
17
|
+
const OPENSPEC_REQUIRED_VERSION = "1.4.1";
|
|
16
18
|
// ===== 参数解析 =====
|
|
17
19
|
function parseArgs(argv) {
|
|
18
20
|
const [command, subcommand, ...rest] = argv;
|
|
@@ -110,12 +112,37 @@ class SelfUpdateError extends Error {
|
|
|
110
112
|
this.latest = latest;
|
|
111
113
|
}
|
|
112
114
|
}
|
|
115
|
+
class OpenSpecDependencyError extends Error {
|
|
116
|
+
phase;
|
|
117
|
+
before;
|
|
118
|
+
constructor(phase, message, before = null) {
|
|
119
|
+
super(message);
|
|
120
|
+
this.name = "OpenSpecDependencyError";
|
|
121
|
+
this.phase = phase;
|
|
122
|
+
this.before = before;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
113
125
|
function isTestMode() {
|
|
114
126
|
return process.env.SUPERSPEC_TEST_MODE === "1" || process.env.NODE_ENV === "test";
|
|
115
127
|
}
|
|
116
128
|
function testEnv(name) {
|
|
117
129
|
return isTestMode() ? process.env[name] : undefined;
|
|
118
130
|
}
|
|
131
|
+
function runtimePlatform() {
|
|
132
|
+
return testEnv("SUPERSPEC_TEST_PLATFORM") ?? process.platform;
|
|
133
|
+
}
|
|
134
|
+
function windowsAwareCommand(name) {
|
|
135
|
+
return runtimePlatform() === "win32" ? `${name}.cmd` : name;
|
|
136
|
+
}
|
|
137
|
+
function npmCommand() {
|
|
138
|
+
return windowsAwareCommand("npm");
|
|
139
|
+
}
|
|
140
|
+
function superspecCommand() {
|
|
141
|
+
return windowsAwareCommand("superspec");
|
|
142
|
+
}
|
|
143
|
+
function openspecCommand() {
|
|
144
|
+
return windowsAwareCommand("openspec");
|
|
145
|
+
}
|
|
119
146
|
function selfUpdateError(phase, err, latest = null) {
|
|
120
147
|
return new SelfUpdateError(phase, commandErrorMessage(err), latest);
|
|
121
148
|
}
|
|
@@ -149,6 +176,112 @@ function selfUpdateFailurePayload(err) {
|
|
|
149
176
|
},
|
|
150
177
|
};
|
|
151
178
|
}
|
|
179
|
+
function openSpecDependencyFailurePayload(err) {
|
|
180
|
+
return {
|
|
181
|
+
ok: false,
|
|
182
|
+
message: err.message,
|
|
183
|
+
openspec: {
|
|
184
|
+
package: OPENSPEC_PACKAGE_NAME,
|
|
185
|
+
required_version: OPENSPEC_REQUIRED_VERSION,
|
|
186
|
+
before: err.before,
|
|
187
|
+
after: null,
|
|
188
|
+
action: "failed",
|
|
189
|
+
phase: err.phase,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function parseOpenSpecVersion(output) {
|
|
194
|
+
const match = output.trim().match(/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/);
|
|
195
|
+
return match?.[1] ?? null;
|
|
196
|
+
}
|
|
197
|
+
let openSpecVersionProbeCount = 0;
|
|
198
|
+
function mockedOpenSpecVersion() {
|
|
199
|
+
const sequence = testEnv("SUPERSPEC_TEST_OPENSPEC_VERSION_SEQUENCE");
|
|
200
|
+
if (sequence !== undefined) {
|
|
201
|
+
const parts = sequence.split("|");
|
|
202
|
+
const raw = parts[Math.min(openSpecVersionProbeCount, parts.length - 1)]?.trim() ?? "";
|
|
203
|
+
openSpecVersionProbeCount += 1;
|
|
204
|
+
if (/^(missing|error)$/i.test(raw))
|
|
205
|
+
throw new Error("openspec CLI 不可用");
|
|
206
|
+
return raw;
|
|
207
|
+
}
|
|
208
|
+
const explicit = testEnv("SUPERSPEC_TEST_OPENSPEC_VERSION");
|
|
209
|
+
if (explicit !== undefined) {
|
|
210
|
+
if (/^(missing|error)$/i.test(explicit.trim()))
|
|
211
|
+
throw new Error("openspec CLI 不可用");
|
|
212
|
+
return explicit;
|
|
213
|
+
}
|
|
214
|
+
return isTestMode() ? OPENSPEC_REQUIRED_VERSION : undefined;
|
|
215
|
+
}
|
|
216
|
+
function pathOpenSpecVersion() {
|
|
217
|
+
const mocked = mockedOpenSpecVersion();
|
|
218
|
+
if (mocked !== undefined) {
|
|
219
|
+
const parsed = parseOpenSpecVersion(mocked);
|
|
220
|
+
if (!parsed)
|
|
221
|
+
throw new Error(`无法解析 openspec --version 输出:${mocked}`);
|
|
222
|
+
return parsed;
|
|
223
|
+
}
|
|
224
|
+
const output = execFileSync(openspecCommand(), ["--version"], {
|
|
225
|
+
encoding: "utf8",
|
|
226
|
+
env: process.env,
|
|
227
|
+
});
|
|
228
|
+
const parsed = parseOpenSpecVersion(output);
|
|
229
|
+
if (!parsed)
|
|
230
|
+
throw new Error(`无法解析 openspec --version 输出:${output.trim()}`);
|
|
231
|
+
return parsed;
|
|
232
|
+
}
|
|
233
|
+
function installRequiredOpenSpecGlobal(before) {
|
|
234
|
+
const testError = testEnv("SUPERSPEC_TEST_OPENSPEC_INSTALL_ERROR");
|
|
235
|
+
if (testError)
|
|
236
|
+
throw new OpenSpecDependencyError("global_install", testError, before);
|
|
237
|
+
if (testEnv("SUPERSPEC_TEST_SKIP_OPENSPEC_INSTALL") === "1")
|
|
238
|
+
return;
|
|
239
|
+
try {
|
|
240
|
+
execFileSync(npmCommand(), ["install", "-g", `${OPENSPEC_PACKAGE_NAME}@${OPENSPEC_REQUIRED_VERSION}`], {
|
|
241
|
+
encoding: "utf8",
|
|
242
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
throw new OpenSpecDependencyError("global_install", `安装 OpenSpec ${OPENSPEC_REQUIRED_VERSION} 失败:${commandErrorMessage(err)}`, before);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function ensureOpenSpecCli() {
|
|
250
|
+
let before = null;
|
|
251
|
+
try {
|
|
252
|
+
before = pathOpenSpecVersion();
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
before = null;
|
|
256
|
+
}
|
|
257
|
+
if (before === OPENSPEC_REQUIRED_VERSION) {
|
|
258
|
+
return {
|
|
259
|
+
package: OPENSPEC_PACKAGE_NAME,
|
|
260
|
+
required_version: OPENSPEC_REQUIRED_VERSION,
|
|
261
|
+
before,
|
|
262
|
+
after: before,
|
|
263
|
+
action: "already_satisfied",
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
installRequiredOpenSpecGlobal(before);
|
|
267
|
+
let after;
|
|
268
|
+
try {
|
|
269
|
+
after = pathOpenSpecVersion();
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
throw new OpenSpecDependencyError("version_check", `安装后仍无法读取 openspec --version:${commandErrorMessage(err)}`, before);
|
|
273
|
+
}
|
|
274
|
+
if (after !== OPENSPEC_REQUIRED_VERSION) {
|
|
275
|
+
throw new OpenSpecDependencyError("version_mismatch", `全局 openspec 版本仍为 ${after},期望 ${OPENSPEC_REQUIRED_VERSION}。请检查 npm 全局 bin 是否在 PATH 前置。`, before);
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
package: OPENSPEC_PACKAGE_NAME,
|
|
279
|
+
required_version: OPENSPEC_REQUIRED_VERSION,
|
|
280
|
+
before,
|
|
281
|
+
after,
|
|
282
|
+
action: before === null ? "installed" : "updated",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
152
285
|
function npmLatestVersion() {
|
|
153
286
|
const testError = testEnv("SUPERSPEC_TEST_NPM_VIEW_ERROR");
|
|
154
287
|
if (testError)
|
|
@@ -157,7 +290,7 @@ function npmLatestVersion() {
|
|
|
157
290
|
if (testLatest)
|
|
158
291
|
return testLatest;
|
|
159
292
|
try {
|
|
160
|
-
const output = execFileSync(
|
|
293
|
+
const output = execFileSync(npmCommand(), ["view", PACKAGE_NAME, "version"], { encoding: "utf8" });
|
|
161
294
|
return output.trim().replace(/^"|"$/g, "");
|
|
162
295
|
}
|
|
163
296
|
catch (err) {
|
|
@@ -171,7 +304,7 @@ function installLatestGlobal() {
|
|
|
171
304
|
if (testEnv("SUPERSPEC_TEST_SKIP_GLOBAL_INSTALL") === "1")
|
|
172
305
|
return;
|
|
173
306
|
try {
|
|
174
|
-
execFileSync(
|
|
307
|
+
execFileSync(npmCommand(), ["install", "-g", `${PACKAGE_NAME}@latest`], {
|
|
175
308
|
encoding: "utf8",
|
|
176
309
|
stdio: ["ignore", "pipe", "pipe"],
|
|
177
310
|
});
|
|
@@ -198,7 +331,7 @@ function pathCliVersion() {
|
|
|
198
331
|
throw new Error(`无法解析 superspec --version 输出:${testOutput}`);
|
|
199
332
|
return parsed;
|
|
200
333
|
}
|
|
201
|
-
const output = execFileSync(
|
|
334
|
+
const output = execFileSync(superspecCommand(), ["--version"], {
|
|
202
335
|
encoding: "utf8",
|
|
203
336
|
env: process.env,
|
|
204
337
|
});
|
|
@@ -227,7 +360,7 @@ function rerunUpdatedCli(projectRoot, args) {
|
|
|
227
360
|
if (testOutput)
|
|
228
361
|
return testOutput;
|
|
229
362
|
try {
|
|
230
|
-
return execFileSync(
|
|
363
|
+
return execFileSync(superspecCommand(), args, {
|
|
231
364
|
cwd: projectRoot,
|
|
232
365
|
encoding: "utf8",
|
|
233
366
|
env: process.env,
|
|
@@ -401,13 +534,19 @@ jobs 子命令:
|
|
|
401
534
|
return rerun.exitCode;
|
|
402
535
|
}
|
|
403
536
|
}
|
|
404
|
-
|
|
537
|
+
const openspec = ensureOpenSpecCli();
|
|
538
|
+
console.log(JSON.stringify({
|
|
539
|
+
...installProject(projectRoot),
|
|
540
|
+
openspec,
|
|
541
|
+
}));
|
|
405
542
|
return 0;
|
|
406
543
|
}
|
|
407
544
|
catch (err) {
|
|
408
545
|
console.log(JSON.stringify(err instanceof SelfUpdateError
|
|
409
546
|
? selfUpdateFailurePayload(err)
|
|
410
|
-
:
|
|
547
|
+
: err instanceof OpenSpecDependencyError
|
|
548
|
+
? openSpecDependencyFailurePayload(err)
|
|
549
|
+
: { ok: false, message: commandErrorMessage(err) }));
|
|
411
550
|
return 1;
|
|
412
551
|
}
|
|
413
552
|
}
|
|
@@ -421,9 +560,11 @@ jobs 子命令:
|
|
|
421
560
|
return rerun.exitCode;
|
|
422
561
|
}
|
|
423
562
|
}
|
|
563
|
+
const openspec = ensureOpenSpecCli();
|
|
424
564
|
const result = installProject(projectRoot, { allowLegacyState: true });
|
|
425
565
|
console.log(JSON.stringify({
|
|
426
566
|
...result,
|
|
567
|
+
openspec,
|
|
427
568
|
message: `SuperSpec ${SUPERSPEC_VERSION} 已更新项目工作流`,
|
|
428
569
|
}));
|
|
429
570
|
return 0;
|
|
@@ -431,7 +572,9 @@ jobs 子命令:
|
|
|
431
572
|
catch (err) {
|
|
432
573
|
console.log(JSON.stringify(err instanceof SelfUpdateError
|
|
433
574
|
? selfUpdateFailurePayload(err)
|
|
434
|
-
:
|
|
575
|
+
: err instanceof OpenSpecDependencyError
|
|
576
|
+
? openSpecDependencyFailurePayload(err)
|
|
577
|
+
: { ok: false, message: commandErrorMessage(err) }));
|
|
435
578
|
return 1;
|
|
436
579
|
}
|
|
437
580
|
}
|