sdd-flow-kit 1.0.16 → 1.0.22
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 +22 -3
- package/dist/cli.js +24 -4
- package/dist/core/inferProject.js +152 -0
- package/dist/core/opsxDelivery.js +248 -0
- package/dist/postinstall.js +57 -39
- package/dist/steps/doctor.js +24 -1
- package/dist/steps/setupProject.js +24 -68
- package/dist/steps/startFlow.js +15 -6
- package/package.json +1 -1
- package/src/templates/artifacts/01-adi-doc-skill-/346/214/207/345/274/225.template.md +24 -10
- package/src/templates/artifacts/06-agent-skill.template.md +30 -8
- package/src/templates/skills/confluence-doc.py.template +248 -66
- package/src/templates/skills/doc-skill.SKILL.md.template +13 -4
|
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.setupProject = setupProject;
|
|
7
7
|
const node_child_process_1 = require("node:child_process");
|
|
8
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
9
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const
|
|
9
|
+
const promises_1 = require("node:readline/promises");
|
|
11
10
|
const node_process_1 = __importDefault(require("node:process"));
|
|
12
11
|
const fs_1 = require("../core/fs");
|
|
12
|
+
const opsxDelivery_1 = require("../core/opsxDelivery");
|
|
13
13
|
const templates_1 = require("../core/templates");
|
|
14
14
|
const EDITOR_CHOICES = ["claude-code", "cursor", "codex", "openclaw"];
|
|
15
15
|
const PROJECT_CHOICES = ["OMS", "ADI", "欢盟", "AD Tools"];
|
|
@@ -47,7 +47,7 @@ function listLocalBranches(projectRoot) {
|
|
|
47
47
|
async function askAgent(defaultAgent) {
|
|
48
48
|
if (!node_process_1.default.stdin.isTTY)
|
|
49
49
|
return defaultAgent;
|
|
50
|
-
const rl = (0,
|
|
50
|
+
const rl = (0, promises_1.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
|
|
51
51
|
const prompt = [
|
|
52
52
|
"请选择开发环境:",
|
|
53
53
|
"1) claude-code (default)",
|
|
@@ -68,7 +68,7 @@ async function askAgent(defaultAgent) {
|
|
|
68
68
|
async function askBranch(defaultBranch) {
|
|
69
69
|
if (!node_process_1.default.stdin.isTTY)
|
|
70
70
|
return defaultBranch;
|
|
71
|
-
const rl = (0,
|
|
71
|
+
const rl = (0, promises_1.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
|
|
72
72
|
const answer = (await rl.question(`请输入开发分支(默认 ${defaultBranch}):\n`)).trim();
|
|
73
73
|
rl.close();
|
|
74
74
|
return answer || defaultBranch;
|
|
@@ -76,7 +76,7 @@ async function askBranch(defaultBranch) {
|
|
|
76
76
|
async function askProjectKind(defaultKind) {
|
|
77
77
|
if (!node_process_1.default.stdin.isTTY)
|
|
78
78
|
return defaultKind;
|
|
79
|
-
const rl = (0,
|
|
79
|
+
const rl = (0, promises_1.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
|
|
80
80
|
const prompt = [
|
|
81
81
|
"请选择当前项目类型(用于生成 PRD 下载 skill):",
|
|
82
82
|
"1) OMS (default)",
|
|
@@ -94,50 +94,6 @@ async function askProjectKind(defaultKind) {
|
|
|
94
94
|
return defaultKind;
|
|
95
95
|
return PROJECT_CHOICES[idx - 1];
|
|
96
96
|
}
|
|
97
|
-
function isEditorEnvironmentReady(projectRoot, agent) {
|
|
98
|
-
const checks = {
|
|
99
|
-
cursor: ["openspec", ".cursor"],
|
|
100
|
-
"claude-code": ["openspec", ".claude"],
|
|
101
|
-
codex: ["openspec", ".codex"],
|
|
102
|
-
openclaw: ["openspec", ".openclaw"],
|
|
103
|
-
custom: ["openspec"],
|
|
104
|
-
};
|
|
105
|
-
const targetPaths = checks[agent] ?? ["openspec"];
|
|
106
|
-
return Promise.all(targetPaths.map(async (p) => {
|
|
107
|
-
try {
|
|
108
|
-
await promises_1.default.access(node_path_1.default.join(projectRoot, p));
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
})).then((arr) => arr.every(Boolean));
|
|
115
|
-
}
|
|
116
|
-
function buildSetupCommand(projectRoot, agent) {
|
|
117
|
-
const baseArgs = ["@lixin5257xxx/opsx-workflow", "setup"];
|
|
118
|
-
if (agent === "cursor") {
|
|
119
|
-
return {
|
|
120
|
-
cmd: "npx",
|
|
121
|
-
args: [...baseArgs, "--editor", "cursor", "--project", projectRoot, "-y"],
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (agent === "claude-code") {
|
|
125
|
-
return {
|
|
126
|
-
cmd: "npx",
|
|
127
|
-
args: [...baseArgs, "--editor", "claude-code", "-y"],
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
if (agent === "openclaw") {
|
|
131
|
-
return {
|
|
132
|
-
cmd: "npx",
|
|
133
|
-
args: [...baseArgs, "--editor", "openclaw", "-y"],
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
cmd: "npx",
|
|
138
|
-
args: [...baseArgs, "--editor", "codex", "-y"],
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
97
|
function ensureBranch(projectRoot, branch) {
|
|
142
98
|
const branches = listLocalBranches(projectRoot);
|
|
143
99
|
if (branches.includes(branch)) {
|
|
@@ -241,7 +197,8 @@ function getOpsxPathsByAgent(agent) {
|
|
|
241
197
|
}
|
|
242
198
|
async function installOpsxConfig(projectRoot, agent) {
|
|
243
199
|
const target = node_path_1.default.join(projectRoot, ".opsx", "config.json");
|
|
244
|
-
const
|
|
200
|
+
const resolvedAgent = await (0, opsxDelivery_1.resolveOpsxAgent)(projectRoot, agent);
|
|
201
|
+
const paths = getOpsxPathsByAgent(resolvedAgent);
|
|
245
202
|
const config = {
|
|
246
203
|
version: 1,
|
|
247
204
|
editor: paths.editor,
|
|
@@ -282,34 +239,29 @@ async function setupProject(options) {
|
|
|
282
239
|
else {
|
|
283
240
|
branchResult = ensureBranch(options.projectRoot, selectedBranch);
|
|
284
241
|
}
|
|
285
|
-
let environmentReady = await
|
|
242
|
+
let environmentReady = await (0, opsxDelivery_1.isOpsxApplyReady)(options.projectRoot);
|
|
286
243
|
let setupExecuted = false;
|
|
287
244
|
let skillInstalled = false;
|
|
288
245
|
let docsSkillInstalled = false;
|
|
289
246
|
let opsxConfigInstalled = false;
|
|
290
247
|
if (options.light) {
|
|
291
248
|
plannedActions.push("light mode: skipped npx @lixin5257xxx/opsx-workflow setup");
|
|
249
|
+
if (!environmentReady) {
|
|
250
|
+
plannedActions.push("opsx apply stack incomplete → run: npx sdd-flow-kit ensure-opsx -y (before /opsx-apply)");
|
|
251
|
+
}
|
|
292
252
|
}
|
|
293
253
|
else if (!environmentReady) {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
env: node_process_1.default.env,
|
|
303
|
-
});
|
|
304
|
-
if (ret.status !== 0) {
|
|
305
|
-
throw new Error(`环境安装失败:${cmd} ${args.join(" ")}`);
|
|
306
|
-
}
|
|
307
|
-
setupExecuted = true;
|
|
308
|
-
environmentReady = await isEditorEnvironmentReady(options.projectRoot, selectedAgent);
|
|
309
|
-
}
|
|
254
|
+
const ensured = await (0, opsxDelivery_1.ensureOpsxWorkflow)({
|
|
255
|
+
projectRoot: options.projectRoot,
|
|
256
|
+
agent: selectedAgent,
|
|
257
|
+
dryRun: options.dryRun,
|
|
258
|
+
});
|
|
259
|
+
plannedActions.push(...ensured.plannedActions);
|
|
260
|
+
setupExecuted = ensured.workflowInstalled;
|
|
261
|
+
environmentReady = ensured.ready;
|
|
310
262
|
}
|
|
311
263
|
else if (options.dryRun) {
|
|
312
|
-
plannedActions.push("
|
|
264
|
+
plannedActions.push("opsx apply stack already ready");
|
|
313
265
|
}
|
|
314
266
|
if (options.dryRun) {
|
|
315
267
|
for (const skillPath of allSkillInstallPaths(options.projectRoot)) {
|
|
@@ -328,6 +280,10 @@ async function setupProject(options) {
|
|
|
328
280
|
docsSkillInstalled = true;
|
|
329
281
|
await installOpsxConfig(options.projectRoot, selectedAgent);
|
|
330
282
|
opsxConfigInstalled = true;
|
|
283
|
+
const repaired = await (0, opsxDelivery_1.repairOpsxDeliveryScripts)(options.projectRoot);
|
|
284
|
+
if (repaired) {
|
|
285
|
+
plannedActions.push("repaired package.json opsx:delivery-pipeline scripts (poquan → opsx)");
|
|
286
|
+
}
|
|
331
287
|
}
|
|
332
288
|
const summary = [
|
|
333
289
|
"# SETUP-SUMMARY",
|
package/dist/steps/startFlow.js
CHANGED
|
@@ -9,6 +9,7 @@ const promises_1 = __importDefault(require("node:fs/promises"));
|
|
|
9
9
|
const utils_1 = require("../core/utils");
|
|
10
10
|
const fs_1 = require("../core/fs");
|
|
11
11
|
const templates_1 = require("../core/templates");
|
|
12
|
+
const inferProject_1 = require("../core/inferProject");
|
|
12
13
|
function renderTemplate(template, vars) {
|
|
13
14
|
let out = template;
|
|
14
15
|
for (const [k, v] of Object.entries(vars)) {
|
|
@@ -22,8 +23,16 @@ async function startFlowScaffold(options) {
|
|
|
22
23
|
const outputRoot = node_path_1.default.join(options.projectRoot, "openspec", "PRD", runId);
|
|
23
24
|
const sourceDir = node_path_1.default.join(outputRoot, "source");
|
|
24
25
|
await promises_1.default.mkdir(sourceDir, { recursive: true });
|
|
25
|
-
// 1) Step1:
|
|
26
|
-
const
|
|
26
|
+
// 1) Step1: doc-skill 指引(按产品解析脚本路径,支持 monorepo frontend)
|
|
27
|
+
const kind = (0, inferProject_1.productToKind)(options.product);
|
|
28
|
+
const docSkill = await (0, inferProject_1.resolveDocSkillDir)(options.projectRoot, kind);
|
|
29
|
+
const step1Template = await (0, templates_1.loadTemplateText)("artifacts/01-adi-doc-skill-指引.template.md");
|
|
30
|
+
const step1 = renderTemplate(step1Template, {
|
|
31
|
+
PRODUCT: options.product,
|
|
32
|
+
PRODUCT_PREFIX: inferProject_1.PRODUCT_PREFIX_BY_KIND[kind],
|
|
33
|
+
DOC_SKILL_REL: docSkill.relFromRoot.replace(/\\/g, "/"),
|
|
34
|
+
VERSION_EXAMPLE: options.version,
|
|
35
|
+
});
|
|
27
36
|
await (0, fs_1.writeTextFileEnsuredDir)(node_path_1.default.join(outputRoot, "01-获取需求文档指引.md"), step1);
|
|
28
37
|
// source 占位:让第 2 步提示词在任何工具里都能直接读
|
|
29
38
|
await (0, fs_1.writeTextFileEnsuredDir)(node_path_1.default.join(sourceDir, "PRD.md"), [
|
|
@@ -106,10 +115,10 @@ async function startFlowScaffold(options) {
|
|
|
106
115
|
"产出在 `openspec/changes/<change-name>/`;在 `05-openspec-提案草稿.md` 记录 change 名称。",
|
|
107
116
|
"门禁:仅当 03 已闭环且 01/02/04 完成后执行。",
|
|
108
117
|
"",
|
|
109
|
-
"## Step 6
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
118
|
+
"## Step 6:Apply + TDD + Playwright(阶段 D)",
|
|
119
|
+
"依次执行 skill:`openspec-apply-change` → `tdd-script` → `openspec-superpowers-pipeline` → `opsx-dev-delivery-pipeline`。",
|
|
120
|
+
"仓库根执行:`pnpm run opsx:delivery-pipeline -- <change-name>`(须 exit 0)。",
|
|
121
|
+
"禁止跳过 TDD / Playwright 裸写代码收尾。详见 `.cursor/skills/sdd-flow-kit/SKILL.md` 阶段 D。",
|
|
113
122
|
].join("\n"));
|
|
114
123
|
return {
|
|
115
124
|
runId,
|
package/package.json
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
# 01-获取需求文档(
|
|
1
|
+
# 01-获取需求文档({{PRODUCT}} / {{DOC_SKILL_REL}})
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 必须执行(不要只用 AI 猜,要跑脚本)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
1. 使用你当前 AI 工具(Cursor / Claude Code / Codex / OpenClaw)调用现有的 `adi-doc-skill`。
|
|
7
|
-
2. 命令/动作建议:让工具“获取 ADI 2.3.2 需求文档”(或等价的触发词),并把下载结果写入本目录:
|
|
8
|
-
- `source/PRD.md`(PRD 主内容)
|
|
9
|
-
- `source/接口文档.md`(接口文档,如有)
|
|
5
|
+
1. 在**仓库根**(当前工作目录,可能是 `frontend/` 或 monorepo 根)执行:
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
```bash
|
|
8
|
+
cd {{DOC_SKILL_REL}}
|
|
9
|
+
export DOC_PRODUCT_PREFIX="{{PRODUCT_PREFIX}}"
|
|
10
|
+
python3 confluence-doc.py {{VERSION_EXAMPLE}}
|
|
11
|
+
```
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
版本号可传:`2.3.2`、`V2.3.2`、`ADI_V2.3.2`、`ADI-V2.3.2`(脚本会归一化)。
|
|
15
14
|
|
|
15
|
+
2. 脚本会依次搜索:`{{PRODUCT_PREFIX}}-V*`、`{{PRODUCT_PREFIX}}_V*`、`{{PRODUCT_PREFIX}} V*`、`V*` 等。
|
|
16
|
+
|
|
17
|
+
3. 成功后把生成的 `docs/*.md` **主文档**复制/回填到本 run 目录:
|
|
18
|
+
- `source/PRD.md`
|
|
19
|
+
|
|
20
|
+
## 路径说明
|
|
21
|
+
|
|
22
|
+
- 推荐脚本目录:`{{DOC_SKILL_REL}}`(已向上解析,可能是 `docs/adi-doc-skill` 或 `../docs/adi-doc-skill`)
|
|
23
|
+
- **禁止**在 ADI 需求中使用 `docs/oms-doc-skill`(会搜 OMS 文档导致「未找到」)
|
|
24
|
+
|
|
25
|
+
## 失败排查
|
|
26
|
+
|
|
27
|
+
- 输出 `❌ 未找到`:到 Confluence 确认页面标题是否含 `{{PRODUCT_PREFIX}}-V2.3.2` 或 `{{PRODUCT_PREFIX}}_V2.3.2`
|
|
28
|
+
- `playwright` 未安装:`pip install playwright && playwright install chromium`
|
|
29
|
+
- 仍失败:在仓库根执行 `npx sdd-flow-kit install --project {{PRODUCT}} -y` 后重试
|
|
@@ -41,7 +41,10 @@ description: 开发某版本需求、SDD 流程、待确认问题澄清、用户
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
43
|
### 阶段 A — Step1 + Step2(必须可停顿)
|
|
44
|
-
1. **Step1
|
|
44
|
+
1. **Step1**:打开 `01-获取需求文档指引.md`,**在终端执行其中的 `confluence-doc.py` 命令**(不要跳过)。
|
|
45
|
+
- ADI 必须用 `docs/adi-doc-skill` 或上级 `../docs/adi-doc-skill`,**禁止**用 `docs/oms-doc-skill`。
|
|
46
|
+
- 用户说「开发 ADI_V2.3.2」时,脚本参数传 `2.3.2` 或 `ADI_V2.3.2` 均可。
|
|
47
|
+
- 将下载的 markdown 回填 `source/PRD.md`。
|
|
45
48
|
2. **Step2**:执行 `02-需求分析提示词.md`(引用 `docs/` 下已下载的 PRD)
|
|
46
49
|
|
|
47
50
|
**门禁(Step2)**
|
|
@@ -97,13 +100,32 @@ description: 开发某版本需求、SDD 流程、待确认问题澄清、用户
|
|
|
97
100
|
|
|
98
101
|
---
|
|
99
102
|
|
|
100
|
-
### 阶段 D —
|
|
101
|
-
**触发条件**:阶段 C 的 openspec
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
### 阶段 D — Apply + TDD + Playwright 交付(禁止裸写代码收尾)
|
|
104
|
+
**触发条件**:阶段 C 的 openspec 提案已 apply-ready,且用户同意实现(如「开始 apply」)。
|
|
105
|
+
|
|
106
|
+
**严禁**:跳过 skill 链、不按 `tasks.md` TDD、不跑 delivery-pipeline 就改 `src/` 并宣称完成。
|
|
107
|
+
|
|
108
|
+
**必须按顺序执行(以仓库根 `AGENTS.md` 与 `.cursor/skills/` 为准):**
|
|
109
|
+
|
|
110
|
+
0. **环境门禁(缺则自动安装,不可跳过)** — 在仓库根执行:
|
|
111
|
+
```bash
|
|
112
|
+
npx sdd-flow-kit ensure-opsx --agent cursor -y
|
|
113
|
+
```
|
|
114
|
+
- 检测:openspec、TDD/Playwright skills、`opsx:delivery-pipeline` 脚本、`AGENTS.md` 等。
|
|
115
|
+
- 若 FAIL:上述命令会运行 `npx @lixin5257xxx/opsx-workflow setup ...` 安装工作流;**exit 0 前不得开始改 `src/`**。
|
|
116
|
+
1. 读取 `AGENTS.md`、`docs/agent-workflow.md`、`.opsx/config.json`。
|
|
117
|
+
2. 绑定 change 名:写入 `.opsx-active-change`(一行 kebab-case),或 `export OPSX_ACTIVE_CHANGE=<name>`。
|
|
118
|
+
3. **依次读取并执行**(不可省略):
|
|
119
|
+
- `.cursor/skills/openspec-apply-change/SKILL.md` — `openspec instructions apply --change "<name>" --json`
|
|
120
|
+
- `.cursor/skills/tdd-script/SKILL.md` — **每项 task 先测后实现**,并勾选 `tasks.md` 的 `- [x]`
|
|
121
|
+
- `.cursor/skills/openspec-superpowers-pipeline/SKILL.md` — Delta Spec / 代码质量审查门控
|
|
122
|
+
- `.cursor/skills/opsx-dev-delivery-pipeline/SKILL.md` — 机械交付脚本(TDD 门禁 → validate → **Playwright** → archive)
|
|
123
|
+
4. 在**仓库根**(含 `package.json` 的目录)执行:
|
|
124
|
+
```bash
|
|
125
|
+
pnpm run opsx:delivery-pipeline -- <change-name>
|
|
126
|
+
```
|
|
127
|
+
脚本路径须为:`.cursor/skills/opsx-dev-delivery-pipeline/scripts/opsx-delivery-pipeline.sh`(若 `package.json` 仍指向 `poquan-*`,先运行 `npx sdd-flow-kit install -y` 修复)。
|
|
128
|
+
5. **完成判定**:`tasks.md` 全部 `- [x]` **且** 上一条命令 **exit 0**。否则不得宣称 SDD 流程结束。
|
|
107
129
|
|
|
108
130
|
---
|
|
109
131
|
|