sdd-flow-kit 1.1.1 → 1.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 +16 -0
- package/dist/cli.js +28 -14
- package/dist/core/bootstrap.js +58 -0
- package/dist/core/opsxDelivery.js +34 -17
- package/dist/core/opsxWorkflowPackage.js +23 -0
- package/dist/core/packageJsonPatch.js +89 -0
- package/dist/postinstall.js +22 -18
- package/dist/steps/doctor.js +19 -0
- package/dist/steps/invokeFlow.js +18 -11
- package/dist/steps/runFlow.js +2 -0
- package/dist/steps/setupProject.js +15 -6
- package/package.json +1 -1
- package/src/templates/artifacts/06-agent-skill.template.md +31 -52
- package/src/templates/rules/sdd-no-implement-until-apply.mdc.template +7 -0
package/README.md
CHANGED
|
@@ -71,6 +71,22 @@ npx sdd-flow-kit doctor --agent cursor
|
|
|
71
71
|
> 完整环境(分支切换 + opsx-workflow)请另执行:`npx sdd-flow-kit install -y`
|
|
72
72
|
> 跳过 postinstall:`SDD_FLOW_KIT_SKIP_POSTINSTALL=1 pnpm add -D sdd-flow-kit`
|
|
73
73
|
|
|
74
|
+
##### `pnpm add` 后依赖全被删掉(`Packages: +1 -932`)
|
|
75
|
+
|
|
76
|
+
**原因**:`pnpm` 会按当前 `package.json` **同步** `node_modules`。若 manifest 里只剩 `sdd-flow-kit`(或文件被误保存成几行空壳),就会卸载其它所有包——**不是** `pnpm add` 单独删包,而是 manifest 已不完整。
|
|
77
|
+
|
|
78
|
+
**恢复**(在业务仓如 `adInsight-web/frontend`):
|
|
79
|
+
|
|
80
|
+
1. 用 Git 恢复完整 `package.json`:`git checkout -- package.json`
|
|
81
|
+
2. 重装:`pnpm install`
|
|
82
|
+
3. 再装 kit(建议 **≥ 1.1.2**,且必须在有完整 `package.json` 的目录执行):
|
|
83
|
+
- `cd frontend && pnpm add -D sdd-flow-kit@1.1.2`
|
|
84
|
+
- 或本地联调:`pnpm add -D file:../../sdd-flow-kit`
|
|
85
|
+
|
|
86
|
+
**v1.1.2+ 防护**:`repairOpsxDeliveryScripts` 仅合并 `scripts`,写入前检查依赖条数与文件体积;`postinstall` 不再改 `package.json`,且必须有 `INIT_CWD` 才运行。
|
|
87
|
+
|
|
88
|
+
**pnpm v10 构建脚本**:若出现 `Ignored build scripts: sdd-flow-kit`,执行 `pnpm approve-builds` 选中 `sdd-flow-kit`,或安装后手动:`node ./node_modules/sdd-flow-kit/dist/postinstall.js`
|
|
89
|
+
|
|
74
90
|
#### 使用说明
|
|
75
91
|
|
|
76
92
|
##### 安装根:cd 到哪,装到哪(v1.2+)
|
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ const runGate_1 = require("./steps/runGate");
|
|
|
16
16
|
const runPhaseAdvance_1 = require("./steps/runPhaseAdvance");
|
|
17
17
|
const runPropose_1 = require("./steps/runPropose");
|
|
18
18
|
const runDeliver_1 = require("./steps/runDeliver");
|
|
19
|
+
const bootstrap_1 = require("./core/bootstrap");
|
|
19
20
|
const projectRoots_1 = require("./core/projectRoots");
|
|
20
21
|
const ALLOWED_AGENTS = ["cursor", "claude-code", "codex", "openclaw", "custom"];
|
|
21
22
|
function getArgValue(flag) {
|
|
@@ -35,9 +36,11 @@ function printHelp() {
|
|
|
35
36
|
"1) 生成流程产物骨架:",
|
|
36
37
|
" npx sdd-flow-kit start --product ADI --version 2.3.2",
|
|
37
38
|
"",
|
|
38
|
-
"2)
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"2) 安装到当前目录(全量:SDD + OPSX + Superpowers,推荐):",
|
|
40
|
+
" cd <你的项目目录> # 如 adInsight-web/frontend",
|
|
41
|
+
" pnpm add -D sdd-flow-kit",
|
|
42
|
+
" npx sdd-flow-kit install --project ADI -y",
|
|
43
|
+
" 本地 OPSX: SDD_OPSX_WORKFLOW_PACKAGE=file:../../opsx-workflow npx sdd-flow-kit install -y",
|
|
41
44
|
"",
|
|
42
45
|
"3) 生成骨架并进入可执行模式(推荐):",
|
|
43
46
|
" npx sdd-flow-kit run --product ADI --version 2.3.2 --agent claude-code",
|
|
@@ -141,6 +144,26 @@ async function main() {
|
|
|
141
144
|
process_1.default.exitCode = 1;
|
|
142
145
|
return;
|
|
143
146
|
}
|
|
147
|
+
const skipBootstrap = process_1.default.env.SDD_FLOW_KIT_SKIP_BOOTSTRAP === "1";
|
|
148
|
+
const bootstrapCommands = new Set([
|
|
149
|
+
"doctor",
|
|
150
|
+
"ensure-opsx",
|
|
151
|
+
"run",
|
|
152
|
+
"invoke",
|
|
153
|
+
"install",
|
|
154
|
+
"setup",
|
|
155
|
+
"gate",
|
|
156
|
+
"phase",
|
|
157
|
+
"propose",
|
|
158
|
+
"deliver",
|
|
159
|
+
]);
|
|
160
|
+
if (!skipBootstrap && !dryRun && bootstrapCommands.has(cmd)) {
|
|
161
|
+
await (0, bootstrap_1.bootstrapProjectIfNeeded)({
|
|
162
|
+
projectRoot,
|
|
163
|
+
agent,
|
|
164
|
+
projectKind: projectKind,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
144
167
|
if (cmd === "gate") {
|
|
145
168
|
const expect = getArgValue("--expect");
|
|
146
169
|
if (!expect || !GATE_EXPECTS.includes(expect)) {
|
|
@@ -245,15 +268,6 @@ async function main() {
|
|
|
245
268
|
// eslint-disable-next-line no-console
|
|
246
269
|
console.log(JSON.stringify({ command: "doctor", projectRoot: path_1.default.resolve(projectRoot), result: doctor }, null, 2));
|
|
247
270
|
}
|
|
248
|
-
if (cmd === "ensure-opsx" && !dryRun) {
|
|
249
|
-
await (0, setupProject_1.setupProject)({
|
|
250
|
-
projectRoot,
|
|
251
|
-
agent,
|
|
252
|
-
yes: true,
|
|
253
|
-
light: true,
|
|
254
|
-
projectKind: projectKind,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
271
|
if (!doctor.ok)
|
|
258
272
|
process_1.default.exitCode = 1;
|
|
259
273
|
return;
|
|
@@ -297,7 +311,7 @@ async function main() {
|
|
|
297
311
|
const text = (fromFlag !== null && fromFlag !== void 0 ? fromFlag : raw).trim();
|
|
298
312
|
const parsed = (0, invokeFlow_1.parseInvokeText)(text);
|
|
299
313
|
if (!parsed) {
|
|
300
|
-
throw new Error("invoke
|
|
314
|
+
throw new Error("invoke 解析失败,示例:开发 ADI 2.3.2 需求 | 进行ADI_2.3.2版本开发");
|
|
301
315
|
}
|
|
302
316
|
const options = (0, invokeFlow_1.toRunOptions)({
|
|
303
317
|
parsed,
|
|
@@ -349,7 +363,7 @@ async function main() {
|
|
|
349
363
|
// eslint-disable-next-line no-console
|
|
350
364
|
console.log(`流程已初始化:openspec/PRD/${result.runId}/`);
|
|
351
365
|
// eslint-disable-next-line no-console
|
|
352
|
-
console.log(`请打开 openspec/PRD/${result.runId}/NEXT.md
|
|
366
|
+
console.log(`请打开 openspec/PRD/${result.runId}/NEXT.md,严格按 gate→phase→propose→deliver 执行;确认点前禁止改 src/。`);
|
|
353
367
|
// eslint-disable-next-line no-console
|
|
354
368
|
console.log(`agent=${result.adapterName}, cli=${result.adapterAvailable ? "detected" : "missing"}, autoExecute=${result.executed ? "yes" : "manual"}`);
|
|
355
369
|
// eslint-disable-next-line no-console
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.bootstrapProjectIfNeeded = bootstrapProjectIfNeeded;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const inferProject_1 = require("./inferProject");
|
|
9
|
+
const packageJsonPatch_1 = require("./packageJsonPatch");
|
|
10
|
+
const opsxDelivery_1 = require("./opsxDelivery");
|
|
11
|
+
const projectRoots_1 = require("./projectRoots");
|
|
12
|
+
const setupProject_1 = require("../steps/setupProject");
|
|
13
|
+
/**
|
|
14
|
+
* pnpm v10 常拦截 postinstall;首次跑 CLI 时补全 SDD + OPSX 全栈(不切分支)。
|
|
15
|
+
*/
|
|
16
|
+
async function bootstrapProjectIfNeeded(options) {
|
|
17
|
+
var _a;
|
|
18
|
+
const installRoot = (0, projectRoots_1.resolveInstallRoot)(options.projectRoot);
|
|
19
|
+
if (await (0, opsxDelivery_1.isOpsxApplyReady)(installRoot)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (options.dryRun) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const kind = (_a = options.projectKind) !== null && _a !== void 0 ? _a : (await (0, inferProject_1.inferProjectKind)(installRoot));
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
console.log("[sdd-flow-kit] 检测到 OPSX 未就绪,正在自动 bootstrap(等价 install -y)…");
|
|
28
|
+
const pkgRoot = await (0, projectRoots_1.resolvePackageRoot)(installRoot);
|
|
29
|
+
const pnpmPatched = await (0, packageJsonPatch_1.ensurePnpmAllowSddFlowKitBuild)(path_1.default.join(pkgRoot, "package.json"));
|
|
30
|
+
if (pnpmPatched) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.log("[sdd-flow-kit] 已写入 pnpm.onlyBuiltDependencies: sdd-flow-kit;下次 pnpm install 将执行 postinstall");
|
|
33
|
+
}
|
|
34
|
+
await (0, setupProject_1.setupProject)({
|
|
35
|
+
projectRoot: installRoot,
|
|
36
|
+
agent: options.agent,
|
|
37
|
+
yes: true,
|
|
38
|
+
light: false,
|
|
39
|
+
skipGit: true,
|
|
40
|
+
projectKind: kind !== null && kind !== void 0 ? kind : "ADI",
|
|
41
|
+
});
|
|
42
|
+
if (!(await (0, opsxDelivery_1.isOpsxApplyReady)(installRoot))) {
|
|
43
|
+
await (0, opsxDelivery_1.ensureOpsxWorkflow)({
|
|
44
|
+
projectRoot: installRoot,
|
|
45
|
+
agent: options.agent,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const ready = await (0, opsxDelivery_1.isOpsxApplyReady)(installRoot);
|
|
49
|
+
if (ready) {
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.log("[sdd-flow-kit] bootstrap 完成:SDD + OPSX 已就绪");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.warn("[sdd-flow-kit] bootstrap 后仍有检查未通过,请执行: npx sdd-flow-kit doctor --agent cursor");
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveOpsxWorkflowPackage = resolveOpsxWorkflowPackage;
|
|
6
7
|
exports.deliveryPipelineNpmScripts = deliveryPipelineNpmScripts;
|
|
7
8
|
exports.pathExists = pathExists;
|
|
8
9
|
exports.resolveOpsxAgent = resolveOpsxAgent;
|
|
@@ -14,8 +15,12 @@ exports.collectDeliverySetupChecks = collectDeliverySetupChecks;
|
|
|
14
15
|
const child_process_1 = require("child_process");
|
|
15
16
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
16
17
|
const path_1 = __importDefault(require("path"));
|
|
17
|
-
const
|
|
18
|
+
const packageJsonPatch_1 = require("./packageJsonPatch");
|
|
18
19
|
const projectRoots_1 = require("./projectRoots");
|
|
20
|
+
const opsxWorkflowPackage_1 = require("./opsxWorkflowPackage");
|
|
21
|
+
function resolveOpsxWorkflowPackage() {
|
|
22
|
+
return (0, opsxWorkflowPackage_1.resolveOpsxWorkflowPackageForCwd)(process.cwd());
|
|
23
|
+
}
|
|
19
24
|
const DELIVERY_SCRIPT_REL = ".cursor/skills/opsx-dev-delivery-pipeline/scripts/opsx-delivery-pipeline.sh";
|
|
20
25
|
function deliveryPipelineNpmScripts() {
|
|
21
26
|
const full = `bash ${DELIVERY_SCRIPT_REL}`;
|
|
@@ -45,7 +50,7 @@ async function resolveOpsxAgent(projectRoot, preferred) {
|
|
|
45
50
|
return "claude-code";
|
|
46
51
|
return preferred;
|
|
47
52
|
}
|
|
48
|
-
/** 将 package.json 中过期的 poquan
|
|
53
|
+
/** 将 package.json 中过期的 poquan 脚本修正为 opsx-dev-delivery-pipeline(仅合并 scripts) */
|
|
49
54
|
async function repairOpsxDeliveryScripts(projectRoot) {
|
|
50
55
|
const pkgPath = path_1.default.join(projectRoot, "package.json");
|
|
51
56
|
if (!(await pathExists(pkgPath)))
|
|
@@ -55,29 +60,35 @@ async function repairOpsxDeliveryScripts(projectRoot) {
|
|
|
55
60
|
return false;
|
|
56
61
|
const { full, short } = deliveryPipelineNpmScripts();
|
|
57
62
|
const text = await promises_1.default.readFile(pkgPath, "utf8");
|
|
58
|
-
|
|
63
|
+
let pkg;
|
|
64
|
+
try {
|
|
65
|
+
pkg = JSON.parse(text);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
59
70
|
if (!pkg.scripts)
|
|
60
71
|
pkg.scripts = {};
|
|
61
|
-
let changed = false;
|
|
62
72
|
const needsFix = (v) => !v || v.includes("poquan-dev-delivery-pipeline") || v.includes("poquan-delivery-pipeline");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
73
|
+
const updates = {};
|
|
74
|
+
if (needsFix(pkg.scripts["opsx:delivery-pipeline"]))
|
|
75
|
+
updates["opsx:delivery-pipeline"] = full;
|
|
67
76
|
if (needsFix(pkg.scripts["opsx:delivery-pipeline:short"])) {
|
|
68
|
-
|
|
69
|
-
changed = true;
|
|
77
|
+
updates["opsx:delivery-pipeline:short"] = short;
|
|
70
78
|
}
|
|
71
|
-
if (
|
|
79
|
+
if (Object.keys(updates).length === 0)
|
|
72
80
|
return false;
|
|
73
|
-
await (0,
|
|
74
|
-
|
|
81
|
+
const result = await (0, packageJsonPatch_1.patchPackageJsonScripts)(pkgPath, updates, {
|
|
82
|
+
minDependencyCount: 2,
|
|
83
|
+
minBytes: 120,
|
|
84
|
+
});
|
|
85
|
+
return result.ok && result.changed;
|
|
75
86
|
}
|
|
76
87
|
function hasCommand(cmd) {
|
|
77
88
|
return (0, child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" }).status === 0;
|
|
78
89
|
}
|
|
79
90
|
function buildOpsxWorkflowSetupCommand(projectRoot, agent) {
|
|
80
|
-
const baseArgs = [
|
|
91
|
+
const baseArgs = [resolveOpsxWorkflowPackage(), "setup"];
|
|
81
92
|
if (agent === "cursor") {
|
|
82
93
|
return {
|
|
83
94
|
cmd: "npx",
|
|
@@ -99,6 +110,7 @@ async function isOpsxApplyReady(projectRoot) {
|
|
|
99
110
|
}
|
|
100
111
|
/** 缺 openspec/TDD/Playwright 工作流时安装 @lixin5257xxx/opsx-workflow,并修复 delivery 脚本 */
|
|
101
112
|
async function ensureOpsxWorkflow(options) {
|
|
113
|
+
var _a;
|
|
102
114
|
const installRoot = await (0, projectRoots_1.resolvePackageRoot)((0, projectRoots_1.resolveInstallRoot)(options.projectRoot));
|
|
103
115
|
const plannedActions = [];
|
|
104
116
|
let checks = await collectDeliverySetupChecks(installRoot);
|
|
@@ -129,7 +141,10 @@ async function ensureOpsxWorkflow(options) {
|
|
|
129
141
|
env: process.env,
|
|
130
142
|
});
|
|
131
143
|
if (ret.status !== 0) {
|
|
132
|
-
|
|
144
|
+
const hint = ((_a = process.env.SDD_OPSX_WORKFLOW_PACKAGE) === null || _a === void 0 ? void 0 : _a.trim()) ?
|
|
145
|
+
""
|
|
146
|
+
: " 本地 OPSX 可设: SDD_OPSX_WORKFLOW_PACKAGE=file:../../opsx-workflow";
|
|
147
|
+
throw new Error(`opsx-workflow 安装失败:${cmd} ${args.join(" ")}${hint}`);
|
|
133
148
|
}
|
|
134
149
|
const repairedScripts = await repairOpsxDeliveryScripts(installRoot);
|
|
135
150
|
if (repairedScripts) {
|
|
@@ -159,10 +174,12 @@ async function collectDeliverySetupChecks(installRoot) {
|
|
|
159
174
|
ok: await pathExists(path_1.default.join(openspecRoot, "openspec")),
|
|
160
175
|
detail: openspecRoot === root ? "openspec/ 目录(安装根)" : `openspec/ 目录(上级 ${openspecRoot})`,
|
|
161
176
|
});
|
|
177
|
+
const openspecOk = hasCommand("openspec") ||
|
|
178
|
+
(0, child_process_1.spawnSync)("npx", ["openspec", "--version"], { stdio: "ignore", cwd: root }).status === 0;
|
|
162
179
|
checks.push({
|
|
163
180
|
name: "openspec-cli",
|
|
164
|
-
ok:
|
|
165
|
-
detail: "openspec CLI
|
|
181
|
+
ok: openspecOk,
|
|
182
|
+
detail: openspecOk ? "openspec CLI 可用" : "请安装 openspec CLI 或执行 opsx-workflow setup",
|
|
166
183
|
});
|
|
167
184
|
const skillPath = path_1.default.join(root, ".cursor", "skills", "opsx-dev-delivery-pipeline", "SKILL.md");
|
|
168
185
|
checks.push({
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveOpsxWorkflowPackageForCwd = resolveOpsxWorkflowPackageForCwd;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
/** 本地联调 SDD_OPSX_WORKFLOW_PACKAGE=file:../../opsx-workflow;不存在则回退 npm 包 */
|
|
10
|
+
function resolveOpsxWorkflowPackageForCwd(cwd) {
|
|
11
|
+
var _a;
|
|
12
|
+
const fromEnv = (_a = process.env.SDD_OPSX_WORKFLOW_PACKAGE) === null || _a === void 0 ? void 0 : _a.trim();
|
|
13
|
+
if (fromEnv === null || fromEnv === void 0 ? void 0 : fromEnv.startsWith("file:")) {
|
|
14
|
+
const localPath = path_1.default.resolve(cwd, fromEnv.slice("file:".length));
|
|
15
|
+
if (fs_1.default.existsSync(localPath)) {
|
|
16
|
+
return fromEnv;
|
|
17
|
+
}
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.warn(`[sdd-flow-kit] SDD_OPSX_WORKFLOW_PACKAGE=${fromEnv} 不存在 (${localPath}),改用 @lixin5257xxx/opsx-workflow`);
|
|
20
|
+
return "@lixin5257xxx/opsx-workflow";
|
|
21
|
+
}
|
|
22
|
+
return fromEnv || "@lixin5257xxx/opsx-workflow";
|
|
23
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.countPackageDependencies = countPackageDependencies;
|
|
7
|
+
exports.patchPackageJsonScripts = patchPackageJsonScripts;
|
|
8
|
+
exports.ensurePnpmAllowSddFlowKitBuild = ensurePnpmAllowSddFlowKitBuild;
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const fs_1 = require("./fs");
|
|
11
|
+
function countPackageDependencies(pkg) {
|
|
12
|
+
const deps = pkg.dependencies;
|
|
13
|
+
const devDeps = pkg.devDependencies;
|
|
14
|
+
return Object.keys(deps !== null && deps !== void 0 ? deps : {}).length + Object.keys(devDeps !== null && devDeps !== void 0 ? devDeps : {}).length;
|
|
15
|
+
}
|
|
16
|
+
async function patchPackageJsonScripts(pkgPath, scriptUpdates, options = {}) {
|
|
17
|
+
var _a, _b, _c, _d;
|
|
18
|
+
const minDependencyCount = (_a = options.minDependencyCount) !== null && _a !== void 0 ? _a : 1;
|
|
19
|
+
const minBytes = (_b = options.minBytes) !== null && _b !== void 0 ? _b : 80;
|
|
20
|
+
const minSizeRatio = (_c = options.minSizeRatio) !== null && _c !== void 0 ? _c : 0.5;
|
|
21
|
+
let text;
|
|
22
|
+
try {
|
|
23
|
+
text = await promises_1.default.readFile(pkgPath, "utf8");
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { ok: false, reason: "package.json 不存在或不可读" };
|
|
27
|
+
}
|
|
28
|
+
if (text.length < minBytes) {
|
|
29
|
+
return { ok: false, reason: `package.json 过小(${text.length} 字节)` };
|
|
30
|
+
}
|
|
31
|
+
let pkg;
|
|
32
|
+
try {
|
|
33
|
+
pkg = JSON.parse(text);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { ok: false, reason: "package.json 不是合法 JSON" };
|
|
37
|
+
}
|
|
38
|
+
if (countPackageDependencies(pkg) < minDependencyCount) {
|
|
39
|
+
return { ok: false, reason: "dependencies 过少" };
|
|
40
|
+
}
|
|
41
|
+
const beforeKeys = Object.keys(pkg);
|
|
42
|
+
const scripts = (_d = pkg.scripts) !== null && _d !== void 0 ? _d : {};
|
|
43
|
+
let changed = false;
|
|
44
|
+
for (const [key, value] of Object.entries(scriptUpdates)) {
|
|
45
|
+
if (scripts[key] !== value) {
|
|
46
|
+
scripts[key] = value;
|
|
47
|
+
changed = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!changed)
|
|
51
|
+
return { ok: true, changed: false };
|
|
52
|
+
pkg.scripts = scripts;
|
|
53
|
+
for (const key of beforeKeys) {
|
|
54
|
+
if (!Object.keys(pkg).includes(key)) {
|
|
55
|
+
return { ok: false, reason: `会丢失字段: ${key}` };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const out = `${JSON.stringify(pkg, null, 2)}\n`;
|
|
59
|
+
if (out.length < text.length * minSizeRatio) {
|
|
60
|
+
return { ok: false, reason: "写入后体积异常缩小" };
|
|
61
|
+
}
|
|
62
|
+
await (0, fs_1.writeTextFileEnsuredDir)(pkgPath, out);
|
|
63
|
+
return { ok: true, changed: true };
|
|
64
|
+
}
|
|
65
|
+
async function ensurePnpmAllowSddFlowKitBuild(pkgPath) {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
let text;
|
|
68
|
+
try {
|
|
69
|
+
text = await promises_1.default.readFile(pkgPath, "utf8");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
let pkg;
|
|
75
|
+
try {
|
|
76
|
+
pkg = JSON.parse(text);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const pnpm = (_a = pkg.pnpm) !== null && _a !== void 0 ? _a : {};
|
|
82
|
+
const list = (_b = pnpm.onlyBuiltDependencies) !== null && _b !== void 0 ? _b : [];
|
|
83
|
+
if (list.includes("sdd-flow-kit"))
|
|
84
|
+
return false;
|
|
85
|
+
pnpm.onlyBuiltDependencies = [...list, "sdd-flow-kit"];
|
|
86
|
+
pkg.pnpm = pnpm;
|
|
87
|
+
await (0, fs_1.writeTextFileEnsuredDir)(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
package/dist/postinstall.js
CHANGED
|
@@ -40,6 +40,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
40
40
|
const process_1 = __importDefault(require("process"));
|
|
41
41
|
const inferProject_1 = require("./core/inferProject");
|
|
42
42
|
const opsxDelivery_1 = require("./core/opsxDelivery");
|
|
43
|
+
const projectRoots_1 = require("./core/projectRoots");
|
|
43
44
|
const setupProject_1 = require("./steps/setupProject");
|
|
44
45
|
async function inferAgent(projectRoot) {
|
|
45
46
|
var _a;
|
|
@@ -68,50 +69,53 @@ async function inferAgent(projectRoot) {
|
|
|
68
69
|
return "cursor";
|
|
69
70
|
}
|
|
70
71
|
async function main() {
|
|
72
|
+
var _a;
|
|
71
73
|
if (process_1.default.env.SDD_FLOW_KIT_SKIP_POSTINSTALL === "1") {
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
|
-
const initCwd = process_1.default.env.INIT_CWD ?
|
|
76
|
+
const initCwd = (_a = process_1.default.env.INIT_CWD) === null || _a === void 0 ? void 0 : _a.trim();
|
|
75
77
|
const pkgDir = path_1.default.resolve(__dirname, "..");
|
|
76
|
-
|
|
78
|
+
if (!initCwd) {
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.warn("[sdd-flow-kit] postinstall: 缺少 INIT_CWD,已跳过。请执行: npx sdd-flow-kit install --project ADI -y");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const projectRoot = (0, projectRoots_1.resolveInstallRoot)(initCwd);
|
|
77
84
|
if (projectRoot === pkgDir)
|
|
78
85
|
return;
|
|
79
86
|
const projectKind = await (0, inferProject_1.inferProjectKind)(projectRoot);
|
|
80
87
|
const agent = await inferAgent(projectRoot);
|
|
88
|
+
const light = process_1.default.env.SDD_FLOW_KIT_POSTINSTALL_LIGHT === "1";
|
|
81
89
|
// eslint-disable-next-line no-console
|
|
82
|
-
console.log(`[sdd-flow-kit] postinstall (light) at: ${projectRoot}`);
|
|
90
|
+
console.log(`[sdd-flow-kit] postinstall (${light ? "light" : "full"}) at: ${projectRoot}`);
|
|
83
91
|
// eslint-disable-next-line no-console
|
|
84
92
|
console.log(`[sdd-flow-kit] agent=${agent}, project=${projectKind}`);
|
|
85
|
-
await (0, setupProject_1.setupProject)({
|
|
93
|
+
const result = await (0, setupProject_1.setupProject)({
|
|
86
94
|
projectRoot,
|
|
87
95
|
agent,
|
|
88
96
|
yes: true,
|
|
89
|
-
light
|
|
97
|
+
light,
|
|
98
|
+
skipGit: true,
|
|
90
99
|
dryRun: false,
|
|
91
100
|
projectKind,
|
|
92
101
|
});
|
|
93
|
-
const repaired = await (0, opsxDelivery_1.repairOpsxDeliveryScripts)(projectRoot);
|
|
94
|
-
if (repaired) {
|
|
95
|
-
// eslint-disable-next-line no-console
|
|
96
|
-
console.log("[sdd-flow-kit] repaired package.json opsx:delivery-pipeline scripts");
|
|
97
|
-
}
|
|
98
102
|
const ready = await (0, opsxDelivery_1.isOpsxApplyReady)(projectRoot);
|
|
99
103
|
if (!ready) {
|
|
100
104
|
// eslint-disable-next-line no-console
|
|
101
|
-
console.warn("[sdd-flow-kit] OPSX
|
|
105
|
+
console.warn("[sdd-flow-kit] OPSX 未完全就绪。请执行: npx sdd-flow-kit install --project " +
|
|
106
|
+
`${projectKind} -y` +
|
|
107
|
+
(process_1.default.env.SDD_OPSX_WORKFLOW_PACKAGE ? "" : "(或设 SDD_OPSX_WORKFLOW_PACKAGE 指向本地 opsx-workflow)"));
|
|
102
108
|
}
|
|
103
109
|
else {
|
|
104
110
|
// eslint-disable-next-line no-console
|
|
105
|
-
console.log("[sdd-flow-kit]
|
|
111
|
+
console.log("[sdd-flow-kit] 安装完成:SDD + OPSX + Superpowers 已就绪");
|
|
106
112
|
}
|
|
107
113
|
// eslint-disable-next-line no-console
|
|
108
|
-
console.log(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
console.log("[sdd-flow-kit] Cursor skill: .cursor/skills/sdd-flow-kit/SKILL.md");
|
|
112
|
-
}
|
|
114
|
+
console.log(`[sdd-flow-kit] 开发需求: npx sdd-flow-kit invoke "开发 ${projectKind} x.x.x 需求" -y`);
|
|
115
|
+
// eslint-disable-next-line no-console
|
|
116
|
+
console.log(`[sdd-flow-kit] environmentReady=${result.environmentReady}, setupExecuted=${result.setupExecuted}`);
|
|
113
117
|
}
|
|
114
118
|
main().catch((err) => {
|
|
115
119
|
// eslint-disable-next-line no-console
|
|
116
|
-
console.warn("[sdd-flow-kit] postinstall
|
|
120
|
+
console.warn("[sdd-flow-kit] postinstall skipped:", err instanceof Error ? err.message : err);
|
|
117
121
|
});
|
package/dist/steps/doctor.js
CHANGED
|
@@ -9,6 +9,7 @@ const promises_1 = __importDefault(require("fs/promises"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const process_1 = __importDefault(require("process"));
|
|
11
11
|
const nodeCompat_1 = require("../core/nodeCompat");
|
|
12
|
+
const bootstrap_1 = require("../core/bootstrap");
|
|
12
13
|
const opsxDelivery_1 = require("../core/opsxDelivery");
|
|
13
14
|
function hasCommand(cmd) {
|
|
14
15
|
const out = (0, child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" });
|
|
@@ -83,6 +84,24 @@ async function runDoctor(options) {
|
|
|
83
84
|
checks.push(c);
|
|
84
85
|
}
|
|
85
86
|
let ok = checks.every((c) => c.ok);
|
|
87
|
+
const opsxStackFailed = checks.some((c) => !c.ok &&
|
|
88
|
+
[
|
|
89
|
+
"opsx-dev-delivery-pipeline-skill",
|
|
90
|
+
"opsx-config",
|
|
91
|
+
"delivery-pipeline-script",
|
|
92
|
+
"package-json-delivery-script",
|
|
93
|
+
].includes(c.name));
|
|
94
|
+
if (!ok && opsxStackFailed && !options.fix && !options.dryRun) {
|
|
95
|
+
await (0, bootstrap_1.bootstrapProjectIfNeeded)({
|
|
96
|
+
projectRoot: options.projectRoot,
|
|
97
|
+
agent: options.agent,
|
|
98
|
+
});
|
|
99
|
+
checks.length = 0;
|
|
100
|
+
for (const c of await (0, opsxDelivery_1.collectDeliverySetupChecks)(options.projectRoot)) {
|
|
101
|
+
checks.push(c);
|
|
102
|
+
}
|
|
103
|
+
ok = checks.every((c) => c.ok);
|
|
104
|
+
}
|
|
86
105
|
if (!ok && options.fix) {
|
|
87
106
|
const ensured = await (0, opsxDelivery_1.ensureOpsxWorkflow)({
|
|
88
107
|
projectRoot: options.projectRoot,
|
package/dist/steps/invokeFlow.js
CHANGED
|
@@ -4,17 +4,24 @@ exports.parseInvokeText = parseInvokeText;
|
|
|
4
4
|
exports.toRunOptions = toRunOptions;
|
|
5
5
|
function parseInvokeText(text) {
|
|
6
6
|
const normalized = text.trim();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
const patterns = [
|
|
8
|
+
/(?:开发|进行)\s*(.+?)(?:[-_\s]*)V?([0-9]+(?:\.[0-9]+){1,3})\s*(?:版本)?\s*(?:需求|PRD)?/i,
|
|
9
|
+
/([A-Za-z\u4e00-\u9fa5]+)[_\s-]?V?([0-9]+(?:\.[0-9]+){1,3})\s*版本/i,
|
|
10
|
+
/([A-Za-z]+)[_\s-]([0-9]+(?:\.[0-9]+){2,3})(?:\s|$)/i,
|
|
11
|
+
];
|
|
12
|
+
for (const re of patterns) {
|
|
13
|
+
const match = normalized.match(re);
|
|
14
|
+
if (!match)
|
|
15
|
+
continue;
|
|
16
|
+
let product = match[1].replace(/[-_\s]+$/g, "").trim();
|
|
17
|
+
product = product.replace(/^进行/, "").trim();
|
|
18
|
+
if (/^adi$/i.test(product) || product.toUpperCase() === "ADI")
|
|
19
|
+
product = "ADI";
|
|
20
|
+
if (/^oms$/i.test(product))
|
|
21
|
+
product = "OMS";
|
|
22
|
+
return { product: product.toUpperCase(), version: match[2] };
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
18
25
|
}
|
|
19
26
|
function toRunOptions(args) {
|
|
20
27
|
var _a, _b;
|
package/dist/steps/runFlow.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.runFlow = runFlow;
|
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const adapters_1 = require("../adapters");
|
|
9
9
|
const fs_1 = require("../core/fs");
|
|
10
|
+
const inferProject_1 = require("../core/inferProject");
|
|
10
11
|
const startFlow_1 = require("./startFlow");
|
|
11
12
|
const setupProject_1 = require("./setupProject");
|
|
12
13
|
function buildChecklist(args) {
|
|
@@ -37,6 +38,7 @@ async function runFlow(options) {
|
|
|
37
38
|
branch: options.branch,
|
|
38
39
|
yes: options.yes,
|
|
39
40
|
dryRun: options.dryRun,
|
|
41
|
+
projectKind: (0, inferProject_1.productToKind)(options.product),
|
|
40
42
|
});
|
|
41
43
|
const scaffold = options.dryRun
|
|
42
44
|
? {
|
|
@@ -11,6 +11,7 @@ const fs_1 = require("../core/fs");
|
|
|
11
11
|
const readlinePrompt_1 = require("../core/readlinePrompt");
|
|
12
12
|
const utils_1 = require("../core/utils");
|
|
13
13
|
const opsxDelivery_1 = require("../core/opsxDelivery");
|
|
14
|
+
const packageJsonPatch_1 = require("../core/packageJsonPatch");
|
|
14
15
|
const projectRoots_1 = require("../core/projectRoots");
|
|
15
16
|
const templates_1 = require("../core/templates");
|
|
16
17
|
const EDITOR_CHOICES = ["claude-code", "cursor", "codex", "openclaw"];
|
|
@@ -125,7 +126,7 @@ function allSkillInstallPaths(projectRoot) {
|
|
|
125
126
|
.map((agent) => skillInstallPath(projectRoot, agent))
|
|
126
127
|
.filter((p) => Boolean(p));
|
|
127
128
|
}
|
|
128
|
-
async function installAgentSkill(projectRoot) {
|
|
129
|
+
async function installAgentSkill(projectRoot, projectKind) {
|
|
129
130
|
const targets = allSkillInstallPaths(projectRoot);
|
|
130
131
|
if (targets.length === 0)
|
|
131
132
|
return;
|
|
@@ -138,7 +139,11 @@ async function installAgentSkill(projectRoot) {
|
|
|
138
139
|
: target.includes("/.codex/")
|
|
139
140
|
? "codex"
|
|
140
141
|
: "openclaw";
|
|
141
|
-
const content = (
|
|
142
|
+
const content = renderVars(template, {
|
|
143
|
+
AGENT_NAME: agentName,
|
|
144
|
+
PROJECT_KIND: projectKind,
|
|
145
|
+
DEFAULT_PRODUCT: projectKind === "AD Tools" ? "AD Tools" : projectKind,
|
|
146
|
+
});
|
|
142
147
|
await (0, fs_1.writeTextFileEnsuredDir)(target, content);
|
|
143
148
|
}
|
|
144
149
|
}
|
|
@@ -231,9 +236,9 @@ async function setupProject(options) {
|
|
|
231
236
|
const branches = listLocalBranches(installRoot);
|
|
232
237
|
const branchExists = branches.includes(selectedBranch);
|
|
233
238
|
let branchResult;
|
|
234
|
-
if (options.light) {
|
|
239
|
+
if (options.light || options.skipGit) {
|
|
235
240
|
branchResult = { branch: selectedBranch, created: false };
|
|
236
|
-
plannedActions.push("light mode: skipped git branch checkout");
|
|
241
|
+
plannedActions.push(options.skipGit ? "skipGit: skipped git branch checkout" : "light mode: skipped git branch checkout");
|
|
237
242
|
}
|
|
238
243
|
else if (options.dryRun) {
|
|
239
244
|
branchResult = { branch: selectedBranch, created: !branchExists };
|
|
@@ -278,14 +283,18 @@ async function setupProject(options) {
|
|
|
278
283
|
plannedActions.push(`would install cursor rule: ${path_1.default.join(installRoot, ".cursor", "rules", "sdd-flow-kit-gates.mdc")}`);
|
|
279
284
|
}
|
|
280
285
|
else {
|
|
281
|
-
await
|
|
286
|
+
const pkgRoot = await (0, projectRoots_1.resolvePackageRoot)(installRoot);
|
|
287
|
+
const pnpmPatched = await (0, packageJsonPatch_1.ensurePnpmAllowSddFlowKitBuild)(path_1.default.join(pkgRoot, "package.json"));
|
|
288
|
+
if (pnpmPatched) {
|
|
289
|
+
plannedActions.push("added pnpm.onlyBuiltDependencies: sdd-flow-kit");
|
|
290
|
+
}
|
|
291
|
+
await installAgentSkill(installRoot, selectedKind);
|
|
282
292
|
await installSddCursorRule(installRoot);
|
|
283
293
|
skillInstalled = true;
|
|
284
294
|
await installDocsSkill(installRoot, selectedKind);
|
|
285
295
|
docsSkillInstalled = true;
|
|
286
296
|
await installOpsxConfig(installRoot, selectedAgent);
|
|
287
297
|
opsxConfigInstalled = true;
|
|
288
|
-
const pkgRoot = await (0, projectRoots_1.resolvePackageRoot)(installRoot);
|
|
289
298
|
const repaired = await (0, opsxDelivery_1.repairOpsxDeliveryScripts)(pkgRoot);
|
|
290
299
|
if (repaired) {
|
|
291
300
|
plannedActions.push("repaired package.json opsx:delivery-pipeline scripts (poquan → opsx)");
|
package/package.json
CHANGED
|
@@ -1,73 +1,52 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: sdd-flow-kit
|
|
3
|
-
description:
|
|
2
|
+
name: sdd-flow-kit
|
|
3
|
+
description: 用户说「开发/进行 {{DEFAULT_PRODUCT}} x.x.x 版本/需求」「ADI_2.3.2」等时触发。**禁止**未过 gate 直接改 src。必须先 invoke/run,确认点闭合后走 OpenSpec+Superpowers(gate→propose→apply→deliver)。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# SDD Flow
|
|
6
|
+
# SDD Flow Kit({{PROJECT_KIND}} — 脚本门禁)
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 触发即执行(第一步,禁止跳过)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
2. **必须**在终端执行其中的 `npx sdd-flow-kit ...`;根据 **exit code** 判断是否可继续(0=通过,非0=停止)。
|
|
12
|
-
3. `docModeOnly: true` 时 **禁止** 修改 `src/`、`e2e/`、`frontend/src/` 等实现路径。
|
|
13
|
-
4. 只有 `gate --expect impl-allowed` 通过后才允许改业务代码。
|
|
14
|
-
5. 只有 `deliver` 成功且 `phase advance --to shipped` 通过后才可宣称「开发完成」。
|
|
15
|
-
|
|
16
|
-
## 准备
|
|
10
|
+
用户一旦表达版本开发意图,**在本回合终端执行**(项目根 = 安装 sdd-flow-kit 的目录,如 `frontend/`):
|
|
17
11
|
|
|
18
12
|
```bash
|
|
19
|
-
npx sdd-flow-kit
|
|
13
|
+
npx sdd-flow-kit invoke "开发 {{DEFAULT_PRODUCT}} <版本号> 需求" -y --agent {{AGENT_NAME}}
|
|
20
14
|
```
|
|
21
15
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## 标准命令序列(复制 NEXT.md,按序执行)
|
|
25
|
-
|
|
26
|
-
| 阶段 | 门禁 / 推进 |
|
|
27
|
-
|------|-------------|
|
|
28
|
-
| PRD 已拉取 | `gate --expect prd-fetched` → `phase advance --to after-prd` |
|
|
29
|
-
| 提问模式 | `gate --expect questions-open` |
|
|
30
|
-
| 用户确认后(仅文档) | 更新 01/02/03/04 → `gate --expect docs-closed` → `phase advance --to docs-done` |
|
|
31
|
-
| 提案 | `propose --change <name>` →(AI 生成 openspec artifacts)→ `gate --expect propose-ready` → `phase advance --to propose-done` |
|
|
32
|
-
| 实现 | `phase advance --to impl --change <name>` → `gate --expect impl-allowed` → 按 tasks 改代码 |
|
|
33
|
-
| 交付 | `deliver --change <name>` → `phase advance --to shipped` |
|
|
34
|
-
|
|
35
|
-
统一参数(示例):
|
|
16
|
+
或:
|
|
36
17
|
|
|
37
18
|
```bash
|
|
38
|
-
|
|
39
|
-
export RID=<runId>
|
|
40
|
-
export CH=<change-name>
|
|
41
|
-
|
|
42
|
-
npx sdd-flow-kit gate --project-root "$PR" --run-id "$RID" --expect docs-closed
|
|
43
|
-
npx sdd-flow-kit phase advance --project-root "$PR" --run-id "$RID" --to docs-done
|
|
19
|
+
npx sdd-flow-kit run --product {{DEFAULT_PRODUCT}} --version <版本号> -y --agent {{AGENT_NAME}}
|
|
44
20
|
```
|
|
45
21
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### 阶段 A
|
|
49
|
-
- Step1:`01-获取需求文档指引.md` → 回填 `source/PRD.md`
|
|
50
|
-
- Step2:`02-需求分析提示词.md`;未闭合时 **只** 改 `03`
|
|
51
|
-
|
|
52
|
-
### 阶段 B
|
|
53
|
-
- 用户确认后:生成 01/02/04,**然后必须** `gate docs-closed`(会检测 git 是否误改 src)
|
|
22
|
+
然后 **只读** `openspec/PRD/<runId>/NEXT.md` 与 `.session-state.json`,按其中命令顺序执行;**exit code ≠ 0 必须停止**。
|
|
54
23
|
|
|
55
|
-
|
|
56
|
-
- `propose` 创建 change;AI 按 openspec-propose 写 artifacts;`gate propose-ready` 校验 apply-ready
|
|
24
|
+
## 铁律(防 vibe coding)
|
|
57
25
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
26
|
+
1. `docModeOnly: true` 时 **禁止** 修改 `src/`、`frontend/src/`、`e2e/`。
|
|
27
|
+
2. 用户粘贴确认点、更新 03 后:先产出 01/02/04,再 **必须**:
|
|
28
|
+
```bash
|
|
29
|
+
npx sdd-flow-kit gate --project-root <安装根> --run-id <runId> --expect docs-closed
|
|
30
|
+
npx sdd-flow-kit phase advance --project-root <安装根> --run-id <runId> --to docs-done
|
|
31
|
+
```
|
|
32
|
+
3. **仅当** `gate --expect impl-allowed` 通过后,才允许按 `openspec/changes/<change>/tasks.md` 改业务代码。
|
|
33
|
+
4. 提案/实现 **必须** 读并执行:
|
|
34
|
+
- `.cursor/skills/opsx-superpowers-workflow/SKILL.md`(总编排)
|
|
35
|
+
- `openspec-propose` → `openspec-apply-change` → `pnpm run opsx:delivery-pipeline -- <change>`
|
|
36
|
+
5. **禁止** 跳过 `propose` / `deliver` 或手写 tasks 全勾 `[x]` 宣称完成。
|
|
61
37
|
|
|
62
|
-
##
|
|
38
|
+
## 阶段摘要
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
40
|
+
| 阶段 | 动作 |
|
|
41
|
+
|------|------|
|
|
42
|
+
| A | Step1 PRD → `gate prd-fetched` → `phase after-prd` |
|
|
43
|
+
| A2 | 提问模式仅 03 → `gate questions-open` |
|
|
44
|
+
| B | 用户确认后 01/02/04 → `gate docs-closed` → `phase docs-done` |
|
|
45
|
+
| C | `propose` + OpenSpec artifacts → `gate propose-ready` → `phase propose-done` |
|
|
46
|
+
| D | `phase impl` → `gate impl-allowed` → 改代码 → `deliver` → `phase shipped` |
|
|
68
47
|
|
|
69
48
|
## 环境
|
|
70
49
|
|
|
71
50
|
- agent: {{AGENT_NAME}}
|
|
72
|
-
-
|
|
73
|
-
-
|
|
51
|
+
- 全量安装: `npx sdd-flow-kit install --project {{PROJECT_KIND}} -y`
|
|
52
|
+
- 本地 OPSX: `SDD_OPSX_WORKFLOW_PACKAGE=file:../../opsx-workflow`
|
|
@@ -22,6 +22,13 @@ alwaysApply: true
|
|
|
22
22
|
- **禁止** 修改 `src/`、`e2e/`、`frontend/src/` 等
|
|
23
23
|
- 用户粘贴确认点后:先 `gate docs-closed`,通过后再 `phase advance --to docs-done`;**不得**同回合改 ts/vue
|
|
24
24
|
|
|
25
|
+
## 自然语言触发(ADI / OMS 等)
|
|
26
|
+
|
|
27
|
+
用户说「开发/进行 ADI 2.3.2 版本」「ADI_2.3.2」等时:
|
|
28
|
+
|
|
29
|
+
1. **先** 在终端执行:`npx sdd-flow-kit invoke "开发 ADI 2.3.2 需求" -y`
|
|
30
|
+
2. 打开 `openspec/PRD/<runId>/NEXT.md`,**禁止**直接改 `src/`
|
|
31
|
+
|
|
25
32
|
## 用户确认点(无需口令)
|
|
26
33
|
|
|
27
34
|
1. 更新 `03` → 生成 `01/02/04`
|