sdd-flow-kit 1.0.25 → 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 +17 -0
- package/dist/adapters/index.js +11 -10
- 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 +18 -18
- package/package.json +4 -1
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. 进入你要开发的项目目录
|
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 = [
|
|
@@ -49,16 +50,16 @@ async function runClaudeFlow(args) {
|
|
|
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
|
+
}
|
package/dist/core/templates.js
CHANGED
|
@@ -4,16 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.loadTemplateText = loadTemplateText;
|
|
7
|
-
const
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const fs_1 = require("./fs");
|
|
9
9
|
function resolveCandidatePaths(relPathFromPackageRoot) {
|
|
10
10
|
// 在 TS 开发态:<pkg>/src/templates/xxx
|
|
11
11
|
// 在编译态:<pkg>/dist/cli.js -> __dirname=<pkg>/dist,所以 src/templates 要向上找两级
|
|
12
12
|
const here = __dirname; // src/core/* 或 dist/*
|
|
13
|
-
const pkgRoot =
|
|
13
|
+
const pkgRoot = path_1.default.resolve(here, "../.."); // 兼容 src/ 与 dist/
|
|
14
14
|
return [
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
path_1.default.join(pkgRoot, "src", "templates", relPathFromPackageRoot),
|
|
16
|
+
path_1.default.join(pkgRoot, "dist", "templates", relPathFromPackageRoot),
|
|
17
17
|
];
|
|
18
18
|
}
|
|
19
19
|
async function loadTemplateText(relPathFromPackageRoot) {
|
package/dist/core/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.formatYYYYMMDD = formatYYYYMMDD;
|
|
4
|
+
exports.replaceAllStr = replaceAllStr;
|
|
4
5
|
exports.ensurePosixRelativePath = ensurePosixRelativePath;
|
|
5
6
|
/**
|
|
6
7
|
* 生成 YYYYMMDD 字符串,用于 openspec/PRD 目录命名。
|
|
@@ -11,7 +12,13 @@ function formatYYYYMMDD(d) {
|
|
|
11
12
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
12
13
|
return `${yyyy}${mm}${dd}`;
|
|
13
14
|
}
|
|
15
|
+
/** Node 14 无 String.prototype.replaceAll(需 Node 15+) */
|
|
16
|
+
function replaceAllStr(text, search, replacement) {
|
|
17
|
+
if (!search)
|
|
18
|
+
return text;
|
|
19
|
+
return text.split(search).join(replacement);
|
|
20
|
+
}
|
|
14
21
|
function ensurePosixRelativePath(p) {
|
|
15
22
|
// 用于模板占位符中展示,避免反斜杠导致工具误解析。
|
|
16
|
-
return p
|
|
23
|
+
return replaceAllStr(p, "\\", "/");
|
|
17
24
|
}
|
package/dist/postinstall.js
CHANGED
|
@@ -36,20 +36,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const
|
|
40
|
-
const
|
|
39
|
+
const path_1 = __importDefault(require("path"));
|
|
40
|
+
const process_1 = __importDefault(require("process"));
|
|
41
41
|
const inferProject_1 = require("./core/inferProject");
|
|
42
42
|
const opsxDelivery_1 = require("./core/opsxDelivery");
|
|
43
43
|
const setupProject_1 = require("./steps/setupProject");
|
|
44
44
|
async function inferAgent(projectRoot) {
|
|
45
|
-
|
|
45
|
+
var _a;
|
|
46
|
+
const fromEnv = ((_a = process_1.default.env.SDD_FLOW_KIT_AGENT) !== null && _a !== void 0 ? _a : "").trim();
|
|
46
47
|
const allowed = ["cursor", "claude-code", "codex", "openclaw"];
|
|
47
48
|
if (allowed.includes(fromEnv))
|
|
48
49
|
return fromEnv;
|
|
49
|
-
const fs = await Promise.resolve().then(() => __importStar(require("
|
|
50
|
+
const fs = await Promise.resolve().then(() => __importStar(require("fs/promises")));
|
|
50
51
|
const exists = async (rel) => {
|
|
51
52
|
try {
|
|
52
|
-
await fs.access(
|
|
53
|
+
await fs.access(path_1.default.join(projectRoot, rel));
|
|
53
54
|
return true;
|
|
54
55
|
}
|
|
55
56
|
catch {
|
|
@@ -67,12 +68,12 @@ async function inferAgent(projectRoot) {
|
|
|
67
68
|
return "cursor";
|
|
68
69
|
}
|
|
69
70
|
async function main() {
|
|
70
|
-
if (
|
|
71
|
+
if (process_1.default.env.SDD_FLOW_KIT_SKIP_POSTINSTALL === "1") {
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
|
-
const initCwd =
|
|
74
|
-
const pkgDir =
|
|
75
|
-
const projectRoot = initCwd
|
|
74
|
+
const initCwd = process_1.default.env.INIT_CWD ? path_1.default.resolve(process_1.default.env.INIT_CWD) : null;
|
|
75
|
+
const pkgDir = path_1.default.resolve(__dirname, "..");
|
|
76
|
+
const projectRoot = initCwd !== null && initCwd !== void 0 ? initCwd : process_1.default.cwd();
|
|
76
77
|
if (projectRoot === pkgDir)
|
|
77
78
|
return;
|
|
78
79
|
const projectKind = await (0, inferProject_1.inferProjectKind)(projectRoot);
|
package/dist/steps/doctor.js
CHANGED
|
@@ -4,12 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runDoctor = runDoctor;
|
|
7
|
-
const
|
|
8
|
-
const promises_1 = __importDefault(require("
|
|
9
|
-
const
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const process_1 = __importDefault(require("process"));
|
|
11
|
+
const nodeCompat_1 = require("../core/nodeCompat");
|
|
10
12
|
const opsxDelivery_1 = require("../core/opsxDelivery");
|
|
11
13
|
function hasCommand(cmd) {
|
|
12
|
-
const out = (0,
|
|
14
|
+
const out = (0, child_process_1.spawnSync)("which", [cmd], { stdio: "ignore" });
|
|
13
15
|
return out.status === 0;
|
|
14
16
|
}
|
|
15
17
|
async function hasPath(filePath) {
|
|
@@ -34,6 +36,11 @@ function agentBin(agent) {
|
|
|
34
36
|
}
|
|
35
37
|
async function runDoctor(options) {
|
|
36
38
|
const checks = [];
|
|
39
|
+
checks.push({
|
|
40
|
+
name: "node-version",
|
|
41
|
+
ok: (0, nodeCompat_1.isNodeVersionSupported)(),
|
|
42
|
+
detail: `Node ${process_1.default.version}(要求 ${(0, nodeCompat_1.nodeVersionRequirementLabel)()},推荐 18/20 LTS)`,
|
|
43
|
+
});
|
|
37
44
|
checks.push({
|
|
38
45
|
name: "git",
|
|
39
46
|
ok: hasCommand("git"),
|
|
@@ -54,7 +61,7 @@ async function runDoctor(options) {
|
|
|
54
61
|
}
|
|
55
62
|
checks.push({
|
|
56
63
|
name: "openspec-dir",
|
|
57
|
-
ok: await hasPath(
|
|
64
|
+
ok: await hasPath(path_1.default.join(options.projectRoot, "openspec")),
|
|
58
65
|
detail: "openspec 目录存在性",
|
|
59
66
|
});
|
|
60
67
|
const toolDirMap = {
|
|
@@ -68,7 +75,7 @@ async function runDoctor(options) {
|
|
|
68
75
|
if (expectedDir) {
|
|
69
76
|
checks.push({
|
|
70
77
|
name: `${options.agent}-dir`,
|
|
71
|
-
ok: await hasPath(
|
|
78
|
+
ok: await hasPath(path_1.default.join(options.projectRoot, expectedDir)),
|
|
72
79
|
detail: `${expectedDir} 目录存在性`,
|
|
73
80
|
});
|
|
74
81
|
}
|
package/dist/steps/invokeFlow.js
CHANGED
|
@@ -17,6 +17,7 @@ function parseInvokeText(text) {
|
|
|
17
17
|
return { product, version };
|
|
18
18
|
}
|
|
19
19
|
function toRunOptions(args) {
|
|
20
|
+
var _a, _b;
|
|
20
21
|
return {
|
|
21
22
|
product: args.parsed.product,
|
|
22
23
|
version: args.parsed.version,
|
|
@@ -24,6 +25,6 @@ function toRunOptions(args) {
|
|
|
24
25
|
branch: args.branch,
|
|
25
26
|
yes: args.yes,
|
|
26
27
|
dryRun: args.dryRun,
|
|
27
|
-
agent: args.agent
|
|
28
|
+
agent: (_b = (_a = args.agent) !== null && _a !== void 0 ? _a : args.parsed.agent) !== null && _b !== void 0 ? _b : "claude-code",
|
|
28
29
|
};
|
|
29
30
|
}
|
package/dist/steps/runFlow.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runFlow = runFlow;
|
|
7
|
-
const
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const adapters_1 = require("../adapters");
|
|
9
9
|
const fs_1 = require("../core/fs");
|
|
10
10
|
const startFlow_1 = require("./startFlow");
|
|
@@ -41,7 +41,7 @@ async function runFlow(options) {
|
|
|
41
41
|
const scaffold = options.dryRun
|
|
42
42
|
? {
|
|
43
43
|
runId: "dry-run-no-files-generated",
|
|
44
|
-
outputRoot:
|
|
44
|
+
outputRoot: path_1.default.join(options.projectRoot, "openspec", "PRD"),
|
|
45
45
|
}
|
|
46
46
|
: await (0, startFlow_1.startFlowScaffold)(options);
|
|
47
47
|
const adapter = (0, adapters_1.getAdapter)(setup.agent);
|
|
@@ -63,7 +63,7 @@ async function runFlow(options) {
|
|
|
63
63
|
version: options.version,
|
|
64
64
|
});
|
|
65
65
|
if (!options.dryRun) {
|
|
66
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
66
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(scaffold.outputRoot, "AUTO-RUN-CHECKLIST.md"), checklist);
|
|
67
67
|
}
|
|
68
68
|
return {
|
|
69
69
|
...scaffold,
|
|
@@ -4,19 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.setupProject = setupProject;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const node_process_1 = __importDefault(require("node:process"));
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const process_1 = __importDefault(require("process"));
|
|
11
10
|
const fs_1 = require("../core/fs");
|
|
11
|
+
const readlinePrompt_1 = require("../core/readlinePrompt");
|
|
12
|
+
const utils_1 = require("../core/utils");
|
|
12
13
|
const opsxDelivery_1 = require("../core/opsxDelivery");
|
|
13
14
|
const templates_1 = require("../core/templates");
|
|
14
15
|
const EDITOR_CHOICES = ["claude-code", "cursor", "codex", "openclaw"];
|
|
15
16
|
const PROJECT_CHOICES = ["OMS", "ADI", "欢盟", "AD Tools"];
|
|
16
17
|
function runGit(args, cwd) {
|
|
17
|
-
|
|
18
|
+
var _a, _b;
|
|
19
|
+
const out = (0, child_process_1.spawnSync)("git", args, { cwd, encoding: "utf8" });
|
|
18
20
|
if (out.status !== 0) {
|
|
19
|
-
const err = out.stderr
|
|
21
|
+
const err = ((_a = out.stderr) === null || _a === void 0 ? void 0 : _a.trim()) || ((_b = out.stdout) === null || _b === void 0 ? void 0 : _b.trim()) || `git ${args.join(" ")} failed`;
|
|
20
22
|
throw new Error(err);
|
|
21
23
|
}
|
|
22
24
|
return out.stdout.trim();
|
|
@@ -45,9 +47,9 @@ function listLocalBranches(projectRoot) {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
async function askAgent(defaultAgent) {
|
|
48
|
-
if (!
|
|
50
|
+
if (!process_1.default.stdin.isTTY)
|
|
49
51
|
return defaultAgent;
|
|
50
|
-
const rl = (0,
|
|
52
|
+
const rl = (0, readlinePrompt_1.createPromptInterface)();
|
|
51
53
|
const prompt = [
|
|
52
54
|
"请选择开发环境:",
|
|
53
55
|
"1) claude-code (default)",
|
|
@@ -56,7 +58,7 @@ async function askAgent(defaultAgent) {
|
|
|
56
58
|
"4) openclaw",
|
|
57
59
|
"输入编号(回车默认 1):",
|
|
58
60
|
].join("\n");
|
|
59
|
-
const answer = (await
|
|
61
|
+
const answer = (await (0, readlinePrompt_1.promptLine)(rl, `${prompt}\n`)).trim();
|
|
60
62
|
rl.close();
|
|
61
63
|
if (!answer)
|
|
62
64
|
return defaultAgent;
|
|
@@ -66,17 +68,17 @@ async function askAgent(defaultAgent) {
|
|
|
66
68
|
return EDITOR_CHOICES[idx - 1];
|
|
67
69
|
}
|
|
68
70
|
async function askBranch(defaultBranch) {
|
|
69
|
-
if (!
|
|
71
|
+
if (!process_1.default.stdin.isTTY)
|
|
70
72
|
return defaultBranch;
|
|
71
|
-
const rl = (0,
|
|
72
|
-
const answer = (await
|
|
73
|
+
const rl = (0, readlinePrompt_1.createPromptInterface)();
|
|
74
|
+
const answer = (await (0, readlinePrompt_1.promptLine)(rl, `请输入开发分支(默认 ${defaultBranch}):\n`)).trim();
|
|
73
75
|
rl.close();
|
|
74
76
|
return answer || defaultBranch;
|
|
75
77
|
}
|
|
76
78
|
async function askProjectKind(defaultKind) {
|
|
77
|
-
if (!
|
|
79
|
+
if (!process_1.default.stdin.isTTY)
|
|
78
80
|
return defaultKind;
|
|
79
|
-
const rl = (0,
|
|
81
|
+
const rl = (0, readlinePrompt_1.createPromptInterface)();
|
|
80
82
|
const prompt = [
|
|
81
83
|
"请选择当前项目类型(用于生成 PRD 下载 skill):",
|
|
82
84
|
"1) OMS (default)",
|
|
@@ -85,7 +87,7 @@ async function askProjectKind(defaultKind) {
|
|
|
85
87
|
"4) AD Tools",
|
|
86
88
|
"输入编号(回车默认 1):",
|
|
87
89
|
].join("\n");
|
|
88
|
-
const answer = (await
|
|
90
|
+
const answer = (await (0, readlinePrompt_1.promptLine)(rl, `${prompt}\n`)).trim();
|
|
89
91
|
rl.close();
|
|
90
92
|
if (!answer)
|
|
91
93
|
return defaultKind;
|
|
@@ -114,7 +116,7 @@ function skillInstallPath(projectRoot, agent) {
|
|
|
114
116
|
const rel = relMap[agent];
|
|
115
117
|
if (!rel)
|
|
116
118
|
return null;
|
|
117
|
-
return
|
|
119
|
+
return path_1.default.join(projectRoot, rel);
|
|
118
120
|
}
|
|
119
121
|
function allSkillInstallPaths(projectRoot) {
|
|
120
122
|
const agents = ["cursor", "claude-code", "codex", "openclaw"];
|
|
@@ -135,19 +137,19 @@ async function installAgentSkill(projectRoot) {
|
|
|
135
137
|
: target.includes("/.codex/")
|
|
136
138
|
? "codex"
|
|
137
139
|
: "openclaw";
|
|
138
|
-
const content =
|
|
140
|
+
const content = (0, utils_1.replaceAllStr)(template, "{{AGENT_NAME}}", agentName);
|
|
139
141
|
await (0, fs_1.writeTextFileEnsuredDir)(target, content);
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
async function installSddCursorRule(projectRoot) {
|
|
143
|
-
const rulePath =
|
|
145
|
+
const rulePath = path_1.default.join(projectRoot, ".cursor", "rules", "sdd-flow-kit-gates.mdc");
|
|
144
146
|
const template = await (0, templates_1.loadTemplateText)("rules/sdd-no-implement-until-apply.mdc.template");
|
|
145
147
|
await (0, fs_1.writeTextFileEnsuredDir)(rulePath, template);
|
|
146
148
|
}
|
|
147
149
|
function renderVars(template, vars) {
|
|
148
150
|
let out = template;
|
|
149
151
|
for (const [k, v] of Object.entries(vars)) {
|
|
150
|
-
out =
|
|
152
|
+
out = (0, utils_1.replaceAllStr)(out, `{{${k}}}`, v);
|
|
151
153
|
}
|
|
152
154
|
return out;
|
|
153
155
|
}
|
|
@@ -166,7 +168,7 @@ function docSkillDirName(kind) {
|
|
|
166
168
|
}
|
|
167
169
|
async function installDocsSkill(projectRoot, kind) {
|
|
168
170
|
const { dir, prefix } = docSkillDirName(kind);
|
|
169
|
-
const docsDir =
|
|
171
|
+
const docsDir = path_1.default.join(projectRoot, "docs", dir);
|
|
170
172
|
const pyTemplate = await (0, templates_1.loadTemplateText)("skills/confluence-doc.py.template");
|
|
171
173
|
const mdTemplate = await (0, templates_1.loadTemplateText)("skills/doc-skill.SKILL.md.template");
|
|
172
174
|
const md = renderVars(mdTemplate, {
|
|
@@ -176,9 +178,9 @@ async function installDocsSkill(projectRoot, kind) {
|
|
|
176
178
|
PRODUCT_PREFIX_LOWER: prefix.toLowerCase(),
|
|
177
179
|
SKILL_DIR: dir,
|
|
178
180
|
});
|
|
179
|
-
const py =
|
|
180
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
181
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
181
|
+
const py = (0, utils_1.replaceAllStr)(pyTemplate, "{{DOC_PRODUCT_PREFIX}}", prefix);
|
|
182
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(docsDir, "SKILL.md"), md);
|
|
183
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(docsDir, "confluence-doc.py"), py);
|
|
182
184
|
return { dirPath: docsDir };
|
|
183
185
|
}
|
|
184
186
|
function getOpsxPathsByAgent(agent) {
|
|
@@ -196,7 +198,7 @@ function getOpsxPathsByAgent(agent) {
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
async function installOpsxConfig(projectRoot, agent) {
|
|
199
|
-
const target =
|
|
201
|
+
const target = path_1.default.join(projectRoot, ".opsx", "config.json");
|
|
200
202
|
const resolvedAgent = await (0, opsxDelivery_1.resolveOpsxAgent)(projectRoot, agent);
|
|
201
203
|
const paths = getOpsxPathsByAgent(resolvedAgent);
|
|
202
204
|
const config = {
|
|
@@ -216,11 +218,12 @@ async function installOpsxConfig(projectRoot, agent) {
|
|
|
216
218
|
await (0, fs_1.writeTextFileEnsuredDir)(target, JSON.stringify(config, null, 2));
|
|
217
219
|
}
|
|
218
220
|
async function setupProject(options) {
|
|
219
|
-
|
|
221
|
+
var _a, _b, _c;
|
|
222
|
+
const defaultAgent = (_a = options.agent) !== null && _a !== void 0 ? _a : "claude-code";
|
|
220
223
|
const selectedAgent = options.yes ? defaultAgent : await askAgent(defaultAgent);
|
|
221
|
-
const defaultKind = options.projectKind
|
|
224
|
+
const defaultKind = (_b = options.projectKind) !== null && _b !== void 0 ? _b : "OMS";
|
|
222
225
|
const selectedKind = options.yes ? defaultKind : await askProjectKind(defaultKind);
|
|
223
|
-
const defaultBranch = options.branch
|
|
226
|
+
const defaultBranch = (_c = options.branch) !== null && _c !== void 0 ? _c : pickDefaultBranch(options.projectRoot);
|
|
224
227
|
const selectedBranch = options.yes ? defaultBranch : await askBranch(defaultBranch);
|
|
225
228
|
const plannedActions = [];
|
|
226
229
|
const branches = listLocalBranches(options.projectRoot);
|
|
@@ -268,9 +271,9 @@ async function setupProject(options) {
|
|
|
268
271
|
plannedActions.push(`would install skill: ${skillPath}`);
|
|
269
272
|
}
|
|
270
273
|
const { dir } = docSkillDirName(selectedKind);
|
|
271
|
-
plannedActions.push(`would install docs skill: ${
|
|
272
|
-
plannedActions.push(`would install opsx config: ${
|
|
273
|
-
plannedActions.push(`would install cursor rule: ${
|
|
274
|
+
plannedActions.push(`would install docs skill: ${path_1.default.join(options.projectRoot, "docs", dir)}`);
|
|
275
|
+
plannedActions.push(`would install opsx config: ${path_1.default.join(options.projectRoot, ".opsx", "config.json")}`);
|
|
276
|
+
plannedActions.push(`would install cursor rule: ${path_1.default.join(options.projectRoot, ".cursor", "rules", "sdd-flow-kit-gates.mdc")}`);
|
|
274
277
|
}
|
|
275
278
|
else {
|
|
276
279
|
await installAgentSkill(options.projectRoot);
|
|
@@ -304,7 +307,7 @@ async function setupProject(options) {
|
|
|
304
307
|
...plannedActions.map((x) => `- ${x}`),
|
|
305
308
|
"",
|
|
306
309
|
].join("\n");
|
|
307
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
310
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(options.projectRoot, ".sdd-flow-kit", "setup-summary.md"), summary);
|
|
308
311
|
return {
|
|
309
312
|
agent: selectedAgent,
|
|
310
313
|
projectKind: selectedKind,
|
package/dist/steps/startFlow.js
CHANGED
|
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.startFlowScaffold = startFlowScaffold;
|
|
7
|
-
const
|
|
8
|
-
const promises_1 = __importDefault(require("
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const promises_1 = __importDefault(require("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");
|
|
@@ -13,15 +13,15 @@ const inferProject_1 = require("../core/inferProject");
|
|
|
13
13
|
function renderTemplate(template, vars) {
|
|
14
14
|
let out = template;
|
|
15
15
|
for (const [k, v] of Object.entries(vars)) {
|
|
16
|
-
out =
|
|
16
|
+
out = (0, utils_1.replaceAllStr)(out, `{{${k}}}`, v);
|
|
17
17
|
}
|
|
18
18
|
return out;
|
|
19
19
|
}
|
|
20
20
|
async function startFlowScaffold(options) {
|
|
21
21
|
const now = new Date();
|
|
22
22
|
const runId = `${(0, utils_1.formatYYYYMMDD)(now)}-${options.product}-${options.version}`;
|
|
23
|
-
const outputRoot =
|
|
24
|
-
const sourceDir =
|
|
23
|
+
const outputRoot = path_1.default.join(options.projectRoot, "openspec", "PRD", runId);
|
|
24
|
+
const sourceDir = path_1.default.join(outputRoot, "source");
|
|
25
25
|
await promises_1.default.mkdir(sourceDir, { recursive: true });
|
|
26
26
|
// 1) Step1: doc-skill 指引(按产品解析脚本路径,支持 monorepo frontend)
|
|
27
27
|
const kind = (0, inferProject_1.productToKind)(options.product);
|
|
@@ -33,23 +33,23 @@ async function startFlowScaffold(options) {
|
|
|
33
33
|
DOC_SKILL_REL: docSkill.relFromRoot.replace(/\\/g, "/"),
|
|
34
34
|
VERSION_EXAMPLE: options.version,
|
|
35
35
|
});
|
|
36
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
36
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "01-获取需求文档指引.md"), step1);
|
|
37
37
|
// source 占位:让第 2 步提示词在任何工具里都能直接读
|
|
38
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
38
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(sourceDir, "PRD.md"), [
|
|
39
39
|
"# source/PRD.md",
|
|
40
40
|
"",
|
|
41
41
|
"请把《PRD》markdown 内容粘贴到这里(由 `adi-doc-skill` 下载并回填)。",
|
|
42
42
|
"",
|
|
43
43
|
"- 建议:保持原有标题层级与编号。",
|
|
44
44
|
].join("\n"));
|
|
45
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
45
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(sourceDir, "接口文档.md"), [
|
|
46
46
|
"# source/接口文档.md",
|
|
47
47
|
"",
|
|
48
48
|
"(可选)如果你已经有接口文档,请粘贴到这里;没有也不影响后续 SDD 循环。",
|
|
49
49
|
"",
|
|
50
50
|
"- 注意:接口文档中的字段/示例越完整,分析报告越贴近落地。",
|
|
51
51
|
].join("\n"));
|
|
52
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
52
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(sourceDir, "openspec-knowledge-base-summary.md"), [
|
|
53
53
|
"# source/openspec-knowledge-base-summary.md",
|
|
54
54
|
"",
|
|
55
55
|
"(可选)现有知识库摘要/快速索引,建议由你在执行第 2 步前手动整理。",
|
|
@@ -60,23 +60,23 @@ async function startFlowScaffold(options) {
|
|
|
60
60
|
const sddLoopPrompt = await (0, templates_1.loadTemplateText)("prompts/02-sdd-loop-prompt.md");
|
|
61
61
|
const step2PromptTemplate = await (0, templates_1.loadTemplateText)("artifacts/02-需求分析提示词.template.md");
|
|
62
62
|
const step2Prompt = renderTemplate(step2PromptTemplate, { SDD_LOOP_PROMPT: sddLoopPrompt });
|
|
63
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
63
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "02-需求分析提示词.md"), step2Prompt);
|
|
64
64
|
// 3) Step2 输出文件占位(由 AI 工具生成并覆盖)
|
|
65
65
|
const file01Template = await (0, templates_1.loadTemplateText)("artifacts/01-需求分析报告.template.md");
|
|
66
66
|
const file02Template = await (0, templates_1.loadTemplateText)("artifacts/02-改动点清单.template.md");
|
|
67
67
|
const file03Template = await (0, templates_1.loadTemplateText)("artifacts/03-待确认问题清单.template.md");
|
|
68
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
69
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
70
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
68
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "01-需求分析报告.md"), file01Template);
|
|
69
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "02-改动点清单.md"), file02Template);
|
|
70
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "03-待确认问题清单.md"), file03Template);
|
|
71
71
|
// 4) Step4 技术文档入口(草稿)
|
|
72
72
|
const techDocTemplate = await (0, templates_1.loadTemplateText)("artifacts/04-技术文档.template.md");
|
|
73
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
73
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "04-技术文档草稿.md"), techDocTemplate);
|
|
74
74
|
// 5) Step5 openspec 提案入口(草稿)
|
|
75
75
|
const openspecEntryTemplate = await (0, templates_1.loadTemplateText)("artifacts/05-openspec-提案草稿.template.md");
|
|
76
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
76
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "05-openspec-提案草稿.md"), openspecEntryTemplate);
|
|
77
77
|
const opsxAutoChainTemplate = await (0, templates_1.loadTemplateText)("artifacts/06-opsx-auto-chain.template.md");
|
|
78
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
79
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
78
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "06-opsx-自动串联指引.md"), opsxAutoChainTemplate);
|
|
79
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, ".session-state.json"), JSON.stringify({
|
|
80
80
|
product: options.product,
|
|
81
81
|
version: options.version,
|
|
82
82
|
runId,
|
|
@@ -88,7 +88,7 @@ async function startFlowScaffold(options) {
|
|
|
88
88
|
createdAtISO: new Date().toISOString(),
|
|
89
89
|
}, null, 2));
|
|
90
90
|
// runDir 自述
|
|
91
|
-
await (0, fs_1.writeTextFileEnsuredDir)(
|
|
91
|
+
await (0, fs_1.writeTextFileEnsuredDir)(path_1.default.join(outputRoot, "RUNME.md"), [
|
|
92
92
|
"# RUNME(SDD 流程执行说明)",
|
|
93
93
|
"",
|
|
94
94
|
`本次 runId: \`${runId}\``,
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdd-flow-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cross-agent SDD automated development workflow kit (ADI/ADI-like).",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=14.16.0"
|
|
9
|
+
},
|
|
7
10
|
"type": "commonjs",
|
|
8
11
|
"main": "dist/index.js",
|
|
9
12
|
"files": [
|