@peterxiaoyang/superspec 0.1.14 → 0.1.15-alpha
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/dist/cli.d.ts +2 -0
- package/dist/cli.js +302 -0
- package/dist/format.d.ts +29 -0
- package/dist/format.js +141 -0
- package/dist/next.d.ts +3 -0
- package/dist/next.js +161 -0
- package/dist/openspec.d.ts +9 -0
- package/dist/openspec.js +41 -0
- package/dist/record.d.ts +20 -0
- package/dist/record.js +211 -0
- package/dist/skill_loop.d.ts +23 -0
- package/dist/skill_loop.js +58 -0
- package/dist/store.d.ts +31 -0
- package/dist/store.js +213 -0
- package/dist/sync.d.ts +3 -0
- package/dist/sync.js +135 -0
- package/dist/task.d.ts +7 -0
- package/dist/task.js +44 -0
- package/dist/transition.d.ts +34 -0
- package/dist/transition.js +400 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.js +3 -0
- package/package.json +8 -53
- package/templates/workflow/skills/superspec-apply/SKILL.md +46 -118
- package/templates/workflow/skills/superspec-archive/SKILL.md +22 -47
- package/templates/workflow/skills/superspec-explore/SKILL.md +38 -69
- package/templates/workflow/skills/superspec-propose/SKILL.md +58 -60
- package/templates/workflow/skills/superspec-review/SKILL.md +36 -81
- package/adapters/codex/agents/architect.toml +0 -13
- package/adapters/codex/agents/code-reviewer.toml +0 -13
- package/adapters/codex/agents/critic.toml +0 -13
- package/adapters/codex/agents/executor.toml +0 -13
- package/adapters/codex/agents/explore.toml +0 -13
- package/adapters/codex/agents/test-engineer.toml +0 -13
- package/adapters/codex/agents/test-runner.toml +0 -13
- package/adapters/codex/agents/verifier.toml +0 -13
- package/adapters/codex/install-map.json +0 -116
- package/bin/launch.js +0 -37
- package/bin/superspec-check.js +0 -4
- package/bin/superspec-hook.js +0 -4
- package/bin/superspec-init.js +0 -4
- package/bin/superspec.js +0 -4
- package/dist/src/apply_worker_chain.d.ts +0 -60
- package/dist/src/apply_worker_chain.js +0 -1283
- package/dist/src/apply_worker_chain_lifecycle.d.ts +0 -21
- package/dist/src/apply_worker_chain_lifecycle.js +0 -409
- package/dist/src/archive.d.ts +0 -23
- package/dist/src/archive.js +0 -430
- package/dist/src/cli.d.ts +0 -1
- package/dist/src/cli.js +0 -33
- package/dist/src/cli_args.d.ts +0 -32
- package/dist/src/cli_args.js +0 -454
- package/dist/src/core.d.ts +0 -20
- package/dist/src/core.js +0 -394
- package/dist/src/disclosure.d.ts +0 -35
- package/dist/src/disclosure.js +0 -671
- package/dist/src/doctor.d.ts +0 -44
- package/dist/src/doctor.js +0 -230
- package/dist/src/evidence.d.ts +0 -29
- package/dist/src/evidence.js +0 -1107
- package/dist/src/gates.d.ts +0 -19
- package/dist/src/gates.js +0 -1631
- package/dist/src/git.d.ts +0 -8
- package/dist/src/git.js +0 -112
- package/dist/src/hooks/adapter.d.ts +0 -5
- package/dist/src/hooks/adapter.js +0 -347
- package/dist/src/hooks/guard_api.d.ts +0 -12
- package/dist/src/hooks/guard_api.js +0 -797
- package/dist/src/hooks/health.d.ts +0 -4
- package/dist/src/hooks/health.js +0 -86
- package/dist/src/hooks/policy_event.d.ts +0 -41
- package/dist/src/hooks/policy_event.js +0 -2634
- package/dist/src/hooks/types.d.ts +0 -72
- package/dist/src/hooks/types.js +0 -1
- package/dist/src/hooks/validation.d.ts +0 -3
- package/dist/src/hooks/validation.js +0 -70
- package/dist/src/i18n.d.ts +0 -21
- package/dist/src/i18n.js +0 -720
- package/dist/src/init_cli.d.ts +0 -28
- package/dist/src/init_cli.js +0 -404
- package/dist/src/install_engine.d.ts +0 -71
- package/dist/src/install_engine.js +0 -538
- package/dist/src/invariants.d.ts +0 -16
- package/dist/src/invariants.js +0 -363
- package/dist/src/openspec.d.ts +0 -37
- package/dist/src/openspec.js +0 -274
- package/dist/src/packet_render.d.ts +0 -5
- package/dist/src/packet_render.js +0 -1767
- package/dist/src/packet_schema.d.ts +0 -59
- package/dist/src/packet_schema.js +0 -1
- package/dist/src/paths.d.ts +0 -22
- package/dist/src/paths.js +0 -203
- package/dist/src/project_init.d.ts +0 -33
- package/dist/src/project_init.js +0 -191
- package/dist/src/self_update.d.ts +0 -14
- package/dist/src/self_update.js +0 -56
- package/dist/src/state.d.ts +0 -37
- package/dist/src/state.js +0 -464
- package/dist/src/tasks.d.ts +0 -38
- package/dist/src/tasks.js +0 -373
- package/dist/src/util.d.ts +0 -163
- package/dist/src/util.js +0 -792
- package/dist/superspec.d.ts +0 -6
- package/dist/superspec.js +0 -97
- package/dist/superspec_guard.d.ts +0 -4
- package/dist/superspec_guard.js +0 -19
- package/dist/superspec_hook.d.ts +0 -4
- package/dist/superspec_hook.js +0 -35
- package/dist/superspec_init.d.ts +0 -2
- package/dist/superspec_init.js +0 -17
- package/schemas/hook-event.schema.json +0 -27
- package/schemas/install-manifest.schema.json +0 -97
- package/templates/hooks/codex-hooks.json +0 -35
- package/templates/sidecar/archive-preservation.json +0 -11
- package/templates/sidecar/business-invariants.md +0 -38
- package/templates/sidecar/config.yaml +0 -13
- package/templates/sidecar/discovery.md +0 -33
- package/templates/sidecar/test-contract.md +0 -27
- package/templates/workflow/prompts/architect.md +0 -27
- package/templates/workflow/prompts/code-reviewer.md +0 -34
- package/templates/workflow/prompts/critic.md +0 -30
- package/templates/workflow/prompts/executor.md +0 -32
- package/templates/workflow/prompts/explore.md +0 -27
- package/templates/workflow/prompts/test-engineer.md +0 -28
- package/templates/workflow/prompts/test-runner.md +0 -35
- package/templates/workflow/prompts/verifier.md +0 -35
package/dist/src/init_cli.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { runCommand, type DecisionOutputFormat, type OpenspecCliProbe } from "./core.ts";
|
|
2
|
-
import { type InstallScope } from "./install_engine.ts";
|
|
3
|
-
type InitArgs = {
|
|
4
|
-
path: string;
|
|
5
|
-
codexHome: string;
|
|
6
|
-
mode: "install" | "update" | "uninstall";
|
|
7
|
-
scope: InstallScope | null;
|
|
8
|
-
format: DecisionOutputFormat;
|
|
9
|
-
dryRun: boolean;
|
|
10
|
-
force: boolean;
|
|
11
|
-
};
|
|
12
|
-
export declare function maybe_install_missing_openspec(opts: {
|
|
13
|
-
cwd: string;
|
|
14
|
-
scope: InstallScope;
|
|
15
|
-
mode: InitArgs["mode"];
|
|
16
|
-
interactive?: boolean;
|
|
17
|
-
commandExistsFn?: (cmd: string, meta?: {
|
|
18
|
-
cwd?: string;
|
|
19
|
-
}) => boolean;
|
|
20
|
-
probeOpenspecFn?: (meta?: {
|
|
21
|
-
cwd?: string;
|
|
22
|
-
}) => OpenspecCliProbe;
|
|
23
|
-
run?: typeof runCommand;
|
|
24
|
-
writeStderr?: (text: string) => void;
|
|
25
|
-
}): "not-needed" | "installed" | "skipped" | "failed";
|
|
26
|
-
export declare function main_init(argv?: string[]): number;
|
|
27
|
-
export declare function main_init_async(argv?: string[]): Promise<number>;
|
|
28
|
-
export {};
|
package/dist/src/init_cli.js
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { block, commandExists, GuardError, printDecision, reason, runCommand, openspec_cli_probe, parseDecisionOutputFormat, REQUIRED_OPENSPEC_MIN_VERSION, } from "./core.js";
|
|
2
|
-
import { ensure_openspec_chinese_context, forced_openspec_install_plan, project_init, recommended_openspec_install_plan } from "./project_init.js";
|
|
3
|
-
import { install_workflow, uninstall_workflow, update_workflow } from "./install_engine.js";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { resolve } from "node:path";
|
|
6
|
-
import { createInterface } from "node:readline/promises";
|
|
7
|
-
import { action_detail_zh, action_status_zh, reason_message_zh, system_failure_zh } from "./i18n.js";
|
|
8
|
-
function usage() {
|
|
9
|
-
return "usage: superspec init [-h] [--scope {project,user}] [--path PATH] [--codex-home PATH] [--format {json,agent,user}] [--create] [--update] [--uninstall] [--dry-run] [--force]\n";
|
|
10
|
-
}
|
|
11
|
-
function help() {
|
|
12
|
-
return `${usage()}\n可选参数:\n -h, --help 显示帮助并退出\n --scope {project,user} 安装到当前项目的 .codex 目录,或安装到用户级 Codex 目录(默认:project)\n --project 等价于 --scope project\n --user 等价于 --scope user\n --global 兼容别名,等价于 --user\n --path PATH --scope project 时使用的项目根目录(默认:当前目录)\n --codex-home PATH --scope user 时使用的 Codex 用户目录(默认:$CODEX_HOME 或 ~/.codex)\n --format {json,agent,user} 输出格式(默认:user);json 用于诊断或自动化,agent 用于工作流消费\n --user-facing 等价于 --format user\n --create 兼容参数;init 默认就会创建缺失内容\n --update 按 manifest 更新 SuperSpec 管理的文件;用户改动文件保留,新的版本写入 *.new\n --uninstall 按 manifest 卸载 SuperSpec 管理的文件;.superspec 数据与既有/用户改动文件会保留\n --dry-run 配合 --uninstall 时只预览将删除的文件,不实际修改\n --force 安装时覆盖已有且内容不同的文件,并保留 *.bak 备份\n`;
|
|
13
|
-
}
|
|
14
|
-
function requestedOutputFormat(argv) {
|
|
15
|
-
if (argv.includes("--user-facing"))
|
|
16
|
-
return "user";
|
|
17
|
-
let selected = null;
|
|
18
|
-
for (let idx = 0; idx < argv.length; idx += 1) {
|
|
19
|
-
if (argv[idx] !== "--format")
|
|
20
|
-
continue;
|
|
21
|
-
const value = argv[idx + 1];
|
|
22
|
-
if (value === undefined || value.startsWith("--"))
|
|
23
|
-
return null;
|
|
24
|
-
selected = value;
|
|
25
|
-
}
|
|
26
|
-
if (selected === null)
|
|
27
|
-
return null;
|
|
28
|
-
try {
|
|
29
|
-
return parseDecisionOutputFormat(selected);
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function fallbackOutputFormat(argv) {
|
|
36
|
-
return requestedOutputFormat(argv) ?? "user";
|
|
37
|
-
}
|
|
38
|
-
function parse_init_argv(argv) {
|
|
39
|
-
const getValues = (flag) => {
|
|
40
|
-
const values = [];
|
|
41
|
-
for (let idx = 0; idx < argv.length; idx += 1) {
|
|
42
|
-
if (argv[idx] !== flag)
|
|
43
|
-
continue;
|
|
44
|
-
const value = argv[idx + 1];
|
|
45
|
-
if (value === undefined || value.startsWith("--"))
|
|
46
|
-
throw new GuardError(`${flag} 缺少取值`);
|
|
47
|
-
values.push(value);
|
|
48
|
-
}
|
|
49
|
-
return values;
|
|
50
|
-
};
|
|
51
|
-
const getValue = (flag) => getValues(flag)[0];
|
|
52
|
-
const update = argv.includes("--update");
|
|
53
|
-
const uninstall = argv.includes("--uninstall");
|
|
54
|
-
if (update && uninstall)
|
|
55
|
-
throw new GuardError("--update 与 --uninstall 不能同时使用");
|
|
56
|
-
const scopeFlag = getValue("--scope");
|
|
57
|
-
if (scopeFlag !== undefined && !["project", "user", "global"].includes(scopeFlag))
|
|
58
|
-
throw new GuardError("--scope 只能是 project 或 user");
|
|
59
|
-
const userShortcut = argv.includes("--user") || argv.includes("--global");
|
|
60
|
-
if (userShortcut && argv.includes("--project"))
|
|
61
|
-
throw new GuardError("--user/--global 与 --project 不能同时使用");
|
|
62
|
-
let scope = scopeFlag === "global" ? "user" : scopeFlag ?? null;
|
|
63
|
-
if (userShortcut)
|
|
64
|
-
scope = "user";
|
|
65
|
-
if (argv.includes("--project"))
|
|
66
|
-
scope = "project";
|
|
67
|
-
const formatValues = getValues("--format");
|
|
68
|
-
for (const value of formatValues)
|
|
69
|
-
parseDecisionOutputFormat(value);
|
|
70
|
-
const selectedFormat = argv.includes("--user-facing")
|
|
71
|
-
? "user"
|
|
72
|
-
: (formatValues.length > 0 ? formatValues[formatValues.length - 1] : "user");
|
|
73
|
-
const format = parseDecisionOutputFormat(selectedFormat);
|
|
74
|
-
return {
|
|
75
|
-
path: resolve(getValue("--path") ?? process.cwd()),
|
|
76
|
-
codexHome: resolve(getValue("--codex-home") ?? process.env.CODEX_HOME ?? joinHomeCodex()),
|
|
77
|
-
mode: uninstall ? "uninstall" : update ? "update" : "install",
|
|
78
|
-
scope,
|
|
79
|
-
format,
|
|
80
|
-
dryRun: argv.includes("--dry-run"),
|
|
81
|
-
force: argv.includes("--force"),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function joinHomeCodex() {
|
|
85
|
-
return `${homedir()}/.codex`;
|
|
86
|
-
}
|
|
87
|
-
function scopeLabel(scope) {
|
|
88
|
-
return scope === "user" ? "用户级" : "项目级";
|
|
89
|
-
}
|
|
90
|
-
function modeLabel(mode, dryRun) {
|
|
91
|
-
if (mode === "update")
|
|
92
|
-
return "更新";
|
|
93
|
-
if (mode === "uninstall")
|
|
94
|
-
return dryRun ? "卸载预览" : "卸载";
|
|
95
|
-
return "初始化";
|
|
96
|
-
}
|
|
97
|
-
function actionSummary(actions) {
|
|
98
|
-
if (!Array.isArray(actions) || actions.length === 0)
|
|
99
|
-
return "";
|
|
100
|
-
const counts = new Map();
|
|
101
|
-
for (const item of actions) {
|
|
102
|
-
if (!item || typeof item !== "object")
|
|
103
|
-
continue;
|
|
104
|
-
const status = String(item.status ?? "");
|
|
105
|
-
if (!status)
|
|
106
|
-
continue;
|
|
107
|
-
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
108
|
-
}
|
|
109
|
-
const changedOrder = ["created", "updated", "removed", "would_remove", "skipped", "failed"];
|
|
110
|
-
const parts = changedOrder
|
|
111
|
-
.map((status) => [status, counts.get(status) ?? 0])
|
|
112
|
-
.filter(([, count]) => count > 0)
|
|
113
|
-
.map(([status, count]) => `${action_status_zh(status)} ${count} 项`);
|
|
114
|
-
if (parts.length > 0)
|
|
115
|
-
return parts.join(",");
|
|
116
|
-
const okCount = counts.get("ok") ?? 0;
|
|
117
|
-
return okCount > 0 ? `已检查 ${okCount} 项,无需改动` : "";
|
|
118
|
-
}
|
|
119
|
-
function actionTarget(action) {
|
|
120
|
-
const match = action.match(/^(?:install|update|uninstall(?: rmdir)?)\s+(.+)$/u);
|
|
121
|
-
return match?.[1] ?? "";
|
|
122
|
-
}
|
|
123
|
-
function skippedActionNotes(actions) {
|
|
124
|
-
if (!Array.isArray(actions))
|
|
125
|
-
return [];
|
|
126
|
-
const groups = new Map();
|
|
127
|
-
for (const item of actions) {
|
|
128
|
-
if (!item || typeof item !== "object")
|
|
129
|
-
continue;
|
|
130
|
-
const action = String(item.action ?? "");
|
|
131
|
-
const status = String(item.status ?? "");
|
|
132
|
-
if (status !== "skipped")
|
|
133
|
-
continue;
|
|
134
|
-
const detail = String(item.detail ?? "");
|
|
135
|
-
const note = detail ? action_detail_zh(detail) : "有文件被保留未改动;需要完整列表时添加 --format json。";
|
|
136
|
-
const target = actionTarget(action);
|
|
137
|
-
const group = groups.get(note) ?? { count: 0, targets: [] };
|
|
138
|
-
group.count += 1;
|
|
139
|
-
if (target && group.targets.length < 3)
|
|
140
|
-
group.targets.push(target);
|
|
141
|
-
groups.set(note, group);
|
|
142
|
-
}
|
|
143
|
-
return [...groups.entries()].map(([note, group]) => {
|
|
144
|
-
const hiddenCount = group.count - group.targets.length;
|
|
145
|
-
if (group.targets.length === 0)
|
|
146
|
-
return `${note}(${group.count} 项)`;
|
|
147
|
-
const suffix = hiddenCount > 0 ? ` 等 ${group.count} 项` : "";
|
|
148
|
-
return `${note}(${group.targets.join(",")}${suffix})`;
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
function knownInitProblemMessageZh(message) {
|
|
152
|
-
if (/install manifest missing; run superspec init before --update/iu.test(message)) {
|
|
153
|
-
return "缺少安装清单;请先运行 superspec init,再执行 update。";
|
|
154
|
-
}
|
|
155
|
-
if (/install manifest missing; nothing to uninstall/iu.test(message)) {
|
|
156
|
-
return "缺少安装清单;当前没有可卸载的 SuperSpec 管理文件。";
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
function reasonTextZh(item) {
|
|
161
|
-
const code = String(item.code ?? "");
|
|
162
|
-
const message = String(item.message ?? "");
|
|
163
|
-
const refs = Array.isArray(item.refs) ? item.refs.map((ref) => String(ref)) : [];
|
|
164
|
-
return knownInitProblemMessageZh(message) ?? reason_message_zh(code, message, refs);
|
|
165
|
-
}
|
|
166
|
-
function renderInitUserDecision(decision, opts = {}) {
|
|
167
|
-
const lines = [];
|
|
168
|
-
const op = modeLabel(opts.mode, opts.dryRun);
|
|
169
|
-
lines.push(`SuperSpec ${op}${decision.allowed ? "完成" : "未完成"}。`);
|
|
170
|
-
lines.push(`范围:${scopeLabel(opts.scope)}。`);
|
|
171
|
-
const root = opts.targetRoot ?? String(decision.install_root ?? decision.project_root ?? "");
|
|
172
|
-
if (root)
|
|
173
|
-
lines.push(`位置:${root}`);
|
|
174
|
-
const summary = actionSummary(decision.actions);
|
|
175
|
-
if (decision.allowed && summary)
|
|
176
|
-
lines.push(`处理:${summary}。`);
|
|
177
|
-
const skippedNotes = skippedActionNotes(decision.actions);
|
|
178
|
-
if (decision.allowed && skippedNotes.length > 0) {
|
|
179
|
-
lines.push("跳过项:");
|
|
180
|
-
for (const note of skippedNotes)
|
|
181
|
-
lines.push(`- ${note}`);
|
|
182
|
-
}
|
|
183
|
-
const reasons = Array.isArray(decision.block_reasons) ? decision.block_reasons : [];
|
|
184
|
-
if (!decision.allowed && reasons.length > 0) {
|
|
185
|
-
lines.push("原因:");
|
|
186
|
-
for (const item of reasons)
|
|
187
|
-
lines.push(`- ${reasonTextZh(item)}`);
|
|
188
|
-
lines.push("修正后重新运行同一命令;需要完整诊断时添加 --format json。");
|
|
189
|
-
}
|
|
190
|
-
return `${lines.join("\n")}\n`;
|
|
191
|
-
}
|
|
192
|
-
function printInitDecision(decision, opts) {
|
|
193
|
-
if (opts.format === "user") {
|
|
194
|
-
process.stdout.write(renderInitUserDecision(decision, opts));
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
printDecision(decision, { command: opts.command ?? "init", format: opts.format });
|
|
198
|
-
}
|
|
199
|
-
function commandFailure(proc) {
|
|
200
|
-
const output = (proc.error?.message ?? (proc.stderr || proc.stdout)).trim();
|
|
201
|
-
if (output)
|
|
202
|
-
return system_failure_zh(output, `安装命令执行失败(退出状态码 ${proc.status ?? "未知"})。`);
|
|
203
|
-
if (proc.status !== null)
|
|
204
|
-
return `安装命令执行失败(退出状态码 ${proc.status})。`;
|
|
205
|
-
return "安装命令执行失败,请查看终端日志后重试。";
|
|
206
|
-
}
|
|
207
|
-
function shouldRetryOpenSpecInstallWithForce(proc) {
|
|
208
|
-
const output = `${proc.error?.message ?? ""}\n${proc.stderr}\n${proc.stdout}`;
|
|
209
|
-
const binConflict = /\bEEXIST\b|already exists|file exists|Refusing to delete|will not overwrite|would overwrite/iu.test(output);
|
|
210
|
-
const openspecBin = /\bopenspec(?:\.(?:cmd|ps1))?\b/iu.test(output);
|
|
211
|
-
return binConflict && openspecBin;
|
|
212
|
-
}
|
|
213
|
-
function engineDecision(gate, projectRoot, result, nextActions) {
|
|
214
|
-
if (result.problems.length > 0) {
|
|
215
|
-
const decision = block("project", gate, result.problems.map((item) => reason(`${gate}_failed`, item)));
|
|
216
|
-
return { ...decision, project_root: projectRoot, actions: result.actions, next_allowed_actions: [`fix ${gate}_failed reasons, then rerun`] };
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
allowed: true,
|
|
220
|
-
decision: "allow",
|
|
221
|
-
change_id: null,
|
|
222
|
-
gate,
|
|
223
|
-
project_root: projectRoot,
|
|
224
|
-
actions: result.actions,
|
|
225
|
-
block_reasons: [],
|
|
226
|
-
next_allowed_actions: nextActions,
|
|
227
|
-
trust_warnings: [".superspec runtime data is never touched by install/update/uninstall; only manifest-managed files are"],
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
async function promptInstallScope() {
|
|
231
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
232
|
-
try {
|
|
233
|
-
for (;;) {
|
|
234
|
-
const answer = (await rl.question("请选择安装范围:project 还是 user?[project] ")).trim().toLowerCase();
|
|
235
|
-
if (answer === "" || answer === "project" || answer === "p" || answer === "1" || answer === "项目")
|
|
236
|
-
return "project";
|
|
237
|
-
if (answer === "user" || answer === "u" || answer === "2" || answer === "global" || answer === "g" || answer === "用户")
|
|
238
|
-
return "user";
|
|
239
|
-
process.stderr.write("请输入 project 或 user。\n");
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
finally {
|
|
243
|
-
rl.close();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function canPrompt() {
|
|
247
|
-
return Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
248
|
-
}
|
|
249
|
-
export function maybe_install_missing_openspec(opts) {
|
|
250
|
-
const commandExistsFn = opts.commandExistsFn ?? ((cmd, meta) => commandExists(cmd, { cwd: meta?.cwd }));
|
|
251
|
-
if (opts.mode !== "install")
|
|
252
|
-
return "not-needed";
|
|
253
|
-
const run = opts.run ?? runCommand;
|
|
254
|
-
const probeOpenspecFn = opts.probeOpenspecFn ?? ((meta) => openspec_cli_probe({ cwd: meta?.cwd, commandExistsFn, run }));
|
|
255
|
-
const before = probeOpenspecFn({ cwd: opts.cwd });
|
|
256
|
-
if (before.ok)
|
|
257
|
-
return "not-needed";
|
|
258
|
-
const writeStderr = opts.writeStderr ?? ((text) => {
|
|
259
|
-
process.stderr.write(text);
|
|
260
|
-
});
|
|
261
|
-
const plan = recommended_openspec_install_plan({ cwd: opts.cwd, commandExistsFn });
|
|
262
|
-
if (plan === null) {
|
|
263
|
-
writeStderr(`${before.message}。SuperSpec 需要 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION},但未找到 npm、pnpm、yarn 或 bun,无法自动安装。\n`);
|
|
264
|
-
return "skipped";
|
|
265
|
-
}
|
|
266
|
-
writeStderr(`${before.message}。SuperSpec 需要 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION},将自动安装或升级。\n`);
|
|
267
|
-
writeStderr(`正在安装 OpenSpec CLI:${plan.rendered}\n`);
|
|
268
|
-
let proc = run(plan.cmd, plan.args, { cwd: opts.cwd, timeout: 300_000 });
|
|
269
|
-
if ((proc.error || proc.status !== 0) && shouldRetryOpenSpecInstallWithForce(proc)) {
|
|
270
|
-
const forcedPlan = forced_openspec_install_plan(plan);
|
|
271
|
-
writeStderr(`OpenSpec CLI 安装遇到全局 bin 冲突,正在覆盖重试:${forcedPlan.rendered}\n`);
|
|
272
|
-
proc = run(forcedPlan.cmd, forcedPlan.args, { cwd: opts.cwd, timeout: 300_000 });
|
|
273
|
-
}
|
|
274
|
-
if (proc.error || proc.status !== 0) {
|
|
275
|
-
writeStderr(`自动安装 OpenSpec CLI 失败:${commandFailure(proc)}\n`);
|
|
276
|
-
return "failed";
|
|
277
|
-
}
|
|
278
|
-
const after = probeOpenspecFn({ cwd: opts.cwd });
|
|
279
|
-
if (!after.ok) {
|
|
280
|
-
writeStderr(`安装命令已完成,但当前 PATH 里的 openspec 仍不可用:${after.message}。请重新打开终端或确认全局 bin 已在 PATH 中,然后重新运行 superspec init;Windows PowerShell 请运行 superspec.cmd init。\n`);
|
|
281
|
-
return "failed";
|
|
282
|
-
}
|
|
283
|
-
writeStderr("OpenSpec CLI 安装或升级完成,继续执行 superspec init。\n");
|
|
284
|
-
return "installed";
|
|
285
|
-
}
|
|
286
|
-
function openspecPreflightBlocked(args, scope, installResult) {
|
|
287
|
-
const targetRoot = scope === "user" ? args.codexHome : args.path;
|
|
288
|
-
const code = installResult === "skipped" ? "openspec_auto_install_unavailable" : "openspec_auto_install_failed";
|
|
289
|
-
const message = installResult === "skipped"
|
|
290
|
-
? `OpenSpec CLI 不满足 SuperSpec 要求,且未找到可用包管理器自动安装 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION}。`
|
|
291
|
-
: `OpenSpec CLI 自动安装或升级 @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION} 未完成。`;
|
|
292
|
-
const decision = block("project", "openspec_preflight", [reason(code, message)], {
|
|
293
|
-
next_actions: [`install or upgrade @fission-ai/openspec >= ${REQUIRED_OPENSPEC_MIN_VERSION}, then rerun \`superspec init --scope ${scope}\``],
|
|
294
|
-
});
|
|
295
|
-
printInitDecision({
|
|
296
|
-
...decision,
|
|
297
|
-
project_root: args.path,
|
|
298
|
-
install_scope: scope,
|
|
299
|
-
install_root: targetRoot,
|
|
300
|
-
}, {
|
|
301
|
-
command: "init",
|
|
302
|
-
format: args.format,
|
|
303
|
-
mode: args.mode,
|
|
304
|
-
scope,
|
|
305
|
-
targetRoot,
|
|
306
|
-
dryRun: args.dryRun,
|
|
307
|
-
});
|
|
308
|
-
return 1;
|
|
309
|
-
}
|
|
310
|
-
function run_init(args, scope) {
|
|
311
|
-
const targetRoot = scope === "user" ? args.codexHome : args.path;
|
|
312
|
-
const gatePrefix = scope === "user" ? "user" : "project";
|
|
313
|
-
let summary;
|
|
314
|
-
if (args.mode === "uninstall") {
|
|
315
|
-
summary = engineDecision(`${gatePrefix}_uninstall`, targetRoot, uninstall_workflow(targetRoot, { dryRun: args.dryRun, scope }), [
|
|
316
|
-
`managed SuperSpec surfaces removed; run superspec init --scope ${scope} to reinstall`,
|
|
317
|
-
]);
|
|
318
|
-
}
|
|
319
|
-
else if (args.mode === "update") {
|
|
320
|
-
summary = engineDecision(`${gatePrefix}_update`, targetRoot, update_workflow(targetRoot, { scope }), [
|
|
321
|
-
scope === "project" ? "review *.new files for user-modified surfaces, then rerun superspec check check-init" : "review *.new files for user-modified user-level surfaces",
|
|
322
|
-
]);
|
|
323
|
-
if (scope === "project" && summary.allowed) {
|
|
324
|
-
const act = ensure_openspec_chinese_context(targetRoot);
|
|
325
|
-
if (Array.isArray(summary.actions))
|
|
326
|
-
summary.actions.push(act);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
else if (scope === "user") {
|
|
330
|
-
summary = engineDecision("user_install", targetRoot, install_workflow(targetRoot, { force: args.force, scope: "user" }), [
|
|
331
|
-
"user-level SuperSpec Codex surfaces installed; use superspec init --scope project inside a repo when project-local surfaces are needed",
|
|
332
|
-
]);
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
summary = project_init(args.path, { force: args.force });
|
|
336
|
-
}
|
|
337
|
-
summary.install_scope = scope;
|
|
338
|
-
summary.install_root = targetRoot;
|
|
339
|
-
printInitDecision(summary, {
|
|
340
|
-
command: "init",
|
|
341
|
-
format: args.format,
|
|
342
|
-
mode: args.mode,
|
|
343
|
-
scope,
|
|
344
|
-
targetRoot,
|
|
345
|
-
dryRun: args.dryRun,
|
|
346
|
-
});
|
|
347
|
-
return summary.allowed ? 0 : 1;
|
|
348
|
-
}
|
|
349
|
-
export function main_init(argv = process.argv.slice(2)) {
|
|
350
|
-
let args = null;
|
|
351
|
-
try {
|
|
352
|
-
if (argv.includes("-h") || argv.includes("--help")) {
|
|
353
|
-
process.stdout.write(help());
|
|
354
|
-
return 0;
|
|
355
|
-
}
|
|
356
|
-
args = parse_init_argv(argv);
|
|
357
|
-
const scope = args.scope ?? "project";
|
|
358
|
-
const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
|
|
359
|
-
if (openspecInstall === "failed" || openspecInstall === "skipped")
|
|
360
|
-
return openspecPreflightBlocked(args, scope, openspecInstall);
|
|
361
|
-
return run_init(args, scope);
|
|
362
|
-
}
|
|
363
|
-
catch (err) {
|
|
364
|
-
const change = "project";
|
|
365
|
-
const errReason = err instanceof GuardError ? reason("guard_error", err.message) : reason("guard_internal_error", `${err.name}: ${err.message}`);
|
|
366
|
-
printInitDecision(block(change, "guard_error", [errReason]), {
|
|
367
|
-
command: "init",
|
|
368
|
-
format: args?.format ?? fallbackOutputFormat(argv),
|
|
369
|
-
mode: args?.mode,
|
|
370
|
-
scope: args?.scope ?? "project",
|
|
371
|
-
targetRoot: args?.path,
|
|
372
|
-
dryRun: args?.dryRun,
|
|
373
|
-
});
|
|
374
|
-
return 2;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
export async function main_init_async(argv = process.argv.slice(2)) {
|
|
378
|
-
let args = null;
|
|
379
|
-
try {
|
|
380
|
-
if (argv.includes("-h") || argv.includes("--help")) {
|
|
381
|
-
process.stdout.write(help());
|
|
382
|
-
return 0;
|
|
383
|
-
}
|
|
384
|
-
args = parse_init_argv(argv);
|
|
385
|
-
const scope = args.scope ?? (canPrompt() ? await promptInstallScope() : "project");
|
|
386
|
-
const openspecInstall = maybe_install_missing_openspec({ cwd: args.path, scope, mode: args.mode });
|
|
387
|
-
if (openspecInstall === "failed" || openspecInstall === "skipped")
|
|
388
|
-
return openspecPreflightBlocked(args, scope, openspecInstall);
|
|
389
|
-
return run_init(args, scope);
|
|
390
|
-
}
|
|
391
|
-
catch (err) {
|
|
392
|
-
const change = "project";
|
|
393
|
-
const errReason = err instanceof GuardError ? reason("guard_error", err.message) : reason("guard_internal_error", `${err.name}: ${err.message}`);
|
|
394
|
-
printInitDecision(block(change, "guard_error", [errReason]), {
|
|
395
|
-
command: "init",
|
|
396
|
-
format: args?.format ?? fallbackOutputFormat(argv),
|
|
397
|
-
mode: args?.mode,
|
|
398
|
-
scope: args?.scope ?? "project",
|
|
399
|
-
targetRoot: args?.path,
|
|
400
|
-
dryRun: args?.dryRun,
|
|
401
|
-
});
|
|
402
|
-
return 2;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { type JsonMap } from "./util.ts";
|
|
2
|
-
export declare const INSTALL_MAP_REL: string;
|
|
3
|
-
export declare const PACKAGE_ROOT: string;
|
|
4
|
-
export declare const PROJECT_INSTALL_MANIFEST_REL: string;
|
|
5
|
-
export declare const USER_INSTALL_MANIFEST_REL: string;
|
|
6
|
-
export declare const INSTALL_MANIFEST_REL: string;
|
|
7
|
-
export type InstallMapping = {
|
|
8
|
-
kind: string;
|
|
9
|
-
source: string;
|
|
10
|
-
target: string;
|
|
11
|
-
};
|
|
12
|
-
export type InstallScope = "project" | "user";
|
|
13
|
-
export type ManifestFileEntry = {
|
|
14
|
-
path: string;
|
|
15
|
-
sha256: string;
|
|
16
|
-
managed: boolean;
|
|
17
|
-
preexisting: boolean;
|
|
18
|
-
};
|
|
19
|
-
export type ManifestConfigPatchEntry = {
|
|
20
|
-
path: string;
|
|
21
|
-
retainedOnUninstall: boolean;
|
|
22
|
-
managed: boolean;
|
|
23
|
-
};
|
|
24
|
-
export type EngineAction = {
|
|
25
|
-
action: string;
|
|
26
|
-
status: "ok" | "created" | "updated" | "skipped" | "removed" | "would_remove" | "failed";
|
|
27
|
-
refs?: string[];
|
|
28
|
-
detail?: string;
|
|
29
|
-
};
|
|
30
|
-
export type EngineResult = {
|
|
31
|
-
actions: EngineAction[];
|
|
32
|
-
problems: string[];
|
|
33
|
-
manifest: JsonMap | null;
|
|
34
|
-
};
|
|
35
|
-
export declare const CODEX_NATIVE_AGENT_MAX_THREADS = 12;
|
|
36
|
-
export declare const CODEX_NATIVE_AGENT_MAX_DEPTH = 1;
|
|
37
|
-
export declare function load_install_map(packageRoot?: string): {
|
|
38
|
-
mappings: InstallMapping[];
|
|
39
|
-
problems: string[];
|
|
40
|
-
};
|
|
41
|
-
export declare function manifest_shape_problems(manifest: any): string[];
|
|
42
|
-
export declare function read_install_manifest(repoRoot: string, opts?: {
|
|
43
|
-
scope?: InstallScope;
|
|
44
|
-
}): {
|
|
45
|
-
manifest: JsonMap | null;
|
|
46
|
-
problems: string[];
|
|
47
|
-
};
|
|
48
|
-
export declare function install_manifest_rel(scope?: InstallScope): string;
|
|
49
|
-
export declare function codex_config_rel(scope?: InstallScope): string;
|
|
50
|
-
export declare function merge_codex_config(text: string, opts?: {
|
|
51
|
-
force?: boolean;
|
|
52
|
-
}): string;
|
|
53
|
-
export declare function ensure_codex_config(repoRoot: string, scope?: InstallScope, opts?: {
|
|
54
|
-
force?: boolean;
|
|
55
|
-
}): {
|
|
56
|
-
action: EngineAction;
|
|
57
|
-
problems: string[];
|
|
58
|
-
};
|
|
59
|
-
export declare function install_workflow(repoRoot: string, opts?: {
|
|
60
|
-
force?: boolean;
|
|
61
|
-
packageRoot?: string;
|
|
62
|
-
scope?: InstallScope;
|
|
63
|
-
}): EngineResult;
|
|
64
|
-
export declare function update_workflow(repoRoot: string, opts?: {
|
|
65
|
-
packageRoot?: string;
|
|
66
|
-
scope?: InstallScope;
|
|
67
|
-
}): EngineResult;
|
|
68
|
-
export declare function uninstall_workflow(repoRoot: string, opts?: {
|
|
69
|
-
dryRun?: boolean;
|
|
70
|
-
scope?: InstallScope;
|
|
71
|
-
}): EngineResult;
|