sdd-flow-kit 1.0.22 → 1.0.27
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 +21 -3
- package/dist/adapters/index.js +13 -12
- package/dist/cli.js +29 -28
- package/dist/core/fs.js +3 -3
- package/dist/core/inferProject.js +22 -20
- package/dist/core/nodeCompat.js +35 -0
- package/dist/core/opsxDelivery.js +26 -25
- package/dist/core/readlinePrompt.js +51 -0
- package/dist/core/templates.js +4 -4
- package/dist/core/utils.js +8 -1
- package/dist/postinstall.js +10 -9
- package/dist/steps/doctor.js +13 -6
- package/dist/steps/invokeFlow.js +2 -1
- package/dist/steps/runFlow.js +3 -3
- package/dist/steps/setupProject.js +34 -31
- package/dist/steps/startFlow.js +26 -22
- package/package.json +4 -1
- package/src/templates/artifacts/05-openspec-/346/217/220/346/241/210/350/215/211/347/250/277.template.md +2 -1
- package/src/templates/artifacts/06-agent-skill.template.md +25 -21
- package/src/templates/artifacts/06-opsx-auto-chain.template.md +38 -0
- package/src/templates/prompts/02-sdd-loop-prompt.md +4 -2
- package/src/templates/rules/sdd-no-implement-until-apply.mdc.template +9 -4
package/README.md
CHANGED
|
@@ -11,6 +11,23 @@
|
|
|
11
11
|
软件架构说明
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
#### Node 版本(14.16 / 18 / 20 均可)
|
|
15
|
+
|
|
16
|
+
| 组件 | 要求 |
|
|
17
|
+
|------|------|
|
|
18
|
+
| **sdd-flow-kit** CLI(`install` / `run` / `doctor` / `postinstall`) | **Node >= 14.16.0**(`engines` 已声明;兼容老 CI/内网机) |
|
|
19
|
+
| **openspec** CLI、`opsx-workflow` | 以各自包文档为准;建议 **18+** |
|
|
20
|
+
| 目标业务仓(如 adInsight `frontend`) | 按该仓 `package.json` engines,与 kit 独立 |
|
|
21
|
+
|
|
22
|
+
安装后建议执行:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node -v # 应 >= v14.16.0
|
|
26
|
+
npx sdd-flow-kit doctor --agent cursor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
若 `doctor` 报 `node-version` 失败,请升级到 14.16+,或在该仓使用 `.nvmrc` 锁定版本。
|
|
30
|
+
|
|
14
31
|
#### 安装教程
|
|
15
32
|
|
|
16
33
|
1. 进入你要开发的项目目录
|
|
@@ -59,10 +76,11 @@
|
|
|
59
76
|
##### 需求澄清:用户无需背提示词
|
|
60
77
|
|
|
61
78
|
`install` 会写入 `.cursor/rules/sdd-flow-kit-gates.mdc` 与触发 Skill。
|
|
62
|
-
澄清阶段你**直接粘贴确认点**(编号列表即可),AI
|
|
63
|
-
|
|
79
|
+
澄清阶段你**直接粘贴确认点**(编号列表即可),AI 先进入文档模式更新 `openspec/PRD/**`。
|
|
80
|
+
**默认**:03 闭环并生成 01/02/04 后,**同一回合自动** `/opsx-propose`(01+02+UI)→ `/opsx-apply`,无需再说「继续」。
|
|
81
|
+
说「只提案」或「先别写代码」可停在提案阶段;设 `autoChainOpsx: false` 可恢复人工分步。
|
|
64
82
|
|
|
65
|
-
|
|
83
|
+
阶段口语(可选,用于断点续跑):`继续` / `做提案` / `开始 apply`。
|
|
66
84
|
|
|
67
85
|
##### `/opsx-apply` 与 TDD + Playwright 检查机制
|
|
68
86
|
|
package/dist/adapters/index.js
CHANGED
|
@@ -4,12 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getAdapter = getAdapter;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const process_1 = __importDefault(require("process"));
|
|
10
10
|
const fs_1 = require("../core/fs");
|
|
11
11
|
function hasCommand(cmd) {
|
|
12
|
-
const result = (0,
|
|
12
|
+
const result = (0, child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" });
|
|
13
13
|
return result.status === 0;
|
|
14
14
|
}
|
|
15
15
|
function createManualAdapter(name, displayName, bins) {
|
|
@@ -25,15 +25,16 @@ function createManualAdapter(name, displayName, bins) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
async function runClaudeFlow(args) {
|
|
28
|
+
var _a;
|
|
28
29
|
// 默认关闭“二次调用 claude”自动执行,避免在 AI 会话内递归触发导致静默挂起。
|
|
29
30
|
// 仅在显式开启时执行:SDD_FLOW_KIT_ENABLE_CLAUDE_AUTORUN=1
|
|
30
|
-
if (
|
|
31
|
+
if (process_1.default.env.SDD_FLOW_KIT_ENABLE_CLAUDE_AUTORUN !== "1") {
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
34
|
const hasClaude = hasCommand("claude");
|
|
34
35
|
if (!hasClaude)
|
|
35
36
|
return false;
|
|
36
|
-
const sddPrompt = await (0, fs_1.readFileIfExists)(
|
|
37
|
+
const sddPrompt = await (0, fs_1.readFileIfExists)(path_1.default.join(args.outputRoot, "02-需求分析提示词.md"));
|
|
37
38
|
if (!sddPrompt)
|
|
38
39
|
return false;
|
|
39
40
|
const prompt = [
|
|
@@ -42,23 +43,23 @@ async function runClaudeFlow(args) {
|
|
|
42
43
|
"1) 01-获取需求文档指引:回填 source/PRD.md(及接口文档,如有)。",
|
|
43
44
|
"2) 02-需求分析:若矛盾点未闭合,仅更新 03-待确认问题清单.md 并停止;用户确认后再生成 01/02/闭环03。",
|
|
44
45
|
"3) 用户确认后补全 04-技术文档草稿.md。",
|
|
45
|
-
"4)
|
|
46
|
-
"5)
|
|
46
|
+
"4) 03 闭环后默认同一回合:/opsx-propose(01+02+UI)→ /opsx-apply → delivery-pipeline exit 0(见 06-opsx-自动串联指引.md)。",
|
|
47
|
+
"5) 仅当用户说「只提案」或 autoChainOpsx=false 时,在 propose 后暂停等待 apply。",
|
|
47
48
|
"完成后输出一句:SDD flow completed",
|
|
48
49
|
"",
|
|
49
50
|
"以下是第2步提示词:",
|
|
50
51
|
sddPrompt,
|
|
51
52
|
].join("\n");
|
|
52
|
-
const commandResult = (0,
|
|
53
|
+
const commandResult = (0, child_process_1.spawnSync)("claude", ["-p", prompt], {
|
|
53
54
|
cwd: args.outputRoot,
|
|
54
55
|
encoding: "utf8",
|
|
55
|
-
env:
|
|
56
|
+
env: process_1.default.env,
|
|
56
57
|
});
|
|
57
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
58
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(args.outputRoot, "AUTO-RUN-LOG.md"), [
|
|
58
59
|
"# AUTO-RUN-LOG",
|
|
59
60
|
"",
|
|
60
61
|
`agent: claude-code`,
|
|
61
|
-
`status: ${commandResult.status
|
|
62
|
+
`status: ${(_a = commandResult.status) !== null && _a !== void 0 ? _a : "unknown"}`,
|
|
62
63
|
"",
|
|
63
64
|
"## stdout",
|
|
64
65
|
commandResult.stdout || "",
|
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const process_1 = __importDefault(require("process"));
|
|
9
9
|
const startFlow_1 = require("./steps/startFlow");
|
|
10
10
|
const runFlow_1 = require("./steps/runFlow");
|
|
11
11
|
const utils_1 = require("./core/utils");
|
|
@@ -14,10 +14,10 @@ const doctor_1 = require("./steps/doctor");
|
|
|
14
14
|
const invokeFlow_1 = require("./steps/invokeFlow");
|
|
15
15
|
const ALLOWED_AGENTS = ["cursor", "claude-code", "codex", "openclaw", "custom"];
|
|
16
16
|
function getArgValue(flag) {
|
|
17
|
-
const idx =
|
|
17
|
+
const idx = process_1.default.argv.indexOf(flag);
|
|
18
18
|
if (idx === -1)
|
|
19
19
|
return null;
|
|
20
|
-
const v =
|
|
20
|
+
const v = process_1.default.argv[idx + 1];
|
|
21
21
|
if (!v)
|
|
22
22
|
return null;
|
|
23
23
|
return v;
|
|
@@ -61,7 +61,8 @@ function printHelp() {
|
|
|
61
61
|
].join("\n"));
|
|
62
62
|
}
|
|
63
63
|
async function main() {
|
|
64
|
-
|
|
64
|
+
var _a, _b, _c, _d;
|
|
65
|
+
const cmd = process_1.default.argv[2];
|
|
65
66
|
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
66
67
|
printHelp();
|
|
67
68
|
return;
|
|
@@ -74,23 +75,23 @@ async function main() {
|
|
|
74
75
|
cmd !== "ensure-opsx" &&
|
|
75
76
|
cmd !== "invoke") {
|
|
76
77
|
printHelp();
|
|
77
|
-
|
|
78
|
+
process_1.default.exitCode = 1;
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
const product = getArgValue("--product");
|
|
81
82
|
const version = getArgValue("--version");
|
|
82
|
-
const projectRoot = getArgValue("--projectRoot")
|
|
83
|
-
const branch = getArgValue("--branch")
|
|
84
|
-
const yes =
|
|
85
|
-
const dryRun =
|
|
86
|
-
const planJson =
|
|
87
|
-
const agentValue = getArgValue("--agent")
|
|
88
|
-
const projectKind = (getArgValue("--project")
|
|
83
|
+
const projectRoot = (_a = getArgValue("--projectRoot")) !== null && _a !== void 0 ? _a : process_1.default.cwd();
|
|
84
|
+
const branch = (_b = getArgValue("--branch")) !== null && _b !== void 0 ? _b : undefined;
|
|
85
|
+
const yes = process_1.default.argv.includes("-y");
|
|
86
|
+
const dryRun = process_1.default.argv.includes("--dry-run");
|
|
87
|
+
const planJson = process_1.default.argv.includes("--plan-json");
|
|
88
|
+
const agentValue = (_c = getArgValue("--agent")) !== null && _c !== void 0 ? _c : "claude-code";
|
|
89
|
+
const projectKind = ((_d = getArgValue("--project")) !== null && _d !== void 0 ? _d : "OMS");
|
|
89
90
|
if (!ALLOWED_AGENTS.includes(agentValue)) {
|
|
90
91
|
// eslint-disable-next-line no-console
|
|
91
92
|
console.error(`不支持的 --agent: ${agentValue}`);
|
|
92
93
|
printHelp();
|
|
93
|
-
|
|
94
|
+
process_1.default.exitCode = 1;
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
96
97
|
const agent = agentValue;
|
|
@@ -98,13 +99,13 @@ async function main() {
|
|
|
98
99
|
if (!allowedProjects.includes(projectKind)) {
|
|
99
100
|
// eslint-disable-next-line no-console
|
|
100
101
|
console.error(`不支持的 --project: ${projectKind}(可选:${allowedProjects.join(" / ")})`);
|
|
101
|
-
|
|
102
|
+
process_1.default.exitCode = 1;
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
105
|
if (cmd === "doctor" || cmd === "ensure-opsx") {
|
|
105
|
-
const fix = cmd === "ensure-opsx" ||
|
|
106
|
+
const fix = cmd === "ensure-opsx" || process_1.default.argv.includes("--fix");
|
|
106
107
|
const doctor = await (0, doctor_1.runDoctor)({
|
|
107
|
-
projectRoot:
|
|
108
|
+
projectRoot: path_1.default.resolve(projectRoot),
|
|
108
109
|
agent,
|
|
109
110
|
fix,
|
|
110
111
|
dryRun,
|
|
@@ -117,11 +118,11 @@ async function main() {
|
|
|
117
118
|
}
|
|
118
119
|
if (planJson) {
|
|
119
120
|
// eslint-disable-next-line no-console
|
|
120
|
-
console.log(JSON.stringify({ command: "doctor", projectRoot:
|
|
121
|
+
console.log(JSON.stringify({ command: "doctor", projectRoot: path_1.default.resolve(projectRoot), result: doctor }, null, 2));
|
|
121
122
|
}
|
|
122
123
|
if (cmd === "ensure-opsx" && !dryRun) {
|
|
123
124
|
await (0, setupProject_1.setupProject)({
|
|
124
|
-
projectRoot:
|
|
125
|
+
projectRoot: path_1.default.resolve(projectRoot),
|
|
125
126
|
agent,
|
|
126
127
|
yes: true,
|
|
127
128
|
light: true,
|
|
@@ -129,12 +130,12 @@ async function main() {
|
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
132
|
if (!doctor.ok)
|
|
132
|
-
|
|
133
|
+
process_1.default.exitCode = 1;
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
136
|
if (cmd === "setup" || cmd === "install") {
|
|
136
137
|
const result = await (0, setupProject_1.setupProject)({
|
|
137
|
-
projectRoot:
|
|
138
|
+
projectRoot: path_1.default.resolve(projectRoot),
|
|
138
139
|
agent,
|
|
139
140
|
branch,
|
|
140
141
|
yes,
|
|
@@ -151,7 +152,7 @@ async function main() {
|
|
|
151
152
|
// eslint-disable-next-line no-console
|
|
152
153
|
console.log(JSON.stringify({
|
|
153
154
|
command: cmd,
|
|
154
|
-
projectRoot:
|
|
155
|
+
projectRoot: path_1.default.resolve(projectRoot),
|
|
155
156
|
dryRun,
|
|
156
157
|
result,
|
|
157
158
|
}, null, 2));
|
|
@@ -160,18 +161,18 @@ async function main() {
|
|
|
160
161
|
}
|
|
161
162
|
if (!product || !version) {
|
|
162
163
|
if (cmd === "invoke") {
|
|
163
|
-
const raw =
|
|
164
|
+
const raw = process_1.default.argv
|
|
164
165
|
.slice(3)
|
|
165
166
|
.filter((token) => !token.startsWith("--") && token !== "-y")
|
|
166
167
|
.join(" ")
|
|
167
168
|
.trim();
|
|
168
169
|
const fromFlag = getArgValue("--text");
|
|
169
|
-
const text = (fromFlag
|
|
170
|
+
const text = (fromFlag !== null && fromFlag !== void 0 ? fromFlag : raw).trim();
|
|
170
171
|
const parsed = (0, invokeFlow_1.parseInvokeText)(text);
|
|
171
172
|
if (!parsed) {
|
|
172
173
|
throw new Error("invoke 解析失败,请使用:开发 ADI 2.3.2 需求");
|
|
173
174
|
}
|
|
174
|
-
const resolvedRoot =
|
|
175
|
+
const resolvedRoot = path_1.default.resolve(projectRoot);
|
|
175
176
|
const options = (0, invokeFlow_1.toRunOptions)({
|
|
176
177
|
parsed,
|
|
177
178
|
projectRoot: resolvedRoot,
|
|
@@ -190,10 +191,10 @@ async function main() {
|
|
|
190
191
|
return;
|
|
191
192
|
}
|
|
192
193
|
printHelp();
|
|
193
|
-
|
|
194
|
+
process_1.default.exitCode = 1;
|
|
194
195
|
return;
|
|
195
196
|
}
|
|
196
|
-
const resolvedRoot =
|
|
197
|
+
const resolvedRoot = path_1.default.resolve(projectRoot);
|
|
197
198
|
const runId = `${(0, utils_1.formatYYYYMMDD)(new Date())}-${product}-${version}`;
|
|
198
199
|
if (cmd === "start") {
|
|
199
200
|
await (0, startFlow_1.startFlowScaffold)({
|
|
@@ -243,5 +244,5 @@ async function main() {
|
|
|
243
244
|
main().catch((e) => {
|
|
244
245
|
// eslint-disable-next-line no-console
|
|
245
246
|
console.error(e);
|
|
246
|
-
|
|
247
|
+
process_1.default.exitCode = 1;
|
|
247
248
|
});
|
package/dist/core/fs.js
CHANGED
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.readFileIfExists = readFileIfExists;
|
|
7
7
|
exports.writeTextFileEnsuredDir = writeTextFileEnsuredDir;
|
|
8
|
-
const promises_1 = __importDefault(require("
|
|
9
|
-
const
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
10
|
async function readFileIfExists(filePath) {
|
|
11
11
|
try {
|
|
12
12
|
return await promises_1.default.readFile(filePath, "utf8");
|
|
@@ -22,6 +22,6 @@ async function readFileIfExists(filePath) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
async function writeTextFileEnsuredDir(filePath, content) {
|
|
25
|
-
await promises_1.default.mkdir(
|
|
25
|
+
await promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
26
26
|
await promises_1.default.writeFile(filePath, content, "utf8");
|
|
27
27
|
}
|
|
@@ -7,8 +7,8 @@ exports.PRODUCT_PREFIX_BY_KIND = exports.DOC_SKILL_BY_KIND = exports.ALLOWED_PRO
|
|
|
7
7
|
exports.productToKind = productToKind;
|
|
8
8
|
exports.resolveDocSkillDir = resolveDocSkillDir;
|
|
9
9
|
exports.inferProjectKind = inferProjectKind;
|
|
10
|
-
const promises_1 = __importDefault(require("
|
|
11
|
-
const
|
|
10
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
12
|
exports.ALLOWED_PROJECTS = ["OMS", "ADI", "欢盟", "AD Tools"];
|
|
13
13
|
exports.DOC_SKILL_BY_KIND = {
|
|
14
14
|
ADI: "adi-doc-skill",
|
|
@@ -37,24 +37,24 @@ function productToKind(product) {
|
|
|
37
37
|
/** 从 projectRoot 向上查找 docs/*-doc-skill(适配 frontend 子目录 + 根目录 docs) */
|
|
38
38
|
async function resolveDocSkillDir(projectRoot, kind) {
|
|
39
39
|
const dirName = exports.DOC_SKILL_BY_KIND[kind];
|
|
40
|
-
let cur =
|
|
40
|
+
let cur = path_1.default.resolve(projectRoot);
|
|
41
41
|
for (let i = 0; i < 8; i += 1) {
|
|
42
|
-
const candidate =
|
|
43
|
-
if (await pathExists(
|
|
44
|
-
const rel =
|
|
42
|
+
const candidate = path_1.default.join(cur, "docs", dirName);
|
|
43
|
+
if (await pathExists(path_1.default.join(candidate, "confluence-doc.py"))) {
|
|
44
|
+
const rel = path_1.default.relative(projectRoot, candidate);
|
|
45
45
|
return {
|
|
46
46
|
absPath: candidate,
|
|
47
|
-
relFromRoot: rel.startsWith("..") ? rel :
|
|
47
|
+
relFromRoot: rel.startsWith("..") ? rel : path_1.default.join("docs", dirName),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
const parent =
|
|
50
|
+
const parent = path_1.default.dirname(cur);
|
|
51
51
|
if (parent === cur)
|
|
52
52
|
break;
|
|
53
53
|
cur = parent;
|
|
54
54
|
}
|
|
55
55
|
return {
|
|
56
|
-
absPath:
|
|
57
|
-
relFromRoot:
|
|
56
|
+
absPath: path_1.default.join(projectRoot, "docs", dirName),
|
|
57
|
+
relFromRoot: path_1.default.join("docs", dirName),
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
async function pathExists(filePath) {
|
|
@@ -87,12 +87,12 @@ async function inferFromExistingDocSkill(projectRoot) {
|
|
|
87
87
|
let dir = projectRoot;
|
|
88
88
|
for (let i = 0; i < 8; i += 1) {
|
|
89
89
|
for (const kind of exports.ALLOWED_PROJECTS) {
|
|
90
|
-
const rel =
|
|
91
|
-
if (await pathExists(
|
|
90
|
+
const rel = path_1.default.join(dir, "docs", exports.DOC_SKILL_BY_KIND[kind]);
|
|
91
|
+
if (await pathExists(path_1.default.join(rel, "SKILL.md"))) {
|
|
92
92
|
return kind;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
const parent =
|
|
95
|
+
const parent = path_1.default.dirname(dir);
|
|
96
96
|
if (parent === dir)
|
|
97
97
|
break;
|
|
98
98
|
dir = parent;
|
|
@@ -101,10 +101,10 @@ async function inferFromExistingDocSkill(projectRoot) {
|
|
|
101
101
|
}
|
|
102
102
|
/** 从目录路径(含父级)推断产品类型 */
|
|
103
103
|
function inferFromPathSegments(projectRoot) {
|
|
104
|
-
const resolved =
|
|
105
|
-
const parts = resolved.split(
|
|
104
|
+
const resolved = path_1.default.resolve(projectRoot);
|
|
105
|
+
const parts = resolved.split(path_1.default.sep);
|
|
106
106
|
for (let i = parts.length; i > 0; i -= 1) {
|
|
107
|
-
const segment = parts.slice(0, i).join(
|
|
107
|
+
const segment = parts.slice(0, i).join(path_1.default.sep);
|
|
108
108
|
const hit = matchKindInText(segment);
|
|
109
109
|
if (hit)
|
|
110
110
|
return hit;
|
|
@@ -112,10 +112,11 @@ function inferFromPathSegments(projectRoot) {
|
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
114
|
async function inferFromPackageJson(dir) {
|
|
115
|
+
var _a, _b;
|
|
115
116
|
try {
|
|
116
|
-
const pkgText = await promises_1.default.readFile(
|
|
117
|
+
const pkgText = await promises_1.default.readFile(path_1.default.join(dir, "package.json"), "utf8");
|
|
117
118
|
const pkg = JSON.parse(pkgText);
|
|
118
|
-
const blob = `${pkg.name
|
|
119
|
+
const blob = `${(_a = pkg.name) !== null && _a !== void 0 ? _a : ""} ${(_b = pkg.description) !== null && _b !== void 0 ? _b : ""}`;
|
|
119
120
|
return matchKindInText(blob);
|
|
120
121
|
}
|
|
121
122
|
catch {
|
|
@@ -127,7 +128,8 @@ async function inferFromPackageJson(dir) {
|
|
|
127
128
|
* monorepo 子目录(如 adInsight-web/frontend)会向上扫描路径,避免误判为 OMS。
|
|
128
129
|
*/
|
|
129
130
|
async function inferProjectKind(projectRoot) {
|
|
130
|
-
|
|
131
|
+
var _a;
|
|
132
|
+
const fromEnv = ((_a = process.env.SDD_FLOW_KIT_PROJECT) !== null && _a !== void 0 ? _a : "").trim();
|
|
131
133
|
if (exports.ALLOWED_PROJECTS.includes(fromEnv)) {
|
|
132
134
|
return fromEnv;
|
|
133
135
|
}
|
|
@@ -140,7 +142,7 @@ async function inferProjectKind(projectRoot) {
|
|
|
140
142
|
const fromPkg = await inferFromPackageJson(dir);
|
|
141
143
|
if (fromPkg)
|
|
142
144
|
return fromPkg;
|
|
143
|
-
const parent =
|
|
145
|
+
const parent = path_1.default.dirname(dir);
|
|
144
146
|
if (parent === dir)
|
|
145
147
|
break;
|
|
146
148
|
dir = parent;
|
|
@@ -0,0 +1,35 @@
|
|
|
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.MIN_NODE_PATCH = exports.MIN_NODE_MINOR = exports.MIN_NODE_MAJOR = void 0;
|
|
7
|
+
exports.parseNodeSemver = parseNodeSemver;
|
|
8
|
+
exports.isNodeVersionSupported = isNodeVersionSupported;
|
|
9
|
+
exports.nodeVersionRequirementLabel = nodeVersionRequirementLabel;
|
|
10
|
+
const process_1 = __importDefault(require("process"));
|
|
11
|
+
/** 与 package.json engines.node 对齐 */
|
|
12
|
+
exports.MIN_NODE_MAJOR = 14;
|
|
13
|
+
exports.MIN_NODE_MINOR = 16;
|
|
14
|
+
exports.MIN_NODE_PATCH = 0;
|
|
15
|
+
function parseNodeSemver() {
|
|
16
|
+
const m = process_1.default.version.match(/^v(\d+)\.(\d+)\.(\d+)/);
|
|
17
|
+
if (!m)
|
|
18
|
+
return { major: 0, minor: 0, patch: 0 };
|
|
19
|
+
return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };
|
|
20
|
+
}
|
|
21
|
+
function isNodeVersionSupported() {
|
|
22
|
+
const v = parseNodeSemver();
|
|
23
|
+
if (v.major > exports.MIN_NODE_MAJOR)
|
|
24
|
+
return true;
|
|
25
|
+
if (v.major < exports.MIN_NODE_MAJOR)
|
|
26
|
+
return false;
|
|
27
|
+
if (v.minor > exports.MIN_NODE_MINOR)
|
|
28
|
+
return true;
|
|
29
|
+
if (v.minor < exports.MIN_NODE_MINOR)
|
|
30
|
+
return false;
|
|
31
|
+
return v.patch >= exports.MIN_NODE_PATCH;
|
|
32
|
+
}
|
|
33
|
+
function nodeVersionRequirementLabel() {
|
|
34
|
+
return `>= ${exports.MIN_NODE_MAJOR}.${exports.MIN_NODE_MINOR}.${exports.MIN_NODE_PATCH}`;
|
|
35
|
+
}
|
|
@@ -11,9 +11,9 @@ exports.buildOpsxWorkflowSetupCommand = buildOpsxWorkflowSetupCommand;
|
|
|
11
11
|
exports.isOpsxApplyReady = isOpsxApplyReady;
|
|
12
12
|
exports.ensureOpsxWorkflow = ensureOpsxWorkflow;
|
|
13
13
|
exports.collectDeliverySetupChecks = collectDeliverySetupChecks;
|
|
14
|
-
const
|
|
15
|
-
const promises_1 = __importDefault(require("
|
|
16
|
-
const
|
|
14
|
+
const child_process_1 = require("child_process");
|
|
15
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
17
|
const fs_1 = require("./fs");
|
|
18
18
|
const DELIVERY_SCRIPT_REL = ".cursor/skills/opsx-dev-delivery-pipeline/scripts/opsx-delivery-pipeline.sh";
|
|
19
19
|
function deliveryPipelineNpmScripts() {
|
|
@@ -32,24 +32,24 @@ async function pathExists(filePath) {
|
|
|
32
32
|
}
|
|
33
33
|
/** 优先使用已安装 OPSX skills 的 editor 目录 */
|
|
34
34
|
async function resolveOpsxAgent(projectRoot, preferred) {
|
|
35
|
-
const deliverySkill =
|
|
35
|
+
const deliverySkill = path_1.default.join(projectRoot, ".cursor", "skills", "opsx-dev-delivery-pipeline", "SKILL.md");
|
|
36
36
|
if (await pathExists(deliverySkill))
|
|
37
37
|
return "cursor";
|
|
38
|
-
const claudeDelivery =
|
|
38
|
+
const claudeDelivery = path_1.default.join(projectRoot, ".claude", "skills", "opsx-dev-delivery-pipeline", "SKILL.md");
|
|
39
39
|
if (await pathExists(claudeDelivery))
|
|
40
40
|
return "claude-code";
|
|
41
|
-
if (await pathExists(
|
|
41
|
+
if (await pathExists(path_1.default.join(projectRoot, ".cursor")))
|
|
42
42
|
return "cursor";
|
|
43
|
-
if (await pathExists(
|
|
43
|
+
if (await pathExists(path_1.default.join(projectRoot, ".claude")))
|
|
44
44
|
return "claude-code";
|
|
45
45
|
return preferred;
|
|
46
46
|
}
|
|
47
47
|
/** 将 package.json 中过期的 poquan-delivery-pipeline 脚本修正为 opsx-dev-delivery-pipeline */
|
|
48
48
|
async function repairOpsxDeliveryScripts(projectRoot) {
|
|
49
|
-
const pkgPath =
|
|
49
|
+
const pkgPath = path_1.default.join(projectRoot, "package.json");
|
|
50
50
|
if (!(await pathExists(pkgPath)))
|
|
51
51
|
return false;
|
|
52
|
-
const scriptOnDisk =
|
|
52
|
+
const scriptOnDisk = path_1.default.join(projectRoot, DELIVERY_SCRIPT_REL);
|
|
53
53
|
if (!(await pathExists(scriptOnDisk)))
|
|
54
54
|
return false;
|
|
55
55
|
const { full, short } = deliveryPipelineNpmScripts();
|
|
@@ -73,7 +73,7 @@ async function repairOpsxDeliveryScripts(projectRoot) {
|
|
|
73
73
|
return true;
|
|
74
74
|
}
|
|
75
75
|
function hasCommand(cmd) {
|
|
76
|
-
return (0,
|
|
76
|
+
return (0, child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" }).status === 0;
|
|
77
77
|
}
|
|
78
78
|
function buildOpsxWorkflowSetupCommand(projectRoot, agent) {
|
|
79
79
|
const baseArgs = ["@lixin5257xxx/opsx-workflow", "setup"];
|
|
@@ -120,7 +120,7 @@ async function ensureOpsxWorkflow(options) {
|
|
|
120
120
|
plannedActions,
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
|
-
const ret = (0,
|
|
123
|
+
const ret = (0, child_process_1.spawnSync)(cmd, args, {
|
|
124
124
|
cwd: options.projectRoot,
|
|
125
125
|
stdio: "inherit",
|
|
126
126
|
env: process.env,
|
|
@@ -142,10 +142,11 @@ async function ensureOpsxWorkflow(options) {
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
async function collectDeliverySetupChecks(projectRoot) {
|
|
145
|
+
var _a, _b, _c, _d, _e, _f;
|
|
145
146
|
const checks = [];
|
|
146
147
|
checks.push({
|
|
147
148
|
name: "openspec-dir",
|
|
148
|
-
ok: await pathExists(
|
|
149
|
+
ok: await pathExists(path_1.default.join(projectRoot, "openspec")),
|
|
149
150
|
detail: "openspec/ 目录",
|
|
150
151
|
});
|
|
151
152
|
checks.push({
|
|
@@ -153,34 +154,34 @@ async function collectDeliverySetupChecks(projectRoot) {
|
|
|
153
154
|
ok: hasCommand("openspec"),
|
|
154
155
|
detail: "openspec CLI(npx openspec 或全局安装)",
|
|
155
156
|
});
|
|
156
|
-
const skillPath =
|
|
157
|
+
const skillPath = path_1.default.join(projectRoot, ".cursor", "skills", "opsx-dev-delivery-pipeline", "SKILL.md");
|
|
157
158
|
checks.push({
|
|
158
159
|
name: "opsx-dev-delivery-pipeline-skill",
|
|
159
160
|
ok: await pathExists(skillPath),
|
|
160
161
|
detail: skillPath,
|
|
161
162
|
});
|
|
162
|
-
const tddPath =
|
|
163
|
+
const tddPath = path_1.default.join(projectRoot, ".cursor", "skills", "tdd-script", "SKILL.md");
|
|
163
164
|
checks.push({
|
|
164
165
|
name: "tdd-script-skill",
|
|
165
166
|
ok: await pathExists(tddPath),
|
|
166
167
|
detail: tddPath,
|
|
167
168
|
});
|
|
168
|
-
const applyPath =
|
|
169
|
+
const applyPath = path_1.default.join(projectRoot, ".cursor", "skills", "openspec-apply-change", "SKILL.md");
|
|
169
170
|
checks.push({
|
|
170
171
|
name: "openspec-apply-change-skill",
|
|
171
172
|
ok: await pathExists(applyPath),
|
|
172
173
|
detail: applyPath,
|
|
173
174
|
});
|
|
174
|
-
const pipelineScript =
|
|
175
|
+
const pipelineScript = path_1.default.join(projectRoot, DELIVERY_SCRIPT_REL);
|
|
175
176
|
checks.push({
|
|
176
177
|
name: "delivery-pipeline-script",
|
|
177
178
|
ok: await pathExists(pipelineScript),
|
|
178
179
|
detail: pipelineScript,
|
|
179
180
|
});
|
|
180
181
|
try {
|
|
181
|
-
const pkgText = await promises_1.default.readFile(
|
|
182
|
+
const pkgText = await promises_1.default.readFile(path_1.default.join(projectRoot, "package.json"), "utf8");
|
|
182
183
|
const pkg = JSON.parse(pkgText);
|
|
183
|
-
const cmd = pkg.scripts
|
|
184
|
+
const cmd = (_b = (_a = pkg.scripts) === null || _a === void 0 ? void 0 : _a["opsx:delivery-pipeline"]) !== null && _b !== void 0 ? _b : "";
|
|
184
185
|
const ok = cmd.includes("opsx-dev-delivery-pipeline") &&
|
|
185
186
|
cmd.includes("opsx-delivery-pipeline.sh") &&
|
|
186
187
|
!cmd.includes("poquan");
|
|
@@ -198,11 +199,11 @@ async function collectDeliverySetupChecks(projectRoot) {
|
|
|
198
199
|
});
|
|
199
200
|
}
|
|
200
201
|
try {
|
|
201
|
-
const cfgText = await promises_1.default.readFile(
|
|
202
|
+
const cfgText = await promises_1.default.readFile(path_1.default.join(projectRoot, ".opsx", "config.json"), "utf8");
|
|
202
203
|
const cfg = JSON.parse(cfgText);
|
|
203
|
-
const skillsDir = cfg.skillsDir
|
|
204
|
-
const hasCursorDelivery = await pathExists(
|
|
205
|
-
const nameOk = cfg.deliveryPipeline
|
|
204
|
+
const skillsDir = (_c = cfg.skillsDir) !== null && _c !== void 0 ? _c : "";
|
|
205
|
+
const hasCursorDelivery = await pathExists(path_1.default.join(projectRoot, ".cursor", "skills", "opsx-dev-delivery-pipeline", "SKILL.md"));
|
|
206
|
+
const nameOk = ((_d = cfg.deliveryPipeline) === null || _d === void 0 ? void 0 : _d.skillName) === "opsx-dev-delivery-pipeline";
|
|
206
207
|
const dirOk = (hasCursorDelivery && skillsDir.includes(".cursor")) ||
|
|
207
208
|
(!hasCursorDelivery && (skillsDir.includes(".cursor") || skillsDir.includes(".claude")));
|
|
208
209
|
checks.push({
|
|
@@ -210,7 +211,7 @@ async function collectDeliverySetupChecks(projectRoot) {
|
|
|
210
211
|
ok: nameOk && dirOk,
|
|
211
212
|
detail: hasCursorDelivery && !skillsDir.includes(".cursor")
|
|
212
213
|
? `skills 在 .cursor 但 config 指向 ${skillsDir},请 npx sdd-flow-kit install -y`
|
|
213
|
-
: `skillsDir=${skillsDir}, skillName=${cfg.deliveryPipeline
|
|
214
|
+
: `skillsDir=${skillsDir}, skillName=${(_f = (_e = cfg.deliveryPipeline) === null || _e === void 0 ? void 0 : _e.skillName) !== null && _f !== void 0 ? _f : ""}`,
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
catch {
|
|
@@ -221,7 +222,7 @@ async function collectDeliverySetupChecks(projectRoot) {
|
|
|
221
222
|
});
|
|
222
223
|
}
|
|
223
224
|
try {
|
|
224
|
-
const pkgText = await promises_1.default.readFile(
|
|
225
|
+
const pkgText = await promises_1.default.readFile(path_1.default.join(projectRoot, "package.json"), "utf8");
|
|
225
226
|
const pkg = JSON.parse(pkgText);
|
|
226
227
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
227
228
|
const hasPw = Boolean(deps["@playwright/test"] || deps.playwright);
|
|
@@ -238,7 +239,7 @@ async function collectDeliverySetupChecks(projectRoot) {
|
|
|
238
239
|
detail: "无法读取 package.json 检查 Playwright",
|
|
239
240
|
});
|
|
240
241
|
}
|
|
241
|
-
const agentsMd =
|
|
242
|
+
const agentsMd = path_1.default.join(projectRoot, "AGENTS.md");
|
|
242
243
|
checks.push({
|
|
243
244
|
name: "agents-md",
|
|
244
245
|
ok: await pathExists(agentsMd),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.createPromptInterface = createPromptInterface;
|
|
40
|
+
exports.promptLine = promptLine;
|
|
41
|
+
const readline = __importStar(require("readline"));
|
|
42
|
+
const process_1 = __importDefault(require("process"));
|
|
43
|
+
/** Node 14 兼容:不使用 node:readline/promises(需 Node 17+) */
|
|
44
|
+
function createPromptInterface() {
|
|
45
|
+
return readline.createInterface({ input: process_1.default.stdin, output: process_1.default.stdout });
|
|
46
|
+
}
|
|
47
|
+
function promptLine(rl, query) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
rl.question(query, (answer) => resolve(answer));
|
|
50
|
+
});
|
|
51
|
+
}
|