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.
@@ -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 promises_2 = require("node:readline/promises");
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, promises_2.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
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, promises_2.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
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, promises_2.createInterface)({ input: node_process_1.default.stdin, output: node_process_1.default.stdout });
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 paths = getOpsxPathsByAgent(agent);
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 isEditorEnvironmentReady(options.projectRoot, selectedAgent);
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 { cmd, args } = buildSetupCommand(options.projectRoot, selectedAgent);
295
- if (options.dryRun) {
296
- plannedActions.push(`would run: ${cmd} ${args.join(" ")}`);
297
- }
298
- else {
299
- const ret = (0, node_child_process_1.spawnSync)(cmd, args, {
300
- cwd: options.projectRoot,
301
- stdio: "inherit",
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("environment already ready, no setup needed");
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",
@@ -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: adi-doc-skill 指引
26
- const step1 = await (0, templates_1.loadTemplateText)("artifacts/01-adi-doc-skill-指引.template.md");
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:/opsx-apply 实现收尾",
110
- "提案 apply-ready 后,按 `.opsx/config.json` 执行 `/opsx-apply` 对应 change 的 apply 与 delivery-pipeline",
111
- "必须在当前仓库本地 OPSX 配置下执行,禁止跨仓 fallback。",
112
- "门禁:delivery-pipeline exit 0 前禁止宣称流程结束。",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "sdd-flow-kit",
3
- "version": "1.0.16",
3
+ "version": "1.0.22",
4
4
  "private": false,
5
5
  "description": "Cross-agent SDD automated development workflow kit (ADI/ADI-like).",
6
6
  "license": "MIT",
@@ -1,15 +1,29 @@
1
- # 01-获取需求文档(adi-doc-skill 指引)
1
+ # 01-获取需求文档({{PRODUCT}} / {{DOC_SKILL_REL}})
2
2
 
3
- 你的步骤 1 目标:从 Confluence 拉取指定需求版本的 Markdown,并落到本目录的 `source/` 下。
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
- - 你应该能在 `source/PRD.md` 看到完整 PRD markdown
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
- (如果你希望自动回填,请在 adapter 中实现对应能力。)
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**:按 `01-获取需求文档指引.md` 拉取 PRD,回填 `source/PRD.md`(及可选接口文档)
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 — `/opsx-apply`(实现与交付收尾)
101
- **触发条件**:阶段 C 的 openspec 提案已生成且状态为 apply-ready
102
-
103
- **必须执行:**
104
- 1. 按总编排 skill(`.opsx/config.json` 中 `deliveryPipeline` 指向的 skill)执行 **`/opsx-apply`**
105
- 2. 在当前仓库根目录跑 delivery-pipeline(如 `pnpm run opsx:delivery-pipeline -- <change-name>`)
106
- 3. **仅在 pipeline 成功(exit 0)后** 才可宣称本需求开发流程结束
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