@hupan56/wlkj 2.6.0 → 2.7.0
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/bin/cli.js +261 -11
- package/package.json +1 -1
- package/templates/qoder/agents/insight-planning.md +67 -0
- package/templates/qoder/agents/insight-research.md +61 -0
- package/templates/qoder/agents/prd-reference.md +8 -2
- package/templates/qoder/commands/optional/wl-insight.md +275 -0
- package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
- package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
- package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
- package/templates/qoder/commands/wl-code.md +10 -2
- package/templates/qoder/commands/wl-commit.md +1 -1
- package/templates/qoder/commands/wl-design-draw.md +78 -0
- package/templates/qoder/commands/wl-design-scan.md +108 -0
- package/templates/qoder/commands/wl-design-spec.md +154 -0
- package/templates/qoder/commands/wl-design.md +32 -0
- package/templates/qoder/commands/wl-init.md +24 -3
- package/templates/qoder/commands/wl-prd-full.md +226 -0
- package/templates/qoder/commands/wl-prd-quick.md +134 -0
- package/templates/qoder/commands/wl-prd-review.md +104 -0
- package/templates/qoder/commands/wl-prd.md +17 -311
- package/templates/qoder/commands/wl-search.md +66 -30
- package/templates/qoder/commands/wl-task.md +290 -59
- package/templates/qoder/commands/wl-test.md +92 -24
- package/templates/qoder/config.yaml +51 -15
- package/templates/qoder/hooks/inject-workflow-state.py +2 -2
- package/templates/qoder/hooks/session-start.py +82 -51
- package/templates/qoder/rules/wl-pipeline.md +216 -105
- package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/archive_prd.py +377 -0
- package/templates/qoder/scripts/autotest.py +1715 -0
- package/templates/qoder/scripts/autotest_batch.py +224 -0
- package/templates/qoder/scripts/autotest_run.py +297 -0
- package/templates/qoder/scripts/benchmark.py +210 -209
- package/templates/qoder/scripts/build_style_index.py +444 -4
- package/templates/qoder/scripts/check_carriers.py +238 -0
- package/templates/qoder/scripts/check_mcp.py +298 -0
- package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
- package/templates/qoder/scripts/common/events.py +46 -0
- package/templates/qoder/scripts/common/extract.py +419 -0
- package/templates/qoder/scripts/common/graph_traverse.py +533 -0
- package/templates/qoder/scripts/common/paths.py +89 -0
- package/templates/qoder/scripts/common/pip_install.py +144 -0
- package/templates/qoder/scripts/common/platform_guard.py +61 -0
- package/templates/qoder/scripts/common/search_engine.py +205 -205
- package/templates/qoder/scripts/common/terms.py +57 -0
- package/templates/qoder/scripts/common/ts_extract.py +536 -0
- package/templates/qoder/scripts/context_pack.py +73 -13
- package/templates/qoder/scripts/enrich_prompt.py +226 -0
- package/templates/qoder/scripts/eval_prd.py +318 -231
- package/templates/qoder/scripts/export.py +487 -487
- package/templates/qoder/scripts/extract_api_params.py +246 -0
- package/templates/qoder/scripts/extract_routes.py +54 -0
- package/templates/qoder/scripts/extract_routes_tree.py +78 -0
- package/templates/qoder/scripts/fill_prototype.py +707 -0
- package/templates/qoder/scripts/gen_design_doc.py +394 -0
- package/templates/qoder/scripts/git_sync.py +27 -15
- package/templates/qoder/scripts/init_doctor.py +292 -40
- package/templates/qoder/scripts/install_qoderwork.py +366 -9
- package/templates/qoder/scripts/kg.py +708 -0
- package/templates/qoder/scripts/kg_auto_login.py +196 -0
- package/templates/qoder/scripts/kg_build.py +612 -0
- package/templates/qoder/scripts/kg_build_db.py +327 -0
- package/templates/qoder/scripts/kg_duckdb.py +549 -0
- package/templates/qoder/scripts/kg_incremental.py +393 -0
- package/templates/qoder/scripts/kg_link_db.py +224 -0
- package/templates/qoder/scripts/kg_mcp_server.py +801 -0
- package/templates/qoder/scripts/kg_semantic.py +150 -0
- package/templates/qoder/scripts/kg_test_runner.py +241 -0
- package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
- package/templates/qoder/scripts/learn.py +118 -39
- package/templates/qoder/scripts/learn_aggregate.py +201 -0
- package/templates/qoder/scripts/mcp_launcher.py +359 -0
- package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
- package/templates/qoder/scripts/repo_root.py +106 -0
- package/templates/qoder/scripts/role.py +12 -0
- package/templates/qoder/scripts/run_weekly_update.bat +5 -0
- package/templates/qoder/scripts/run_weekly_update.sh +5 -0
- package/templates/qoder/scripts/search_index.py +307 -60
- package/templates/qoder/scripts/secure-ls.js +5640 -0
- package/templates/qoder/scripts/setup.py +706 -641
- package/templates/qoder/scripts/setup_lanhu.py +963 -0
- package/templates/qoder/scripts/status.py +250 -11
- package/templates/qoder/scripts/sync_carriers.py +259 -0
- package/templates/qoder/scripts/syncgate.py +2 -2
- package/templates/qoder/scripts/task.py +75 -0
- package/templates/qoder/scripts/team_sync.py +60 -4
- package/templates/qoder/scripts/workspace_init.py +1 -1
- package/templates/qoder/skills/design-import/SKILL.md +226 -0
- package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
- package/templates/qoder/skills/design-review/SKILL.md +82 -25
- package/templates/qoder/skills/prd-generator/SKILL.md +184 -60
- package/templates/qoder/skills/prd-review/SKILL.md +18 -1
- package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
- package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
- package/templates/qoder/skills/test-generator/SKILL.md +15 -2
- package/templates/qoder/skills/wl-code/SKILL.md +55 -36
- package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
- package/templates/qoder/skills/wl-design/SKILL.md +55 -0
- package/templates/qoder/skills/wl-init/SKILL.md +76 -67
- package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
- package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
- package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
- package/templates/qoder/skills/wl-report/SKILL.md +131 -107
- package/templates/qoder/skills/wl-search/SKILL.md +141 -75
- package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
- package/templates/qoder/skills/wl-status/SKILL.md +83 -61
- package/templates/qoder/skills/wl-task/SKILL.md +132 -58
- package/templates/qoder/skills/wl-test/SKILL.md +406 -40
- package/templates/qoder/templates/prototype-app.html +13 -8
- package/templates/qoder/templates/prototype-web.html +376 -93
- package/templates/root/AGENTS.md +89 -34
- package/templates/root/requirements.txt +21 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
- package/templates/qoder/agents/prd-planning.md +0 -57
- package/templates/qoder/agents/prd-research.md +0 -34
- package/templates/qoder/commands/wl-insight.md +0 -51
- package/templates/qoder/scripts/__pycache__/init_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/setup.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/common/__pycache__/developer.cpython-39.pyc +0 -0
- package/templates/qoder/skills/wl-prd/SKILL.md +0 -92
package/bin/cli.js
CHANGED
|
@@ -24,14 +24,81 @@ const LOCAL_STATE_FILES = new Set([
|
|
|
24
24
|
".developer", ".current-task", ".engine-version",
|
|
25
25
|
]);
|
|
26
26
|
|
|
27
|
+
// 探测可用的 Python 命令 (python 或 python3)。
|
|
28
|
+
// macOS 系统通常只有 python3, 硬编码 python 会让 Mac 全员失败。
|
|
29
|
+
// 探测结果缓存到全局, 避免每次调用都 fork 进程。
|
|
30
|
+
let _PY_CMD_CACHE = null;
|
|
31
|
+
function detectPyCmd() {
|
|
32
|
+
if (_PY_CMD_CACHE) return _PY_CMD_CACHE;
|
|
33
|
+
for (const c of ["python", "python3"]) {
|
|
34
|
+
try {
|
|
35
|
+
execSync(`${c} --version`, { encoding: "utf-8", timeout: 5000, stdio: "pipe" });
|
|
36
|
+
_PY_CMD_CACHE = c;
|
|
37
|
+
return c;
|
|
38
|
+
} catch { /* try next */ }
|
|
39
|
+
}
|
|
40
|
+
_PY_CMD_CACHE = null;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
function py(script, args = []) {
|
|
28
45
|
const p = path.join(process.cwd(), ".qoder", "scripts", script);
|
|
29
46
|
if (!fs.existsSync(p)) { console.log("请先运行: npx wlkj init"); process.exit(1); }
|
|
47
|
+
const pyCmd = detectPyCmd();
|
|
48
|
+
if (!pyCmd) { console.log("Python 未安装, 无法运行脚本。跑: npx @hupan56/wlkj install-env"); return ""; }
|
|
30
49
|
try {
|
|
31
|
-
return execSync(
|
|
50
|
+
return execSync(`${pyCmd} "${p}" ${args.map(a => `"${a}"`).join(" ")}`, { cwd: process.cwd(), encoding: "utf-8", timeout: 15000 });
|
|
32
51
|
} catch (e) { return (e.stdout || e.message); }
|
|
33
52
|
}
|
|
34
53
|
|
|
54
|
+
// 带镜像兜底的 pip 安装 —— 复用 Python 的 common/pip_install.py (单一事实源)
|
|
55
|
+
// 为什么不复用 Python: 上一轮给 init_doctor 做了 common/pip_install.py (失败切清华/阿里/腾讯)。
|
|
56
|
+
// cli.js 的 install-env/update 是独立 JS 路径, 之前没享受镜像兜底 → 50 人里弱网用户全崩。
|
|
57
|
+
// 这里调用同一个 Python 工具, 保证两路径行为一致。
|
|
58
|
+
//
|
|
59
|
+
// 优先: 引擎已装 → 调 common.pip_install (有镜像兜底)
|
|
60
|
+
// 回退: 引擎没装 → 裸 pip install (install-env 首次场景, 无 Python 工具可用)
|
|
61
|
+
function installPipDeps(pyCmd, reqPath) {
|
|
62
|
+
const scriptsDir = path.join(process.cwd(), ".qoder", "scripts");
|
|
63
|
+
const hasTool = fs.existsSync(path.join(scriptsDir, "common", "pip_install.py"));
|
|
64
|
+
if (hasTool) {
|
|
65
|
+
// 调 Python 工具: install_with_fallback(['-r', reqPath]) → 输出结果
|
|
66
|
+
const prog = [
|
|
67
|
+
"import sys; sys.path.insert(0, '" + scriptsDir.replace(/\\/g, "\\\\") + "')",
|
|
68
|
+
"from common.pip_install import install_with_fallback",
|
|
69
|
+
"import json",
|
|
70
|
+
"r = install_with_fallback(['-r', " + JSON.stringify(reqPath) + "])",
|
|
71
|
+
"print(json.dumps(r, ensure_ascii=False))",
|
|
72
|
+
].join("; ");
|
|
73
|
+
try {
|
|
74
|
+
const out = execSync(`${pyCmd} -c "${prog}"`, { encoding: "utf-8", timeout: 600000 });
|
|
75
|
+
// 最后一行是 JSON 结果
|
|
76
|
+
const lines = out.trim().split("\n");
|
|
77
|
+
const jsonLine = lines[lines.length - 1];
|
|
78
|
+
const result = JSON.parse(jsonLine);
|
|
79
|
+
if (result.ok) {
|
|
80
|
+
console.log(` [OK] Python 依赖已装` + (result.source && result.source !== "default" ? ` (镜像源: ${result.source})` : ""));
|
|
81
|
+
} else {
|
|
82
|
+
console.log(` [WARN] pip 安装失败 (尝试了所有镜像)`);
|
|
83
|
+
console.log(` ${result.hint || ""}`.replace(/\n/g, "\n "));
|
|
84
|
+
}
|
|
85
|
+
return result.ok;
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.log(` [WARN] 调用镜像工具失败, 回退裸 pip: ${(e.message || "").slice(0, 80)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// 回退: 裸 pip install (引擎未装, 或工具调用失败)
|
|
91
|
+
try {
|
|
92
|
+
execSync(`${pyCmd} -m pip install -r "${reqPath}"`, { stdio: "inherit", timeout: 300000 });
|
|
93
|
+
console.log(" [OK] Python 依赖已装");
|
|
94
|
+
return true;
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.log(` [WARN] pip 安装失败: ${(e.message || "").slice(0, 80)}`);
|
|
97
|
+
console.log(" 国内网络建议配镜像: pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple");
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
35
102
|
function cp(src, dest) {
|
|
36
103
|
const s = path.join(T, src), d = path.join(process.cwd(), dest);
|
|
37
104
|
if (!fs.existsSync(s)) return;
|
|
@@ -58,11 +125,99 @@ function gitOp(op) {
|
|
|
58
125
|
catch (e) { console.log(e.stdout || e.message); }
|
|
59
126
|
}
|
|
60
127
|
|
|
61
|
-
|
|
128
|
+
// 快速环境检测 (init 前置): 缺 Python 致命退出, 缺 git 警告不阻塞
|
|
129
|
+
// 目的: 杜绝"裸机没 Python 时 init 静默失败仍宣布成功"的假成功
|
|
130
|
+
function checkEnvQuick() {
|
|
131
|
+
console.log("--- 环境检测 ---");
|
|
132
|
+
// Node 一定有 (npx 在跑), 输出 version 确认
|
|
133
|
+
console.log(` Node.js: [OK] ${process.version}`);
|
|
134
|
+
|
|
135
|
+
// Python: python 或 python3 任一可用即可
|
|
136
|
+
let pyCmd = null, pyVer = null;
|
|
137
|
+
for (const c of ["python", "python3"]) {
|
|
138
|
+
try {
|
|
139
|
+
const v = execSync(`${c} --version`, { encoding: "utf-8", timeout: 5000, stdio: "pipe" }).trim();
|
|
140
|
+
pyCmd = c; pyVer = v; break;
|
|
141
|
+
} catch { /* try next */ }
|
|
142
|
+
}
|
|
143
|
+
if (!pyCmd) {
|
|
144
|
+
console.log(" Python: [缺失] (引擎必需)");
|
|
145
|
+
console.log("\n ⚠ 没装 Python, 无法继续。请先装环境:");
|
|
146
|
+
console.log(" npx @hupan56/wlkj install-env");
|
|
147
|
+
console.log(" (自动装 Node/Python/git + pip 依赖; 装完重开终端再 init)\n");
|
|
148
|
+
return { ok: false, pyCmd: null };
|
|
149
|
+
}
|
|
150
|
+
console.log(` Python: [OK] ${pyVer} (${pyCmd})`);
|
|
151
|
+
|
|
152
|
+
// git: 可选, 缺了只警告 (与 init_doctor 口径一致)
|
|
153
|
+
try {
|
|
154
|
+
const gv = execSync("git --version", { encoding: "utf-8", timeout: 5000, stdio: "pipe" }).trim();
|
|
155
|
+
console.log(` git: [OK] ${gv}`);
|
|
156
|
+
} catch {
|
|
157
|
+
console.log(" git: [缺失] (可选, 团队同步/源码克隆将禁用, 本地 PRD/搜索/任务仍可用)");
|
|
158
|
+
console.log(" 想启用: npx @hupan56/wlkj install-env");
|
|
159
|
+
}
|
|
160
|
+
console.log("");
|
|
161
|
+
return { ok: true, pyCmd };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 交互式问角色 (50 人团队角色各异, 不能写死 pm)
|
|
165
|
+
// 支持的角色 (与 config.yaml / kg.py ROLE_SECTIONS 对齐)
|
|
166
|
+
const ROLES = [
|
|
167
|
+
{ key: "pm", label: "产品经理 (PM) — 写需求/PRD/任务" },
|
|
168
|
+
{ key: "design", label: "设计师 (UI) — 录入设计稿/出原型" },
|
|
169
|
+
{ key: "dev", label: "开发 (前端/后端) — 写代码/提交/测试" },
|
|
170
|
+
{ key: "test", label: "测试 (QA) — 跑测试用例/验证" },
|
|
171
|
+
{ key: "admin", label: "管理员 — 管理/维护工作流" },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
function askRole() {
|
|
175
|
+
// 非交互环境 (CI/AI 调用) 默认 pm
|
|
176
|
+
if (!process.stdin.isTTY) {
|
|
177
|
+
console.log(" (非交互环境, 默认角色: pm)");
|
|
178
|
+
return "pm";
|
|
179
|
+
}
|
|
180
|
+
console.log("\n 你的角色是? (决定命令视角和建议)");
|
|
181
|
+
ROLES.forEach((r, i) => console.log(` ${i + 1}. ${r.label}`));
|
|
182
|
+
const readline = require("readline").createInterface({ input: process.stdin, output: process.stdout });
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
readline.question("\n 选择 (1-5, 默认1): ", (ans) => {
|
|
185
|
+
readline.close();
|
|
186
|
+
const idx = parseInt(ans, 10) - 1;
|
|
187
|
+
const role = (idx >= 0 && idx < ROLES.length) ? ROLES[idx].key : "pm";
|
|
188
|
+
console.log(` 角色: ${role}\n`);
|
|
189
|
+
resolve(role);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 校验角色参数合法性 (大小写无关: PM/pm/Dev/dev 都认)
|
|
195
|
+
function normalizeRole(r) {
|
|
196
|
+
if (!r) return null;
|
|
197
|
+
const low = String(r).toLowerCase();
|
|
198
|
+
return ROLES.some(x => x.key === low) ? low : null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function doInit(name, roleArg) {
|
|
62
202
|
const cwd = process.cwd();
|
|
63
203
|
const hasExisting = fs.existsSync(path.join(cwd, ".qoder", "scripts", "setup.py"));
|
|
64
204
|
console.log(`\nwlkj init -> ${cwd}${hasExisting ? " (已存在, 增量更新)" : ""}\n`);
|
|
65
205
|
|
|
206
|
+
// === 0. 环境前置检测 (避免裸机无 Python 时假成功) ===
|
|
207
|
+
const env = checkEnvQuick();
|
|
208
|
+
if (!env.ok) process.exit(1);
|
|
209
|
+
const { pyCmd } = env;
|
|
210
|
+
|
|
211
|
+
// === 0.5. 确定角色 (参数 > 交互式, 不再写死 pm)
|
|
212
|
+
// 50 人团队角色各异: 产品/UI/前后端/测试/管理, 每个角色的命令视角不同。
|
|
213
|
+
// 写死 pm = 设计师和开发拿不到匹配的上下文 (见 wl-prd-full 的 --role 适配)。
|
|
214
|
+
let role = normalizeRole(roleArg);
|
|
215
|
+
if (!role) {
|
|
216
|
+
role = await askRole();
|
|
217
|
+
} else {
|
|
218
|
+
console.log(` 角色: ${role} (来自参数)\n`);
|
|
219
|
+
}
|
|
220
|
+
|
|
66
221
|
// === 1. 拷贝完整引擎 (镜像 .qoder/ 结构) ===
|
|
67
222
|
// 源: templates/qoder/* 目标: .qoder/*
|
|
68
223
|
// 注意: init 用 "update" 模式的保护逻辑更安全 —— 新装时目标文件不存在,
|
|
@@ -124,7 +279,7 @@ function doInit(name) {
|
|
|
124
279
|
console.log(`\n--- 自动初始化 ---`);
|
|
125
280
|
const setupPath = path.join(cwd, ".qoder", "scripts", "setup.py");
|
|
126
281
|
if (fs.existsSync(setupPath)) {
|
|
127
|
-
const setupArgs = [name,
|
|
282
|
+
const setupArgs = [name, role, "--skip-cron", "--skip-qoderwork"].filter(Boolean);
|
|
128
283
|
try {
|
|
129
284
|
const cmd = `python "${setupPath}" ${setupArgs.map(a => `"${a}"`).join(" ")}`;
|
|
130
285
|
console.log(` 运行: setup.py ${setupArgs.join(" ")}`);
|
|
@@ -133,8 +288,38 @@ function doInit(name) {
|
|
|
133
288
|
console.log(` setup 部分失败 (不阻塞): ${(e.message || "").slice(0, 100)}`);
|
|
134
289
|
console.log(` 可重新运行: npx @hupan56/wlkj init ${name || "<你的名字>"}`);
|
|
135
290
|
}
|
|
136
|
-
|
|
137
|
-
|
|
291
|
+
} else {
|
|
292
|
+
console.log(` setup.py 未找到, 跳过自动初始化`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// === 6.5. 自动配 QoderWork MCP (新机关键: 否则 mcp.json 不生成, 4 个 MCP 全连不上) ===
|
|
296
|
+
// 复刻 doUpdate 第 7 步的逻辑: 拷 launcher + install_qoderwork --mcp-only
|
|
297
|
+
// setup.py 带了 --skip-qoderwork (offer_qoderwork 是交互式+装skill, 不适合 npx),
|
|
298
|
+
// 这里用 --mcp-only (纯文件操作, 无交互) 把 mcp.json 配好
|
|
299
|
+
if (pyCmd) {
|
|
300
|
+
console.log(`\n--- QoderWork MCP 自动配置 ---`);
|
|
301
|
+
const home = process.env.USERPROFILE || require("os").homedir();
|
|
302
|
+
const launcherSrc = path.join(cwd, ".qoder", "scripts", "mcp_launcher.py");
|
|
303
|
+
const launcherDst = path.join(home, ".qoderwork", "mcp_launcher.py");
|
|
304
|
+
try {
|
|
305
|
+
if (fs.existsSync(launcherSrc)) {
|
|
306
|
+
fs.mkdirSync(path.dirname(launcherDst), { recursive: true });
|
|
307
|
+
fs.copyFileSync(launcherSrc, launcherDst);
|
|
308
|
+
console.log(` [OK] launcher 已装: ~/.qoderwork/mcp_launcher.py`);
|
|
309
|
+
}
|
|
310
|
+
const instScript = path.join(cwd, ".qoder", "scripts", "install_qoderwork.py");
|
|
311
|
+
if (fs.existsSync(instScript)) {
|
|
312
|
+
try {
|
|
313
|
+
execSync(`${pyCmd} "${instScript}" --mcp-only`, { stdio: "inherit", timeout: 20000 });
|
|
314
|
+
console.log(` [OK] mcp.json 已生成 (kg/mysql/lanhu/playwright, launcher 模式)`);
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.log(` [WARN] MCP 配置未完全成功 (不阻塞): ${(e.message || "").slice(0, 80)}`);
|
|
317
|
+
console.log(` 可手动补: ${pyCmd} .qoder/scripts/install_qoderwork.py`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
} catch (e) {
|
|
321
|
+
console.log(` [WARN] MCP 配置跳过: ${(e.message || "").slice(0, 80)}`);
|
|
322
|
+
}
|
|
138
323
|
}
|
|
139
324
|
|
|
140
325
|
// === 7. 写版本戳 ===
|
|
@@ -146,6 +331,9 @@ function doInit(name) {
|
|
|
146
331
|
console.log(`\n 现在可以开始了:`);
|
|
147
332
|
console.log(` 在 Qoder 里说 "写个 XX 的需求"`);
|
|
148
333
|
console.log(` 或输 / 看命令列表 (/wl-prd /wl-search /wl-task)`);
|
|
334
|
+
console.log(`\n ✓ QoderWork MCP 已自动配好 (kg/mysql/lanhu/playwright)`);
|
|
335
|
+
console.log(` 用 QoderWork 桌面端想装技能(软链到 ~/.qoderwork/skills/)?`);
|
|
336
|
+
console.log(` python .qoder/scripts/install_qoderwork.py`);
|
|
149
337
|
console.log(`\n 看不到命令? 新建对话 / 重启 QoderWork`);
|
|
150
338
|
console.log(` 环境问题? python .qoder/scripts/init_doctor.py --fix\n`);
|
|
151
339
|
}
|
|
@@ -248,12 +436,23 @@ function doStatus() {
|
|
|
248
436
|
console.log("");
|
|
249
437
|
const dev = path.join(process.cwd(), ".qoder", ".developer");
|
|
250
438
|
if (fs.existsSync(dev)) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
439
|
+
// 解析 .developer 的 name= 和 role= 行
|
|
440
|
+
const lines = fs.readFileSync(dev, "utf-8").split("\n");
|
|
441
|
+
const nameLine = lines.find(l => l.startsWith("name="));
|
|
442
|
+
const roleLine = lines.find(l => l.startsWith("role="));
|
|
443
|
+
const name = nameLine ? nameLine.split("=")[1]?.trim() : "?";
|
|
444
|
+
const role = roleLine ? roleLine.split("=")[1]?.trim() : "pm";
|
|
445
|
+
console.log(`dev: ${name} (角色: ${role})`);
|
|
446
|
+
if (!roleLine) {
|
|
447
|
+
console.log(` ⚠ 角色未设置 (默认 pm)。重跑: npx @hupan56/wlkj init ${name} <pm|design|dev|test|admin>`);
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
console.log("dev: - (未初始化。跑: npx @hupan56/wlkj init <名字> <角色>)");
|
|
451
|
+
console.log(" 角色: 产品(pm)/设计(design)/开发(dev)/测试(test)/管理(admin)");
|
|
452
|
+
}
|
|
453
|
+
|
|
255
454
|
process.stdout.write(py("task.py", ["current", "--source"]));
|
|
256
|
-
|
|
455
|
+
|
|
257
456
|
const wsSpecs = path.join(process.cwd(), "workspace", "specs", "prd");
|
|
258
457
|
if (fs.existsSync(wsSpecs)) {
|
|
259
458
|
const n = fs.readdirSync(wsSpecs).filter(f => f.endsWith(".md")).length;
|
|
@@ -380,6 +579,38 @@ function doUpdate() {
|
|
|
380
579
|
console.log(` [清理] 废弃文件 ${cleaned} 个 + 过期缓存 ${cacheCleaned} 个`);
|
|
381
580
|
}
|
|
382
581
|
|
|
582
|
+
// === 6. 补装 pip 依赖 (老用户升级时补齐, 走镜像兜底) ===
|
|
583
|
+
console.log(`\n--- Python 依赖补装 (失败自动切镜像) ---`);
|
|
584
|
+
const reqFile = path.join(cwd, "requirements.txt");
|
|
585
|
+
const pyCmd = detectPyCmd();
|
|
586
|
+
if (pyCmd && fs.existsSync(reqFile)) {
|
|
587
|
+
installPipDeps(pyCmd, reqFile);
|
|
588
|
+
} else if (!pyCmd) {
|
|
589
|
+
console.log(` [跳过] Python 未装`);
|
|
590
|
+
} else {
|
|
591
|
+
console.log(` [跳过] requirements.txt 不存在`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// === 7. 刷新 launcher + mcp.json (老用户的可能是旧版/写死路径) ===
|
|
595
|
+
try {
|
|
596
|
+
const home = process.env.USERPROFILE || require("os").homedir();
|
|
597
|
+
const launcherSrc = path.join(cwd, ".qoder", "scripts", "mcp_launcher.py");
|
|
598
|
+
const launcherDst = path.join(home, ".qoderwork", "mcp_launcher.py");
|
|
599
|
+
if (fs.existsSync(launcherSrc)) {
|
|
600
|
+
fs.mkdirSync(path.dirname(launcherDst), { recursive: true });
|
|
601
|
+
fs.copyFileSync(launcherSrc, launcherDst);
|
|
602
|
+
console.log(` [OK] launcher 已刷新`);
|
|
603
|
+
}
|
|
604
|
+
// 刷新 mcp.json 为 launcher 模式 (install_qoderwork.py 的 rewrite 逻辑)
|
|
605
|
+
const instScript = path.join(cwd, ".qoder", "scripts", "install_qoderwork.py");
|
|
606
|
+
if (pyCmd && fs.existsSync(instScript)) {
|
|
607
|
+
try {
|
|
608
|
+
execSync(`${pyCmd} "${instScript}" --mcp-only`, { stdio: "pipe", timeout: 15000 });
|
|
609
|
+
console.log(` [OK] mcp.json 已刷新为动态路径`);
|
|
610
|
+
} catch { /* --mcp-only 可能不支持, 静默跳过 */ }
|
|
611
|
+
}
|
|
612
|
+
} catch (e) { /* 刷新失败不阻塞升级 */ }
|
|
613
|
+
|
|
383
614
|
console.log(`\n${"=".repeat(50)}`);
|
|
384
615
|
console.log(` 升级完成! (v${PKG_VERSION})`);
|
|
385
616
|
console.log(`${"=".repeat(50)}`);
|
|
@@ -562,6 +793,25 @@ function doInstallEnv(checkOnly = false, location = null) {
|
|
|
562
793
|
console.log(` 或用命令: npx @hupan56/wlkj add-path "D:\\wldev\\nodejs"`);
|
|
563
794
|
}
|
|
564
795
|
|
|
796
|
+
// Python pip 依赖 (知识图谱/MCP/测试需要)
|
|
797
|
+
console.log("\n--- Python 依赖 ---");
|
|
798
|
+
const pyCmd = check("python") ? "python" : (check("python3") ? "python3" : null);
|
|
799
|
+
if (pyCmd) {
|
|
800
|
+
// 找 requirements.txt: 引擎已安装则用 .qoder/scripts/../requirements.txt
|
|
801
|
+
const reqPaths = [
|
|
802
|
+
path.join(process.cwd(), "requirements.txt"),
|
|
803
|
+
path.join(process.cwd(), ".qoder", "..", "requirements.txt"),
|
|
804
|
+
].filter(p => fs.existsSync(p));
|
|
805
|
+
if (reqPaths.length > 0) {
|
|
806
|
+
console.log(` 安装 pip 依赖: ${path.basename(reqPaths[0])} (失败自动切国内镜像)`);
|
|
807
|
+
installPipDeps(pyCmd, reqPaths[0]);
|
|
808
|
+
} else {
|
|
809
|
+
console.log(" [跳过] 未找到 requirements.txt (先 npx @hupan56/wlkj init 安装引擎)");
|
|
810
|
+
}
|
|
811
|
+
} else {
|
|
812
|
+
console.log(" [跳过] Python 未装, 无法装 pip 依赖");
|
|
813
|
+
}
|
|
814
|
+
|
|
565
815
|
console.log("\n=== 结果 ===");
|
|
566
816
|
if (need.length === 0) {
|
|
567
817
|
console.log(" 环境就绪! 下一步: npx @hupan56/wlkj init");
|
|
@@ -598,7 +848,7 @@ const [,, cmd, ...rest] = process.argv;
|
|
|
598
848
|
const mapped = rest.map(a => a === "-p" ? "--priority" : a);
|
|
599
849
|
|
|
600
850
|
switch (cmd) {
|
|
601
|
-
case "init": doInit(rest[0]); break;
|
|
851
|
+
case "init": doInit(rest[0], rest[1]); break;
|
|
602
852
|
case "update": case "upgrade": doUpdate(); break;
|
|
603
853
|
case "install-env": {
|
|
604
854
|
const checkOnly = rest.includes("--check");
|
package/package.json
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Insight Planning Agent(未来规划 · Gap 分析 + Roadmap)
|
|
2
|
+
|
|
3
|
+
> 本 agent 服务于 `/wl-insight plan` 类型。完整规则见 `.qoder/commands/optional/wl-insight.md`。
|
|
4
|
+
> 来源:从 prd-planning.md 抽拆迁而来——Phase 1(现状分析)已归并给 prd-reference,
|
|
5
|
+
> 本 agent 只保留 Phase 2-4(理想态调研 + Gap + MVP + roadmap)。
|
|
6
|
+
|
|
7
|
+
## Required: Load Context First (Pull-Based)
|
|
8
|
+
Read the dispatch prompt — it MUST contain: topic, current developer name,
|
|
9
|
+
and either (a) a reference to a recent insight research report, or
|
|
10
|
+
(b) a user-described ideal state. If neither, STOP and report back:
|
|
11
|
+
"planning needs an ideal state — either a research report or user description".
|
|
12
|
+
For current state, run: `python .qoder/scripts/context_pack.py <关键词> --platform <web|app>`
|
|
13
|
+
|
|
14
|
+
## Recursion Guard
|
|
15
|
+
You are the insight-planning sub-agent. Do NOT spawn another insight-planning agent.
|
|
16
|
+
|
|
17
|
+
## 🔒 语言锁
|
|
18
|
+
**全部输出必须简体中文。**(技术专有名词 OST/Gap/MVP 可保留英文缩写)
|
|
19
|
+
|
|
20
|
+
## Your Role
|
|
21
|
+
做 Gap 分析 + 未来规划,产出方案候选(OST 的 Solution 层)。
|
|
22
|
+
现状分析**不在本 agent 职责内**——现状由 context_pack 提供,或由 prd-reference 承接。
|
|
23
|
+
|
|
24
|
+
## Phase 1: 取现状(输入,不是本 agent 的活)
|
|
25
|
+
- 从 dispatch prompt 或 context_pack 结果读现状摘要
|
|
26
|
+
- 若现状信息不足 → 回报 caller"现状锚定不全,建议先跑 context_pack 或先做 discover"
|
|
27
|
+
|
|
28
|
+
## Phase 2: 理想态(如果有 research 报告)
|
|
29
|
+
1. 读最近一份 `workspace/members/{dev}/drafts/insight-research-*.md`
|
|
30
|
+
2. 提取调研结论作为理想态基准
|
|
31
|
+
3. 若无 research 报告 → 用 dispatch prompt 里用户描述的理想态
|
|
32
|
+
|
|
33
|
+
## Phase 3: Gap 分析(核心)
|
|
34
|
+
对比现状 vs 理想态:
|
|
35
|
+
|
|
36
|
+
| 维度 | 现状 | 理想态 | Gap | 业务价值(1-4) | 可行性(1-4) | 分 | 证据来源 |
|
|
37
|
+
|------|------|--------|-----|--------------|-------------|-----|---------|
|
|
38
|
+
|
|
39
|
+
- 业务价值:1=锦上添花 / 2=提效 / 3=解锁核心场景 / 4=不做会丢市场
|
|
40
|
+
- 可行性:看现状改动量、有没有先例可复用
|
|
41
|
+
- 分 = 价值 × 可行性,按分排序
|
|
42
|
+
|
|
43
|
+
## Phase 4: Plans(OST 第 3 层 Solution)
|
|
44
|
+
### MVP(本期)
|
|
45
|
+
- 取高价值高可行性的 Gap
|
|
46
|
+
- **当前人力能做完的才放进 MVP**(不贪多)
|
|
47
|
+
- 每条标理由(为什么是它进 MVP)
|
|
48
|
+
|
|
49
|
+
### Roadmap(后续版本)
|
|
50
|
+
- 按版本分组(V{N+1} / V{N+2}...)
|
|
51
|
+
- 标依赖关系(A 依赖 B 先做)
|
|
52
|
+
- 长期/实验性的标"待验证"
|
|
53
|
+
|
|
54
|
+
## Phase 5: 与用户确认(最重要)
|
|
55
|
+
把 Gap 表 + MVP + roadmap **一次性编号列清**,问:
|
|
56
|
+
1. Gap 分析准确吗?
|
|
57
|
+
2. MVP 范围合理吗?
|
|
58
|
+
3. Roadmap 优先级对吗?
|
|
59
|
+
调整基于反馈——最多 2 轮,然后用用户最新答案定稿(不无限循环)。
|
|
60
|
+
|
|
61
|
+
## Output
|
|
62
|
+
1. 存规划报告:`workspace/members/{dev}/drafts/insight-plan-{topic}-{YYYY-MM-DD}.md`
|
|
63
|
+
2. 报告末尾给出口提示:"要落地哪个?说'转 PRD'我把它作为现状输入,进 /wl-prd-full 参考模式落地。"
|
|
64
|
+
|
|
65
|
+
> ⚠️ **本 agent 不生成 PRD,不分配 REQ-ID,不画原型。** 那是 /wl-prd-full 的活。
|
|
66
|
+
> mode 标记的 planning/brainstorm 已不在 PRD 侧使用——insight 报告**不带 mode 标记**,
|
|
67
|
+
> 它不是 PRD;转 PRD 时由 prd 以 `<!-- mode: reference -->` 承接。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Insight Research Agent(外部调研 · 多视角脑暴)
|
|
2
|
+
|
|
3
|
+
> 本 agent 服务于 `/wl-insight research` 类型。完整规则见 `.qoder/commands/optional/wl-insight.md`。
|
|
4
|
+
> 来源:整体从 prd-research.md 搬迁而来(脑暴 + web 搜索能力归 insight)。
|
|
5
|
+
|
|
6
|
+
## Required: Load Context First (Pull-Based)
|
|
7
|
+
This platform does NOT auto-inject context. Before doing anything else:
|
|
8
|
+
1. Read the dispatch prompt to get: assigned perspective, topic,
|
|
9
|
+
current state summary (from context_pack), developer name
|
|
10
|
+
2. If perspective is missing from dispatch prompt, STOP and report back:
|
|
11
|
+
"perspective not specified - caller must assign one of: 竞品对标/行业趋势/技术方案/用户场景"
|
|
12
|
+
|
|
13
|
+
## Recursion Guard
|
|
14
|
+
You are the insight-research sub-agent.
|
|
15
|
+
Do NOT spawn another insight-research agent.
|
|
16
|
+
|
|
17
|
+
## 🔒 语言锁
|
|
18
|
+
**全部输出必须简体中文。**(技术专有名词可保留英文)
|
|
19
|
+
|
|
20
|
+
## Your Role
|
|
21
|
+
你是 N 个调研 agent 中的一个,负责**一个特定视角**的外部调研。
|
|
22
|
+
你的视角在 dispatch prompt 里指定。你的产出是**中间产物**——
|
|
23
|
+
caller(/wl-insight research)会把所有 agent 的报告合并成一份调研报告,
|
|
24
|
+
然后删除中间产物。
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
1. 从 dispatch prompt 读你的视角:竞品对标 / 行业趋势 / 技术方案 / 用户场景 之一
|
|
28
|
+
2. 读 dispatch prompt 里的**本项目现状摘要**(让调研有针对性,不泛泛而谈)
|
|
29
|
+
3. 用 WebSearch 搜 3-5 个权威源(官方文档 / 行业报告 / 技术博客 / 公开案例)
|
|
30
|
+
4. 从你的视角分析,重点找:**外部做得好而我们没做的**(Gap 信号)
|
|
31
|
+
5. 写到 `workspace/members/{dev}/drafts/research-agent-{N}-{视角}.md`
|
|
32
|
+
|
|
33
|
+
> ⚠️ **不要凭训练记忆编数据**。搜不到就标"【待验证】",不要编竞品功能/数据。
|
|
34
|
+
> ⚠️ 每个发现必须附**来源链接**。
|
|
35
|
+
|
|
36
|
+
## Output Format
|
|
37
|
+
```markdown
|
|
38
|
+
# 调研报告(中间产物):{视角}
|
|
39
|
+
|
|
40
|
+
## 视角
|
|
41
|
+
{你负责的视角}
|
|
42
|
+
|
|
43
|
+
## 本项目现状(来自 dispatch)
|
|
44
|
+
{一句话摘要,让读者知道对标基准}
|
|
45
|
+
|
|
46
|
+
## 关键发现
|
|
47
|
+
1. {发现} —— 来源:{链接}
|
|
48
|
+
- 对本项目的启发:{...}
|
|
49
|
+
- Gap 信号:{外部做得好而我们没做的}
|
|
50
|
+
2. ...
|
|
51
|
+
|
|
52
|
+
## 风险
|
|
53
|
+
- {采纳这个做法的风险/前提条件}
|
|
54
|
+
|
|
55
|
+
## 机会建议
|
|
56
|
+
- {值得跟进的机会点}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
> ⚠️ **本 agent 不生成 PRD。** 你是 research 类型的子 agent,产出中间产物。
|
|
60
|
+
> 合并后的调研报告也不带 mode 标记(它不是 PRD)。
|
|
61
|
+
> caller 合并后会删除所有 `research-agent-*.md` 中间产物。
|
|
@@ -5,7 +5,7 @@ This platform does NOT auto-inject task context. Before anything else:
|
|
|
5
5
|
1. Read the dispatch prompt — it MUST contain: platform (Web/APP/Both),
|
|
6
6
|
user requirement, current developer name
|
|
7
7
|
2. If platform is missing from the dispatch prompt, STOP and report back:
|
|
8
|
-
"platform not specified - caller must ask user first (see /wl-prd Step 0)"
|
|
8
|
+
"platform not specified - caller must ask user first (see /wl-prd-full Step 0)"
|
|
9
9
|
3. If task context needed: python .qoder/scripts/task.py current --source
|
|
10
10
|
|
|
11
11
|
## Recursion Guard
|
|
@@ -14,16 +14,22 @@ Do NOT spawn another prd-reference agent.
|
|
|
14
14
|
|
|
15
15
|
## Your Role
|
|
16
16
|
Generate PRD + prototype by referencing existing project data.
|
|
17
|
+
**同时承担"现状分析"职责**(原 prd-planning 的 Phase 1 已并入此处):
|
|
18
|
+
不仅定位文件,还要总结"现状怎么做的、有什么已知限制"——
|
|
19
|
+
这是 PRD 基于现状做轻反思的输入,也是 insight 探索的现状锚点来源。
|
|
17
20
|
|
|
18
21
|
## Workflow
|
|
19
22
|
1. Search the index (platform-aware):
|
|
20
23
|
python .qoder/scripts/search_index.py <keyword> --platform <web|app>
|
|
21
24
|
python .qoder/scripts/search_index.py --style <type> --platform <web|app>
|
|
22
25
|
python .qoder/scripts/search_index.py --prd <keyword>
|
|
26
|
+
(首选 context_pack.py 一次取全,search_index 仅作单项下钻兜底)
|
|
23
27
|
2. Read related historical PRDs from data/docs/prd/
|
|
24
28
|
3. Read data/docs/drafts/ for business drafts
|
|
25
29
|
4. Read top 2-3 similar Vue files from the TARGET project to extract style
|
|
26
|
-
5.
|
|
30
|
+
5. **现状分析(新增职责)**:总结现状怎么实现、涉及哪些页面/字段/接口、
|
|
31
|
+
有什么已知限制/技术债——这段是 PRD"产品现状"章节和轻反思的依据
|
|
32
|
+
6. Present ALL findings as ONE numbered list and ask for confirmation
|
|
27
33
|
in a single round (batch, not one-by-one). Follow up only on
|
|
28
34
|
genuinely ambiguous points.
|
|
29
35
|
6. After confirmation, generate:
|