@hupan56/wlkj 2.0.1 → 2.2.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 +88 -4
- package/bin/install-env.py +272 -0
- package/package.json +2 -1
- package/templates/qoder/scripts/install_qoderwork.py +5 -2
- package/templates/qoder/scripts/search_index.py +47 -1
- package/templates/qoder/skills/wl-code/SKILL.md +36 -0
- package/templates/qoder/skills/wl-prd/SKILL.md +73 -0
- package/templates/qoder/skills/wl-spec/SKILL.md +39 -0
- package/templates/qoder/skills/wl-test/SKILL.md +40 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -0
- package/templates/qoder/config.toml +0 -5
package/bin/cli.js
CHANGED
|
@@ -172,10 +172,12 @@ function doHelp() {
|
|
|
172
172
|
console.log("");
|
|
173
173
|
console.log("wlkj - AI 产品研发工作流 (v2.0)");
|
|
174
174
|
console.log("");
|
|
175
|
-
console.log("===
|
|
176
|
-
console.log(" npx wlkj
|
|
177
|
-
console.log("
|
|
178
|
-
console.log("
|
|
175
|
+
console.log("=== 环境安装 (什么都没装时先跑这个) ===");
|
|
176
|
+
console.log(" npx @hupan56/wlkj install-env 检测+自动装 Node/Python/git");
|
|
177
|
+
console.log(" npx @hupan56/wlkj install-env --check 只检测不装");
|
|
178
|
+
console.log("");
|
|
179
|
+
console.log("=== 一键安装 (装好环境后) ===");
|
|
180
|
+
console.log(" npx @hupan56/wlkj init [你的名字] 安装完整引擎 + 自动初始化");
|
|
179
181
|
console.log("");
|
|
180
182
|
console.log("=== 安装后怎么用 ===");
|
|
181
183
|
console.log(" 在 Qoder (IDE/Quest/QoderWork) 里:");
|
|
@@ -197,11 +199,93 @@ function doHelp() {
|
|
|
197
199
|
console.log("");
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
function doInstallEnv(checkOnly = false) {
|
|
203
|
+
const { execSync } = require("child_process");
|
|
204
|
+
console.log("\n=== 环境检测 ===\n");
|
|
205
|
+
|
|
206
|
+
// 检测函数
|
|
207
|
+
function check(cmd) {
|
|
208
|
+
try { return execSync(`${cmd} --version`, { encoding: "utf-8", timeout: 5000, stdio: "pipe" }).trim().split("\n")[0]; }
|
|
209
|
+
catch { return null; }
|
|
210
|
+
}
|
|
211
|
+
function has(cmd) {
|
|
212
|
+
try { execSync(`where ${cmd}`, { encoding: "utf-8", timeout: 3000, stdio: "pipe" }); return true; }
|
|
213
|
+
catch { try { execSync(`which ${cmd}`, { encoding: "utf-8", timeout: 3000, stdio: "pipe" }); return true; } catch { return false; } }
|
|
214
|
+
}
|
|
215
|
+
function install(name, wingetId, brewPkg) {
|
|
216
|
+
if (checkOnly) return false;
|
|
217
|
+
const isWin = process.platform === "win32";
|
|
218
|
+
const isMac = process.platform === "darwin";
|
|
219
|
+
try {
|
|
220
|
+
if (isWin && has("winget")) {
|
|
221
|
+
console.log(` 用 winget 装 ${name}...`);
|
|
222
|
+
execSync(`winget install --id ${wingetId} -e --source winget --accept-source-agreements --accept-package-agreements`,
|
|
223
|
+
{ stdio: "inherit", timeout: 300000 });
|
|
224
|
+
return true;
|
|
225
|
+
} else if (isMac && has("brew")) {
|
|
226
|
+
console.log(` 用 brew 装 ${name}...`);
|
|
227
|
+
execSync(`brew install ${brewPkg}`, { stdio: "inherit", timeout: 300000 });
|
|
228
|
+
return true;
|
|
229
|
+
} else if (!isWin && !isMac) {
|
|
230
|
+
// Linux: 尝试 apt/dnf/yum
|
|
231
|
+
for (const [mgr, pkg] of [["apt", brewPkg], ["dnf", brewPkg]]) {
|
|
232
|
+
if (has(mgr)) {
|
|
233
|
+
console.log(` 用 ${mgr} 装 ${name}...`);
|
|
234
|
+
execSync(`sudo ${mgr} install -y ${pkg}`, { stdio: "inherit", timeout: 300000 });
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (e) { console.log(` 自动安装失败: ${(e.message || "").slice(0, 80)}`); }
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const nodeVer = check("node");
|
|
244
|
+
const pyVer = check("python") || check("python3");
|
|
245
|
+
const gitVer = check("git");
|
|
246
|
+
|
|
247
|
+
console.log(` Node.js: ${nodeVer ? "[OK] " + nodeVer : "[缺失] (npx 命令需要)"}`);
|
|
248
|
+
console.log(` Python: ${pyVer ? "[OK] " + pyVer : "[缺失] (引擎需要)"}`);
|
|
249
|
+
console.log(` git: ${gitVer ? "[OK] " + gitVer : "[缺失] (可选, 核心功能不依赖)"}`);
|
|
250
|
+
|
|
251
|
+
if (checkOnly) {
|
|
252
|
+
console.log("\n仅检测。去掉 --check 自动安装。");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const need = [];
|
|
257
|
+
if (!nodeVer) {
|
|
258
|
+
console.log("");
|
|
259
|
+
if (nodeVer || install("Node.js", "OpenJS.NodeJS.LTS", "node")) { console.log(` [OK] Node.js 已装`); }
|
|
260
|
+
else { need.push("Node.js (手动: https://nodejs.org)"); }
|
|
261
|
+
}
|
|
262
|
+
if (!pyVer) {
|
|
263
|
+
console.log("");
|
|
264
|
+
if (install("Python", "Python.Python.3.12", "python@3.12")) { console.log(` [OK] Python 已装`); }
|
|
265
|
+
else { need.push("Python (手动: https://python.org, 勾 Add to PATH)"); }
|
|
266
|
+
}
|
|
267
|
+
if (!gitVer) {
|
|
268
|
+
console.log("");
|
|
269
|
+
if (install("git", "Git.Git", "git")) { console.log(` [OK] git 已装`); }
|
|
270
|
+
else { need.push("git (手动: https://git-scm.com, 可选)"); }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log("\n=== 结果 ===");
|
|
274
|
+
if (need.length === 0) {
|
|
275
|
+
console.log(" 环境就绪! 下一步: npx @hupan56/wlkj init");
|
|
276
|
+
console.log(" (新装的可能需要重新打开终端)");
|
|
277
|
+
} else {
|
|
278
|
+
console.log(" 以下需手动安装:");
|
|
279
|
+
need.forEach(n => console.log(" - " + n));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
200
283
|
const [,, cmd, ...rest] = process.argv;
|
|
201
284
|
const mapped = rest.map(a => a === "-p" ? "--priority" : a);
|
|
202
285
|
|
|
203
286
|
switch (cmd) {
|
|
204
287
|
case "init": doInit(rest[0]); break;
|
|
288
|
+
case "install-env": doInstallEnv(rest[0] === "--check"); break;
|
|
205
289
|
case "task": process.stdout.write(py("task.py", mapped)); break;
|
|
206
290
|
case "status": doStatus(); break;
|
|
207
291
|
case "session": process.stdout.write(py("add_session.py", rest)); break;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
install-env.py - 一键安装前置环境 (Node.js + Python + git)
|
|
5
|
+
|
|
6
|
+
在跑 npx @hupan56/wlkj init 之前, 如果什么都没装, 先跑这个。
|
|
7
|
+
它会检测并自动安装缺失的环境。
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python install-env.py # 检测 + 自动装缺失的
|
|
11
|
+
python install-env.py --check # 只检测, 不装
|
|
12
|
+
|
|
13
|
+
注意: 这个脚本本身需要 Python 才能跑。如果用户连 Python 都没有:
|
|
14
|
+
Windows: 去 python.org 下载安装 (勾 Add to PATH), 或用 winget:
|
|
15
|
+
winget install Python.Python.3.12
|
|
16
|
+
Mac: brew install python
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import os
|
|
21
|
+
import platform
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
28
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
29
|
+
except (AttributeError, TypeError, OSError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
IS_WIN = sys.platform == 'win32'
|
|
33
|
+
IS_MAC = sys.platform == 'darwin'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def run(cmd, **kw):
|
|
37
|
+
kw.setdefault('capture_output', True)
|
|
38
|
+
kw.setdefault('text', True)
|
|
39
|
+
kw.setdefault('encoding', 'utf-8')
|
|
40
|
+
kw.setdefault('errors', 'replace')
|
|
41
|
+
try:
|
|
42
|
+
return subprocess.run(cmd, **kw)
|
|
43
|
+
except FileNotFoundError:
|
|
44
|
+
import types
|
|
45
|
+
return types.SimpleNamespace(returncode=127, stdout='', stderr='not found')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def check(cmd_name, version_flag='--version'):
|
|
49
|
+
"""检查某个命令是否存在, 返回 (是否可用, 版本字符串)。"""
|
|
50
|
+
r = run([cmd_name, version_flag])
|
|
51
|
+
if r.returncode == 0:
|
|
52
|
+
return True, r.stdout.strip().split('\n')[0]
|
|
53
|
+
return False, None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def ask(prompt, default='y'):
|
|
57
|
+
if not sys.stdin.isatty():
|
|
58
|
+
return default.lower() in ('y', 'yes')
|
|
59
|
+
try:
|
|
60
|
+
ans = input(f'{prompt} [{default}] ').strip().lower() or default
|
|
61
|
+
return ans in ('y', 'yes')
|
|
62
|
+
except (EOFError, KeyboardInterrupt):
|
|
63
|
+
return default.lower() in ('y', 'yes')
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def has_winget():
|
|
67
|
+
return check('winget')[0]
|
|
68
|
+
|
|
69
|
+
def has_brew():
|
|
70
|
+
return check('brew')[0]
|
|
71
|
+
|
|
72
|
+
def has_choco():
|
|
73
|
+
return check('choco')[0]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ============================================================
|
|
77
|
+
# 安装器
|
|
78
|
+
# ============================================================
|
|
79
|
+
|
|
80
|
+
def install_node():
|
|
81
|
+
"""安装 Node.js。"""
|
|
82
|
+
print('\n--- 安装 Node.js ---')
|
|
83
|
+
if IS_WIN:
|
|
84
|
+
# 优先 winget
|
|
85
|
+
if has_winget():
|
|
86
|
+
print(' 用 winget 安装 Node.js LTS...')
|
|
87
|
+
r = run(['winget', 'install', '--id', 'OpenJS.NodeJS.LTS', '-e',
|
|
88
|
+
'--source', 'winget', '--accept-source-agreements',
|
|
89
|
+
'--accept-package-agreements'])
|
|
90
|
+
if r.returncode == 0:
|
|
91
|
+
print(' [OK] Node.js 安装成功 (可能需要新终端生效)')
|
|
92
|
+
return True
|
|
93
|
+
print(' winget 失败: ' + (r.stderr or '').strip()[:100])
|
|
94
|
+
# 回退 choco
|
|
95
|
+
if has_choco():
|
|
96
|
+
print(' 用 choco 安装...')
|
|
97
|
+
r = run(['choco', 'install', 'nodejs-lts', '-y'])
|
|
98
|
+
if r.returncode == 0:
|
|
99
|
+
return True
|
|
100
|
+
print(' 自动安装失败。手动下载: https://nodejs.org (选 LTS)')
|
|
101
|
+
return False
|
|
102
|
+
elif IS_MAC:
|
|
103
|
+
if has_brew():
|
|
104
|
+
print(' 用 brew 安装 Node.js...')
|
|
105
|
+
r = run(['brew', 'install', 'node'])
|
|
106
|
+
return r.returncode == 0
|
|
107
|
+
print(' 请先装 Homebrew: https://brew.sh')
|
|
108
|
+
return False
|
|
109
|
+
else:
|
|
110
|
+
# Linux
|
|
111
|
+
for mgr, cmd in [('apt', ['sudo', 'apt-get', 'install', '-y', 'nodejs', 'npm']),
|
|
112
|
+
('dnf', ['sudo', 'dnf', 'install', '-y', 'nodejs', 'npm']),
|
|
113
|
+
('yum', ['sudo', 'yum', 'install', '-y', 'nodejs', 'npm'])]:
|
|
114
|
+
if check(mgr)[0]:
|
|
115
|
+
print(f' 用 {mgr} 安装...')
|
|
116
|
+
r = run(cmd)
|
|
117
|
+
return r.returncode == 0
|
|
118
|
+
print(' 请手动安装: https://nodejs.org')
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def install_python():
|
|
123
|
+
"""安装 Python 3.9+。"""
|
|
124
|
+
print('\n--- 安装 Python ---')
|
|
125
|
+
if IS_WIN:
|
|
126
|
+
if has_winget():
|
|
127
|
+
print(' 用 winget 安装 Python 3.12...')
|
|
128
|
+
r = run(['winget', 'install', '--id', 'Python.Python.3.12', '-e',
|
|
129
|
+
'--source', 'winget', '--accept-source-agreements',
|
|
130
|
+
'--accept-package-agreements'])
|
|
131
|
+
if r.returncode == 0:
|
|
132
|
+
print(' [OK] Python 安装成功 (可能需要新终端生效)')
|
|
133
|
+
return True
|
|
134
|
+
print(' winget 失败: ' + (r.stderr or '').strip()[:100])
|
|
135
|
+
print(' 自动安装失败。手动下载: https://python.org (勾 Add to PATH)')
|
|
136
|
+
return False
|
|
137
|
+
elif IS_MAC:
|
|
138
|
+
if has_brew():
|
|
139
|
+
print(' 用 brew 安装 Python...')
|
|
140
|
+
r = run(['brew', 'install', 'python@3.12'])
|
|
141
|
+
return r.returncode == 0
|
|
142
|
+
print(' 请先装 Homebrew: https://brew.sh')
|
|
143
|
+
return False
|
|
144
|
+
else:
|
|
145
|
+
for mgr, cmd in [('apt', ['sudo', 'apt-get', 'install', '-y', 'python3', 'python3-pip']),
|
|
146
|
+
('dnf', ['sudo', 'dnf', 'install', '-y', 'python3']),
|
|
147
|
+
('yum', ['sudo', 'yum', 'install', '-y', 'python3'])]:
|
|
148
|
+
if check(mgr)[0]:
|
|
149
|
+
print(f' 用 {mgr} 安装...')
|
|
150
|
+
r = run(cmd)
|
|
151
|
+
return r.returncode == 0
|
|
152
|
+
print(' 请手动安装: https://python.org')
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def install_git():
|
|
157
|
+
"""安装 git。"""
|
|
158
|
+
print('\n--- 安装 git ---')
|
|
159
|
+
if IS_WIN:
|
|
160
|
+
if has_winget():
|
|
161
|
+
print(' 用 winget 安装 Git...')
|
|
162
|
+
r = run(['winget', 'install', '--id', 'Git.Git', '-e',
|
|
163
|
+
'--source', 'winget', '--accept-source-agreements',
|
|
164
|
+
'--accept-package-agreements'])
|
|
165
|
+
if r.returncode == 0:
|
|
166
|
+
print(' [OK] git 安装成功 (可能需要新终端生效)')
|
|
167
|
+
return True
|
|
168
|
+
print(' 自动安装失败。手动下载: https://git-scm.com/download/win')
|
|
169
|
+
return False
|
|
170
|
+
elif IS_MAC:
|
|
171
|
+
# Mac 上 git 通常自带 (装了 Xcode Command Line Tools)
|
|
172
|
+
r = run(['xcode-select', '--install'])
|
|
173
|
+
print(' 通过 Xcode Command Line Tools 安装...')
|
|
174
|
+
return True
|
|
175
|
+
else:
|
|
176
|
+
for mgr, cmd in [('apt', ['sudo', 'apt-get', 'install', '-y', 'git']),
|
|
177
|
+
('dnf', ['sudo', 'dnf', 'install', '-y', 'git']),
|
|
178
|
+
('yum', ['sudo', 'yum', 'install', '-y', 'git'])]:
|
|
179
|
+
if check(mgr)[0]:
|
|
180
|
+
print(f' 用 {mgr} 安装...')
|
|
181
|
+
r = run(cmd)
|
|
182
|
+
return r.returncode == 0
|
|
183
|
+
print(' 请手动安装: https://git-scm.com')
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ============================================================
|
|
188
|
+
# 主逻辑
|
|
189
|
+
# ============================================================
|
|
190
|
+
|
|
191
|
+
def main():
|
|
192
|
+
parser = argparse.ArgumentParser(
|
|
193
|
+
description='一键安装前置环境 (Node.js + Python + git)',
|
|
194
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
195
|
+
epilog='''安装完后跑:
|
|
196
|
+
npx @hupan56/wlkj init
|
|
197
|
+
|
|
198
|
+
如果连 Python 都没有 (无法跑本脚本):
|
|
199
|
+
Windows: winget install Python.Python.3.12
|
|
200
|
+
Mac: brew install python
|
|
201
|
+
''')
|
|
202
|
+
parser.add_argument('--check', action='store_true', help='只检测, 不安装')
|
|
203
|
+
args = parser.parse_args()
|
|
204
|
+
|
|
205
|
+
print('=' * 50)
|
|
206
|
+
print(' 环境检测' + (' (仅检测)' if args.check else ' + 自动安装'))
|
|
207
|
+
print('=' * 50)
|
|
208
|
+
|
|
209
|
+
# 检测当前状态
|
|
210
|
+
print('\n--- 当前状态 ---')
|
|
211
|
+
node_ok, node_ver = check('node')
|
|
212
|
+
py_ok, py_ver = check('python') or (sys.version_info >= (3, 9), f'Python {sys.version.split()[0]}')
|
|
213
|
+
git_ok, git_ver = check('git')
|
|
214
|
+
|
|
215
|
+
# Python 特殊: 本脚本在跑说明至少有 python, 但可能版本不够
|
|
216
|
+
if not py_ok:
|
|
217
|
+
py_ok = sys.version_info >= (3, 9)
|
|
218
|
+
py_ver = f'Python {sys.version_info[0]}.{sys.version_info[1]}'
|
|
219
|
+
|
|
220
|
+
print(f' Node.js: {"[OK] " + node_ver if node_ok else "[缺失]"}')
|
|
221
|
+
print(f' Python: {"[OK] " + py_ver if py_ok else "[缺失或版本低]"}')
|
|
222
|
+
print(f' git: {"[OK] " + git_ver if git_ok else "[缺失] (可选, 不装也能用核心功能)"}')
|
|
223
|
+
|
|
224
|
+
if args.check:
|
|
225
|
+
print('\n仅检测模式。缺少的环境手动装, 或去掉 --check 自动安装。')
|
|
226
|
+
return 0 if (node_ok and py_ok) else 1
|
|
227
|
+
|
|
228
|
+
# 按需安装
|
|
229
|
+
installed = []
|
|
230
|
+
if not node_ok:
|
|
231
|
+
if ask('\nNode.js 缺失 (npx 命令需要它), 自动安装?', 'y'):
|
|
232
|
+
if install_node():
|
|
233
|
+
installed.append('Node.js')
|
|
234
|
+
if not py_ok:
|
|
235
|
+
if ask('\nPython 版本过低或缺失, 自动安装?', 'y'):
|
|
236
|
+
if install_python():
|
|
237
|
+
installed.append('Python')
|
|
238
|
+
if not git_ok:
|
|
239
|
+
if ask('\ngit 缺失 (团队协作用, 核心功能不依赖), 自动安装?', 'y'):
|
|
240
|
+
if install_git():
|
|
241
|
+
installed.append('git')
|
|
242
|
+
|
|
243
|
+
# 重新检测
|
|
244
|
+
print('\n--- 安装后状态 ---')
|
|
245
|
+
node_ok2, _ = check('node')
|
|
246
|
+
# Python: 如果新装了, 当前进程还是旧的, 但 PATH 里应该有了
|
|
247
|
+
git_ok2, _ = check('git')
|
|
248
|
+
|
|
249
|
+
all_ok = node_ok or node_ok2
|
|
250
|
+
print(f' Node.js: {"[OK]" if all_ok else "[仍缺失, 可能需要新终端]"}')
|
|
251
|
+
print(f' Python: [OK] {py_ver}')
|
|
252
|
+
print(f' git: {"[OK]" if (git_ok or git_ok2) else "[缺失, 可选]"}')
|
|
253
|
+
|
|
254
|
+
if installed:
|
|
255
|
+
print(f'\n本次安装: {", ".join(installed)}')
|
|
256
|
+
print(' ⚠ 新装的环境可能需要**重新打开终端**才能生效')
|
|
257
|
+
|
|
258
|
+
if all_ok:
|
|
259
|
+
print('\n' + '=' * 50)
|
|
260
|
+
print(' 环境就绪! 下一步:')
|
|
261
|
+
print('=' * 50)
|
|
262
|
+
print('\n mkdir 我的项目 && cd 我的项目')
|
|
263
|
+
print(' npx @hupan56/wlkj init')
|
|
264
|
+
print('')
|
|
265
|
+
return 0
|
|
266
|
+
else:
|
|
267
|
+
print('\n Node.js 仍缺失, 请手动安装: https://nodejs.org')
|
|
268
|
+
return 1
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == '__main__':
|
|
272
|
+
sys.exit(main())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hupan56/wlkj",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
|
|
5
5
|
"bin": { "wlkj": "./bin/cli.js" },
|
|
6
6
|
"files": ["bin/", "templates/"],
|
|
@@ -9,3 +9,4 @@
|
|
|
9
9
|
"publishConfig": { "access": "public" },
|
|
10
10
|
"engines": { "node": ">=16" }
|
|
11
11
|
}
|
|
12
|
+
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
"""
|
|
3
3
|
install_qoderwork.py - 把 .qoder/skills/ 安装到 QoderWork 桌面端可识别的位置
|
|
4
4
|
|
|
5
|
-
QoderWork 从 %USERPROFILE%\\.
|
|
6
|
-
|
|
5
|
+
QoderWork 从 %USERPROFILE%\\.qoderwork\\skills\\ 加载技能(每个子目录一个 skill),
|
|
6
|
+
从 %USERPROFILE%\\.qoderwork\\commands\\ 加载斜杠命令。
|
|
7
|
+
本项目源在 <repo>/.qoder/skills/ 和 <repo>/.qoder/commands/,QoderWork 不会读项目
|
|
8
|
+
目录,必须软链/拷过去。
|
|
9
|
+
(注: 旧版本误用 ~/.qoderworkcn/,已废弃,官方路径为 ~/.qoderwork/)
|
|
7
10
|
|
|
8
11
|
为什么用 junction(mklink /J)而不是 symlink(mklink /D):
|
|
9
12
|
- junction 不需要管理员权限,普通用户可建
|
|
@@ -15,7 +15,7 @@ Usage:
|
|
|
15
15
|
python search_index.py --list
|
|
16
16
|
python search_index.py --vben
|
|
17
17
|
"""
|
|
18
|
-
import os, json, sys
|
|
18
|
+
import os, json, sys, time
|
|
19
19
|
|
|
20
20
|
# UTF-8 stdio (防御性: stdout 被捕获时不崩溃)
|
|
21
21
|
try:
|
|
@@ -69,7 +69,53 @@ def _match_keyword(query_word, kw):
|
|
|
69
69
|
return False
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
def _search_cache_key(query, platform):
|
|
73
|
+
"""缓存 key: query|platform|keyword-index mtime。"""
|
|
74
|
+
import hashlib
|
|
75
|
+
ki_path = os.path.join(BASE, 'data', 'index', 'keyword-index.json') if os.path.isdir(os.path.join(BASE, 'data', 'index')) else None
|
|
76
|
+
mtime = '%d' % os.path.getmtime(ki_path) if ki_path and os.path.isfile(ki_path) else '0'
|
|
77
|
+
raw = '{}|{}|{}'.format(query.lower(), platform or '', mtime)
|
|
78
|
+
return hashlib.md5(raw.encode('utf-8')).hexdigest()[:16]
|
|
79
|
+
|
|
80
|
+
|
|
72
81
|
def search_keywords(query, platform=None):
|
|
82
|
+
# 结果缓存: 同 query+platform+索引未变 → 直接返回缓存输出
|
|
83
|
+
cache_key = _search_cache_key(query, platform)
|
|
84
|
+
cache_path = os.path.join(BASE, '.qoder', '.runtime', 'search-cache-%s.txt' % cache_key)
|
|
85
|
+
if os.path.isfile(cache_path):
|
|
86
|
+
try:
|
|
87
|
+
age = time.time() - os.path.getmtime(cache_path)
|
|
88
|
+
if age < 86400: # 24h 有效
|
|
89
|
+
with open(cache_path, encoding='utf-8') as f:
|
|
90
|
+
sys.stdout.write(f.read())
|
|
91
|
+
return
|
|
92
|
+
except OSError:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
# 缓存未命中, 正常计算并写入缓存
|
|
96
|
+
import io
|
|
97
|
+
old_stdout = sys.stdout
|
|
98
|
+
sys.stdout = buf = io.StringIO()
|
|
99
|
+
try:
|
|
100
|
+
_search_keywords_impl(query, platform)
|
|
101
|
+
finally:
|
|
102
|
+
sys.stdout = old_stdout
|
|
103
|
+
output = buf.getvalue()
|
|
104
|
+
sys.stdout.write(output)
|
|
105
|
+
# 写缓存 (best-effort)
|
|
106
|
+
try:
|
|
107
|
+
cache_dir = os.path.join(BASE, '.qoder', '.runtime')
|
|
108
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
109
|
+
tmp = cache_path + '.tmp'
|
|
110
|
+
with open(tmp, 'w', encoding='utf-8') as f:
|
|
111
|
+
f.write(output)
|
|
112
|
+
os.replace(tmp, cache_path)
|
|
113
|
+
except OSError:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _search_keywords_impl(query, platform=None):
|
|
118
|
+
"""实际的搜索逻辑 (原 search_keywords 内容)。"""
|
|
73
119
|
ki = load_index('keyword-index.json', hint='Run: python git_sync.py')
|
|
74
120
|
if ki is None:
|
|
75
121
|
return
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wl-code
|
|
3
|
+
description: "按开发 Spec 严格实现代码(/wl-code 命令入口)。Implement code following a Spec strictly. 用户说'开始写代码''按规格实现''实现这个接口''把这个需求做出来'或输入 /wl-code 时触发。需用户确认(DANGEROUS)。"
|
|
4
|
+
trigger: "Spec 已确认待实现;用户说'开始写代码''按spec实现''/wl-code'"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# wl-code — 按 Spec 实现代码(/wl-code 命令入口)
|
|
8
|
+
|
|
9
|
+
> 这是 `/wl-code` 命令的 skill 入口。让 QoderWork 的 `/` 列表也能用 `/wl-code`。
|
|
10
|
+
> ⚠️ DANGEROUS 操作(写代码),执行前向用户确认范围。
|
|
11
|
+
> 完整规则见功能型 skill `.qoder/skills/spec-coder/SKILL.md`
|
|
12
|
+
> 和命令 `.qoder/commands/wl-code.md`。
|
|
13
|
+
|
|
14
|
+
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
15
|
+
|
|
16
|
+
- `.qoder/.developer` — 当前开发者
|
|
17
|
+
- `.qoder/.current-task` — 当前活动任务(找对应的 spec)
|
|
18
|
+
- 活动任务的 spec:`workspace/specs/` 或 `workspace/tasks/{id}/spec.md`
|
|
19
|
+
|
|
20
|
+
## 执行
|
|
21
|
+
|
|
22
|
+
1. **加载 Spec**:按 REQ-ID 找 spec;没参数就读 `.qoder/.current-task`
|
|
23
|
+
2. **搜相关代码**:
|
|
24
|
+
```bash
|
|
25
|
+
python .qoder/scripts/search_index.py <关键词>
|
|
26
|
+
```
|
|
27
|
+
读相关文件了解现有模式。
|
|
28
|
+
3. **按 spec-coder skill 实现**:严格按 spec,遵循 data/code/ 的团队约定
|
|
29
|
+
4. **自检**:
|
|
30
|
+
- [ ] Spec 所有需求都覆盖
|
|
31
|
+
- [ ] 无 TODO/FIXME
|
|
32
|
+
- [ ] 风格跟周围代码一致
|
|
33
|
+
- [ ] 有错误处理
|
|
34
|
+
5. **报告**:告诉用户实现了什么,建议下一步 `/wl-test`
|
|
35
|
+
|
|
36
|
+
详细编码规范见 `.qoder/skills/spec-coder/SKILL.md`。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wl-prd
|
|
3
|
+
description: "生成 PRD + 平台匹配的原型(/wl-prd 命令入口)。Generate PRD with platform-aware prototype. 用户说'写个需求''生成PRD''做个原型''保单这块加个功能''写个产品需求文档'或输入 /wl-prd 时触发。先问平台!"
|
|
4
|
+
trigger: "用户描述需求、要 PRD、要原型、要 mockup,或直接 /wl-prd"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# wl-prd — PRD + 原型生成(/wl-prd 命令入口)
|
|
8
|
+
|
|
9
|
+
> 这是 `/wl-prd` 命令的 skill 入口。QoderWork 的 `/` 列表读 skills,
|
|
10
|
+
> 所以本 skill 让 QoderWork 也能用 `/wl-prd`,与 Qoder IDE 保持一致。
|
|
11
|
+
> 完整工作流见同名 command 文件 `.qoder/commands/wl-prd.md` 和
|
|
12
|
+
> 功能型 skill `.qoder/skills/prd-generator/SKILL.md`。
|
|
13
|
+
|
|
14
|
+
## ⚠️ STEP 0: 必须先问平台(任何分析前)
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
这个需求是针对哪个平台?
|
|
18
|
+
|
|
19
|
+
1. Web 管理端 (fywl-ui) - Ant Design Vue + VxeGrid 风格
|
|
20
|
+
2. APP 移动端 (Carmg-H5) - Vant 风格
|
|
21
|
+
3. 两端都要
|
|
22
|
+
|
|
23
|
+
请选择 (1/2/3):
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**绝不自动判断。绝不假设。绝不跳过。永远先问。等用户回答。**
|
|
27
|
+
|
|
28
|
+
| 回答 | 平台 | 项目 | 搜索 flag | 原型模板 |
|
|
29
|
+
|------|------|------|-----------|----------|
|
|
30
|
+
| 1 / Web / PC / 管理端 | Web | fywl-ui | `--platform web` | prototype-web.html |
|
|
31
|
+
| 2 / APP / H5 / 移动端 | APP | Carmg-H5 | `--platform app` | prototype-app.html |
|
|
32
|
+
| 3 / 都要 / 两端 | Both | Both | 都跑 | 两个模板,两份原型 |
|
|
33
|
+
|
|
34
|
+
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
35
|
+
|
|
36
|
+
会话开始先读(不存在就跳过,不要报错中断):
|
|
37
|
+
- `.qoder/.developer` — 当前开发者名(产出存到 `workspace/members/{dev}/drafts/`)
|
|
38
|
+
- `.qoder/.current-task` — 当前活动任务
|
|
39
|
+
- `data/index/.index-meta.json` — 知识图谱新鲜度(过期提示先 `/wl-init`,不阻塞)
|
|
40
|
+
|
|
41
|
+
## 完整流程
|
|
42
|
+
|
|
43
|
+
按 `.qoder/commands/wl-prd.md` 执行(含 Reference/Brainstorm/Planning/Quick
|
|
44
|
+
四种模式)。核心步骤:
|
|
45
|
+
|
|
46
|
+
1. **一次取全上下文**(fast path):
|
|
47
|
+
```bash
|
|
48
|
+
python .qoder/scripts/context_pack.py <业务关键词> --platform <web|app>
|
|
49
|
+
```
|
|
50
|
+
2. 读历史 PRD、业务草稿、风格 token
|
|
51
|
+
3. 一次性确认发现(不要一条一条问)
|
|
52
|
+
4. 生成 PRD + 原型(从平台模板开始,填真实字段,diff 高亮)
|
|
53
|
+
5. **EVA 质量门禁**:
|
|
54
|
+
```bash
|
|
55
|
+
python .qoder/scripts/eval_prd.py <draft-prd.md> <prototype.html>
|
|
56
|
+
```
|
|
57
|
+
<80% 就修到 PASS。
|
|
58
|
+
6. 发布 + 自动同步:
|
|
59
|
+
```bash
|
|
60
|
+
python .qoder/scripts/team_sync.py push
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
详细生成规则、样式 token、原型规则见:
|
|
64
|
+
- 功能型 skill:`.qoder/skills/prd-generator/SKILL.md`
|
|
65
|
+
- 原型规则:`.qoder/skills/prototype-generator/SKILL.md`
|
|
66
|
+
- 命令完整定义:`.qoder/commands/wl-prd.md`
|
|
67
|
+
|
|
68
|
+
## REQ-ID 分配(并发安全)
|
|
69
|
+
|
|
70
|
+
**必须**用原子分配器,禁止手动扫描最大值:
|
|
71
|
+
```bash
|
|
72
|
+
python -c "import sys; sys.path.insert(0,'.qoder/scripts'); from common.reqid import allocate_req_id; n=allocate_req_id(); print('REQ-%d-%03d' % (__import__('datetime').date.today().year, n))"
|
|
73
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wl-spec
|
|
3
|
+
description: "从已确认 PRD 生成开发 Spec 文档(/wl-spec 命令入口)。Generate or review technical Spec from PRD + Design. 用户说'生成规格''写spec''出技术方案''把PRD转开发文档'或输入 /wl-spec 时触发。"
|
|
4
|
+
trigger: "PRD 确认/发布后;用户说'生成Spec''写技术规格''/wl-spec'"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# wl-spec — 生成开发 Spec(/wl-spec 命令入口)
|
|
8
|
+
|
|
9
|
+
> 这是 `/wl-spec` 命令的 skill 入口。让 QoderWork 的 `/` 列表也能用 `/wl-spec`。
|
|
10
|
+
> 完整规则见功能型 skill `.qoder/skills/spec-generator/SKILL.md`
|
|
11
|
+
> 和命令 `.qoder/commands/wl-spec.md`。
|
|
12
|
+
|
|
13
|
+
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
14
|
+
|
|
15
|
+
- `.qoder/.developer` — 当前开发者
|
|
16
|
+
- `.qoder/.current-task` — 当前任务(决定 spec 存哪个 task 目录)
|
|
17
|
+
- 扫描 PRD 来源:`data/docs/prd/`(已发布)+
|
|
18
|
+
`workspace/members/{dev}/drafts/REQ-*.md`(草稿),找出还没 Spec 的 PRD
|
|
19
|
+
- 字段命名约定:`python .qoder/scripts/search_index.py --field <字段名>`
|
|
20
|
+
- 团队 Java 约定(MyBatis Plus + RESTful + BigDecimal 金额)见
|
|
21
|
+
`.qoder/skills/spec-generator/SKILL.md` 的 Step 3
|
|
22
|
+
|
|
23
|
+
## 执行
|
|
24
|
+
|
|
25
|
+
1. 定位 PRD(按 REQ-ID 或最新发布的)
|
|
26
|
+
2. 读 PRD + 相关代码(用 search_index.py 找现有实现模式)
|
|
27
|
+
3. 按 spec-generator skill 生成 spec.md(接口/字段/数据模型/验收标准)
|
|
28
|
+
4. 存到 `workspace/specs/spec-{REQ-ID}-{desc}.md`
|
|
29
|
+
5. 通知开发评审
|
|
30
|
+
6. 发布后自动同步:
|
|
31
|
+
```bash
|
|
32
|
+
python .qoder/scripts/team_sync.py push
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Review 模式
|
|
36
|
+
|
|
37
|
+
带 `review` 参数 → 读现有 spec 做评审,给出改进建议,不重新生成。
|
|
38
|
+
|
|
39
|
+
详细生成规则见 `.qoder/skills/spec-generator/SKILL.md`。
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wl-test
|
|
3
|
+
description: "为已实现代码生成单元测试(/wl-test 命令入口)。Generate unit tests for implemented code. 用户说'写单元测试''补测试''覆盖一下''写个test'或输入 /wl-test 时触发。需用户确认(DANGEROUS)。"
|
|
4
|
+
trigger: "代码实现完成;用户说'写测试''补单测''生成test''/wl-test'"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# wl-test — 生成单元测试(/wl-test 命令入口)
|
|
8
|
+
|
|
9
|
+
> 这是 `/wl-test` 命令的 skill 入口。让 QoderWork 的 `/` 列表也能用 `/wl-test`。
|
|
10
|
+
> ⚠️ DANGEROUS 操作(写测试文件),执行前向用户确认。
|
|
11
|
+
> 完整规则见功能型 skill `.qoder/skills/test-generator/SKILL.md`
|
|
12
|
+
> 和命令 `.qoder/commands/wl-test.md`。
|
|
13
|
+
|
|
14
|
+
## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
|
|
15
|
+
|
|
16
|
+
- `.qoder/.developer` — 当前开发者
|
|
17
|
+
- `.qoder/.current-task` — 当前活动任务(找刚实现的代码)
|
|
18
|
+
- 活动 task 的实现代码 + 对应 spec
|
|
19
|
+
|
|
20
|
+
## 执行
|
|
21
|
+
|
|
22
|
+
1. **找目标代码**:
|
|
23
|
+
```bash
|
|
24
|
+
python .qoder/scripts/search_index.py <类名或关键词>
|
|
25
|
+
```
|
|
26
|
+
没参数就用当前 task 的实现代码。
|
|
27
|
+
2. **读代码 + spec**:理解要测什么
|
|
28
|
+
3. **按 test-generator skill 生成**,覆盖:
|
|
29
|
+
- 每个函数的 happy path
|
|
30
|
+
- 边界值(null/空/边界)
|
|
31
|
+
- 错误处理
|
|
32
|
+
- 集成点
|
|
33
|
+
4. **自检**:
|
|
34
|
+
- [ ] 测试能跑
|
|
35
|
+
- [ ] 覆盖主场景
|
|
36
|
+
- [ ] 断言正确
|
|
37
|
+
- [ ] 遵循团队测试约定
|
|
38
|
+
5. **报告**:告诉用户生成了哪些测试,建议下一步 `/wl-commit`
|
|
39
|
+
|
|
40
|
+
详细测试规范见 `.qoder/skills/test-generator/SKILL.md`。
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# 完整使用说明
|
|
2
|
+
|
|
3
|
+
> 从零开始,一步一步来。连 Python 都没有也能跟着做。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 第一步:装前置环境(10 分钟)
|
|
8
|
+
|
|
9
|
+
### 1.1 装 Node.js(必须)
|
|
10
|
+
|
|
11
|
+
Node.js 用来跑 `npx` 安装命令。
|
|
12
|
+
|
|
13
|
+
**Windows:**
|
|
14
|
+
1. 打开 https://nodejs.org
|
|
15
|
+
2. 下载 **LTS 版**(左边那个绿色按钮)
|
|
16
|
+
3. 双击安装,一路点"下一步",全部默认
|
|
17
|
+
4. 装完按 `Win + R`,输 `cmd` 回车,在黑窗口里输:
|
|
18
|
+
```
|
|
19
|
+
node --version
|
|
20
|
+
```
|
|
21
|
+
看到 `v20.x.x` 之类的就成功了
|
|
22
|
+
|
|
23
|
+
**Mac:**
|
|
24
|
+
1. 打开终端(Launchpad → 其他 → 终端)
|
|
25
|
+
2. 输:`brew install node`(没装 brew 的先去 brew.sh 装)
|
|
26
|
+
3. 验证:`node --version`
|
|
27
|
+
|
|
28
|
+
### 1.2 装 Python(必须)
|
|
29
|
+
|
|
30
|
+
Python 用来跑工作流的脚本引擎。
|
|
31
|
+
|
|
32
|
+
**Windows:**
|
|
33
|
+
1. 打开 https://www.python.org/downloads/
|
|
34
|
+
2. 点黄色大按钮 "Download Python 3.x.x"
|
|
35
|
+
3. 双击安装包,**重要:勾选底部 "Add Python to PATH"**,再点 "Install Now"
|
|
36
|
+
4. 装完在 cmd 里输:
|
|
37
|
+
```
|
|
38
|
+
python --version
|
|
39
|
+
```
|
|
40
|
+
看到 `Python 3.9.x` 或更高就成功了
|
|
41
|
+
|
|
42
|
+
**Mac:**
|
|
43
|
+
1. 终端输:`brew install python`
|
|
44
|
+
2. 验证:`python3 --version`
|
|
45
|
+
|
|
46
|
+
### 1.3 装 git(推荐,不装也能用大部分功能)
|
|
47
|
+
|
|
48
|
+
git 用来团队协作和拉取源码。**没有 git 不影响写 PRD、建任务、出报告。**
|
|
49
|
+
|
|
50
|
+
**Windows:**
|
|
51
|
+
1. 打开 https://git-scm.com/download/win
|
|
52
|
+
2. 下载自动安装包,双击安装,全部默认
|
|
53
|
+
3. 验证:`git --version`
|
|
54
|
+
|
|
55
|
+
**Mac:**
|
|
56
|
+
- 打开终端,第一次输 `git` 时 Mac 会提示安装,点"安装"即可
|
|
57
|
+
- 或手动:`brew install git`
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 第二步:创建项目并安装工作流(2 分钟)
|
|
62
|
+
|
|
63
|
+
### 2.1 建项目文件夹
|
|
64
|
+
|
|
65
|
+
**Windows:**
|
|
66
|
+
```
|
|
67
|
+
mkdir D:\我的项目
|
|
68
|
+
cd D:\我的项目
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Mac:**
|
|
72
|
+
```
|
|
73
|
+
mkdir ~/我的项目
|
|
74
|
+
cd ~/我的项目
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2.2 一键安装
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
npx @hupan56/wlkj init
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
它会自动:
|
|
84
|
+
- 下载并安装完整工作流引擎
|
|
85
|
+
- 问你叫什么名字(或从 git 配置读)
|
|
86
|
+
- 注册你的身份
|
|
87
|
+
- 如果有 git,自动配置
|
|
88
|
+
- 如果配了源码地址,自动拉取源码并建索引
|
|
89
|
+
|
|
90
|
+
看到这个就成功了:
|
|
91
|
+
```
|
|
92
|
+
✓ 开发者: 你的名字
|
|
93
|
+
安装完成! 现在可以开始了
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> 也可以指定名字:`npx @hupan56/wlkj init 小王`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 第三步:开始使用
|
|
101
|
+
|
|
102
|
+
### 方式 A:用 Qoder 客户端(推荐)
|
|
103
|
+
|
|
104
|
+
如果你装了 **Qoder IDE / Quest / QoderWork**:
|
|
105
|
+
|
|
106
|
+
1. 用 Qoder 打开你刚创建的项目文件夹
|
|
107
|
+
2. 在对话框里直接说中文:
|
|
108
|
+
- "写个保险异常筛选的需求"
|
|
109
|
+
- "查一下考勤代码在哪"
|
|
110
|
+
- "建个任务:登录优化"
|
|
111
|
+
3. 或输入 `/` 看所有命令(`/wl-prd` `/wl-search` 等)
|
|
112
|
+
|
|
113
|
+
**看不到 `/` 命令?**
|
|
114
|
+
- Quest 模式:新建一个对话
|
|
115
|
+
- QoderWork:跑 `python .qoder/scripts/install_qoderwork.py`,然后重启 QoderWork
|
|
116
|
+
|
|
117
|
+
### 方式 B:用命令行(无 Qoder 时)
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
python .qoder/scripts/search_index.py 保险
|
|
121
|
+
python .qoder/scripts/task.py create "登录优化"
|
|
122
|
+
python .qoder/scripts/report.py daily
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 全部功能速查
|
|
128
|
+
|
|
129
|
+
### 写需求 + 原型
|
|
130
|
+
|
|
131
|
+
| 做什么 | 说什么 / 命令 |
|
|
132
|
+
|--------|-------------|
|
|
133
|
+
| 写完整需求 | "写个 XXX 的需求" 或 `/wl-prd` |
|
|
134
|
+
| 写小改动 | "快速写个 XXX" |
|
|
135
|
+
| 头脑风暴 | "脑暴一下 XXX" |
|
|
136
|
+
| 画原型 | 随需求自动生成 |
|
|
137
|
+
| 评审 PRD | "评审这个 PRD" |
|
|
138
|
+
|
|
139
|
+
### 查代码(不懂代码也能查)
|
|
140
|
+
|
|
141
|
+
| 做什么 | 说什么 / 命令 |
|
|
142
|
+
|--------|-------------|
|
|
143
|
+
| 找业务代码 | "保险的代码在哪" 或 `/wl-search 保险` |
|
|
144
|
+
| 找 API | `/wl-search --api 考勤` |
|
|
145
|
+
| 找字段用法 | `/wl-search --field nickName` |
|
|
146
|
+
| 找同类页面 | `/wl-search --style table` |
|
|
147
|
+
| 一键全上下文 | `python .qoder/scripts/context_pack.py 保险 --platform web` |
|
|
148
|
+
|
|
149
|
+
### 管任务
|
|
150
|
+
|
|
151
|
+
| 做什么 | 说什么 / 命令 |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| 建任务 | "建个任务:登录优化" |
|
|
154
|
+
| 设截止日期 | `python .qoder/scripts/task.py set-due 任务名 2026-07-15` |
|
|
155
|
+
| 标记依赖 | `python .qoder/scripts/task.py block A B`(A 被 B 阻塞) |
|
|
156
|
+
| 看能开始的 | `python .qoder/scripts/task.py list --ready` |
|
|
157
|
+
| 看被卡住的 | `python .qoder/scripts/task.py list --blocked` |
|
|
158
|
+
| 看甘特图 | `python .qoder/scripts/task.py gantt` |
|
|
159
|
+
| 完成任务 | "这个任务做完了" |
|
|
160
|
+
|
|
161
|
+
### 看进度 + 出报告
|
|
162
|
+
|
|
163
|
+
| 做什么 | 说什么 / 命令 |
|
|
164
|
+
|--------|-------------|
|
|
165
|
+
| 看项目健康度 | "项目怎么样" 或 `python .qoder/scripts/status.py` |
|
|
166
|
+
| 出日报 | "今天干了啥" 或 `python .qoder/scripts/report.py daily` |
|
|
167
|
+
| 出周报 | `python .qoder/scripts/report.py weekly` |
|
|
168
|
+
|
|
169
|
+
### 导出
|
|
170
|
+
|
|
171
|
+
| 做什么 | 命令 |
|
|
172
|
+
|--------|------|
|
|
173
|
+
| 导 Jira CSV | `python .qoder/scripts/export.py jira` |
|
|
174
|
+
| 导 OpenAPI | `python .qoder/scripts/export.py openapi --project 你的项目` |
|
|
175
|
+
| 查调用图 | `python .qoder/scripts/export.py callgraph --class ClassName` |
|
|
176
|
+
|
|
177
|
+
### 团队协作
|
|
178
|
+
|
|
179
|
+
| 做什么 | 说什么 / 命令 |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| 同步给团队 | "同步" 或 `python .qoder/scripts/team_sync.py push` |
|
|
182
|
+
| 拉取队友最新 | "拉取最新" 或 `python .qoder/scripts/team_sync.py pull` |
|
|
183
|
+
| 看同步状态 | `python .qoder/scripts/team_sync.py status` |
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 团队配置(团队负责人做一次)
|
|
188
|
+
|
|
189
|
+
### 填源码仓库地址
|
|
190
|
+
|
|
191
|
+
打开 `.qoder/config.yaml`,找到这段,改成你们的 git 地址:
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
git_sync:
|
|
195
|
+
projects:
|
|
196
|
+
你的项目名: # ← 改成你的项目名
|
|
197
|
+
url: http://你的git服务器/你的项目.git # ← 改成你的
|
|
198
|
+
branch: main # ← 改成你的主分支
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
> 不知道填什么?注释掉整段,手动把代码放到 `data/code/项目名/` 也行。
|
|
202
|
+
|
|
203
|
+
### 注册周五自动更新(全队只需一台机器)
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
python .qoder/scripts/setup_weekly_cron.bat
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 配飞书通知(可选)
|
|
210
|
+
|
|
211
|
+
`.qoder/config.yaml` 里:
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
feishu:
|
|
215
|
+
enabled: true
|
|
216
|
+
webhook_url: "https://open.feishu.cn/open-apis/bot/v2/hook/你的token"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 常见问题
|
|
222
|
+
|
|
223
|
+
### 安装时
|
|
224
|
+
|
|
225
|
+
| 问题 | 解决 |
|
|
226
|
+
|------|------|
|
|
227
|
+
| `npx` 命令找不到 | 先装 Node.js(见第一步) |
|
|
228
|
+
| `python` 命令找不到 | 先装 Python(见第一步),**勾选 Add to PATH** |
|
|
229
|
+
| setup.py 报错 | 跑 `python .qoder/scripts/init_doctor.py --fix` 自动修复 |
|
|
230
|
+
|
|
231
|
+
### 使用时
|
|
232
|
+
|
|
233
|
+
| 问题 | 解决 |
|
|
234
|
+
|------|------|
|
|
235
|
+
| 搜索没结果 | 先跑 `python .qoder/scripts/setup.py` 建索引 |
|
|
236
|
+
| Qoder 里看不到 `/` 命令 | 新建对话 / 重启 QoderWork |
|
|
237
|
+
| 命令报错 | `python .qoder/scripts/init_doctor.py --fix` |
|
|
238
|
+
| 索引过期 | `python .qoder/scripts/git_sync.py --index-only` |
|
|
239
|
+
|
|
240
|
+
### 速查
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
安装 → npx @hupan56/wlkj init
|
|
244
|
+
环境修复 → python .qoder/scripts/init_doctor.py --fix
|
|
245
|
+
重新初始化 → python .qoder/scripts/setup.py
|
|
246
|
+
性能测试 → python .qoder/scripts/benchmark.py
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 环境要求总结
|
|
252
|
+
|
|
253
|
+
| 环境 | 必须? | 版本 |
|
|
254
|
+
|------|--------|------|
|
|
255
|
+
| **Node.js** | 必须(装工作流用) | 16+ |
|
|
256
|
+
| **Python** | 必须(跑引擎) | 3.9+ |
|
|
257
|
+
| **git** | 推荐(团队协作用,不装也能用核心功能) | 任意 |
|
|
258
|
+
| **Qoder 客户端** | 推荐(AI 对话驱动,不装也能用命令行) | IDE/Quest/QoderWork 任一 |
|
|
259
|
+
| **飞书** | 可选(通知推送) | 有群机器人即可 |
|