@hupan56/wlkj 2.0.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.
Files changed (87) hide show
  1. package/bin/cli.js +213 -0
  2. package/package.json +11 -0
  3. package/templates/cli.js +198 -0
  4. package/templates/qoder/commands/wl-code.md +43 -0
  5. package/templates/qoder/commands/wl-commit.md +30 -0
  6. package/templates/qoder/commands/wl-init.md +80 -0
  7. package/templates/qoder/commands/wl-insight.md +51 -0
  8. package/templates/qoder/commands/wl-prd.md +199 -0
  9. package/templates/qoder/commands/wl-report.md +166 -0
  10. package/templates/qoder/commands/wl-search.md +52 -0
  11. package/templates/qoder/commands/wl-spec.md +18 -0
  12. package/templates/qoder/commands/wl-status.md +51 -0
  13. package/templates/qoder/commands/wl-task.md +71 -0
  14. package/templates/qoder/commands/wl-test.md +42 -0
  15. package/templates/qoder/config.toml +5 -0
  16. package/templates/qoder/config.yaml +141 -0
  17. package/templates/qoder/hooks/inject-workflow-state.py +117 -0
  18. package/templates/qoder/hooks/session-start.py +204 -0
  19. package/templates/qoder/rules/wl-pipeline.md +105 -0
  20. package/templates/qoder/scripts/add_session.py +245 -0
  21. package/templates/qoder/scripts/benchmark.py +209 -0
  22. package/templates/qoder/scripts/build_style_index.py +268 -0
  23. package/templates/qoder/scripts/code_index.py +41 -0
  24. package/templates/qoder/scripts/collect_prds.py +31 -0
  25. package/templates/qoder/scripts/common/__init__.py +0 -0
  26. package/templates/qoder/scripts/common/active_task.py +230 -0
  27. package/templates/qoder/scripts/common/atomicio.py +172 -0
  28. package/templates/qoder/scripts/common/developer.py +161 -0
  29. package/templates/qoder/scripts/common/eval_api.py +144 -0
  30. package/templates/qoder/scripts/common/feishu.py +278 -0
  31. package/templates/qoder/scripts/common/filelock.py +211 -0
  32. package/templates/qoder/scripts/common/identity.py +285 -0
  33. package/templates/qoder/scripts/common/mentions.py +134 -0
  34. package/templates/qoder/scripts/common/paths.py +311 -0
  35. package/templates/qoder/scripts/common/reqid.py +218 -0
  36. package/templates/qoder/scripts/common/search_engine.py +205 -0
  37. package/templates/qoder/scripts/common/task_utils.py +342 -0
  38. package/templates/qoder/scripts/common/terms.py +234 -0
  39. package/templates/qoder/scripts/common/utf8.py +38 -0
  40. package/templates/qoder/scripts/context_pack.py +196 -0
  41. package/templates/qoder/scripts/eval_prd.py +225 -0
  42. package/templates/qoder/scripts/export.py +487 -0
  43. package/templates/qoder/scripts/git_sync.py +1087 -0
  44. package/templates/qoder/scripts/handoff.py +22 -0
  45. package/templates/qoder/scripts/init_developer.py +76 -0
  46. package/templates/qoder/scripts/init_doctor.py +527 -0
  47. package/templates/qoder/scripts/install_qoderwork.py +339 -0
  48. package/templates/qoder/scripts/learn.py +67 -0
  49. package/templates/qoder/scripts/notify.py +5 -0
  50. package/templates/qoder/scripts/parse_prds.py +33 -0
  51. package/templates/qoder/scripts/report.py +281 -0
  52. package/templates/qoder/scripts/role.py +39 -0
  53. package/templates/qoder/scripts/run_weekly_update.bat +17 -0
  54. package/templates/qoder/scripts/run_weekly_update.sh +20 -0
  55. package/templates/qoder/scripts/search_index.py +352 -0
  56. package/templates/qoder/scripts/setup.py +453 -0
  57. package/templates/qoder/scripts/setup_weekly_cron.bat +22 -0
  58. package/templates/qoder/scripts/setup_weekly_cron.sh +19 -0
  59. package/templates/qoder/scripts/status.py +389 -0
  60. package/templates/qoder/scripts/syncgate.py +330 -0
  61. package/templates/qoder/scripts/task.py +954 -0
  62. package/templates/qoder/scripts/team.py +29 -0
  63. package/templates/qoder/scripts/team_sync.py +419 -0
  64. package/templates/qoder/scripts/workspace_init.py +102 -0
  65. package/templates/qoder/settings.json +53 -0
  66. package/templates/qoder/skills/design-review/SKILL.md +25 -0
  67. package/templates/qoder/skills/prd-generator/SKILL.md +180 -0
  68. package/templates/qoder/skills/prd-review/SKILL.md +36 -0
  69. package/templates/qoder/skills/prototype-generator/SKILL.md +141 -0
  70. package/templates/qoder/skills/spec-coder/SKILL.md +69 -0
  71. package/templates/qoder/skills/spec-generator/SKILL.md +67 -0
  72. package/templates/qoder/skills/test-generator/SKILL.md +72 -0
  73. package/templates/qoder/skills/wl-commit/SKILL.md +76 -0
  74. package/templates/qoder/skills/wl-init/SKILL.md +67 -0
  75. package/templates/qoder/skills/wl-insight/SKILL.md +81 -0
  76. package/templates/qoder/skills/wl-report/SKILL.md +87 -0
  77. package/templates/qoder/skills/wl-search/SKILL.md +75 -0
  78. package/templates/qoder/skills/wl-status/SKILL.md +61 -0
  79. package/templates/qoder/skills/wl-task/SKILL.md +58 -0
  80. package/templates/qoder/templates/prd-full-template.md +103 -0
  81. package/templates/qoder/templates/prd-quick-template.md +69 -0
  82. package/templates/qoder/templates/prototype-app.html +344 -0
  83. package/templates/qoder/templates/prototype-web.html +310 -0
  84. package/templates/root/AGENTS.md +182 -0
  85. package/templates/root/README-pipeline.md +56 -0
  86. package/templates/root/ROLES.md +85 -0
  87. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -0
package/bin/cli.js ADDED
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ // wlkj - workflow toolkit
3
+
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const { execSync } = require("child_process");
7
+
8
+ const T = path.join(__dirname, "..", "templates");
9
+
10
+ function py(script, args = []) {
11
+ const p = path.join(process.cwd(), ".qoder", "scripts", script);
12
+ if (!fs.existsSync(p)) { console.log("请先运行: npx wlkj init"); process.exit(1); }
13
+ try {
14
+ return execSync(`python "${p}" ${args.map(a => `"${a}"`).join(" ")}`, { cwd: process.cwd(), encoding: "utf-8", timeout: 15000 });
15
+ } catch (e) { return (e.stdout || e.message); }
16
+ }
17
+
18
+ function cp(src, dest) {
19
+ const s = path.join(T, src), d = path.join(process.cwd(), dest);
20
+ if (!fs.existsSync(s)) return;
21
+ fs.mkdirSync(path.dirname(d), { recursive: true });
22
+ if (!fs.existsSync(d)) { fs.copyFileSync(s, d); return true; }
23
+ return false;
24
+ }
25
+
26
+ function gitOp(op) {
27
+ const cwd = process.cwd();
28
+ const cmds = {
29
+ "提交": 'git add . && git commit -m "update [ai-assisted]" && git push',
30
+ "推送": "git push",
31
+ "拉取最新": "git pull",
32
+ "同步": "git pull",
33
+ "查看状态": "git status",
34
+ "提交PRD": 'git add workspace/specs/prd/ && git commit -m "docs(ai): PRD update [ai-generated]" && git push',
35
+ "提交Spec": 'git add workspace/specs/ && git commit -m "docs(ai): Spec update [ai-generated]" && git push',
36
+ "提交任务": 'git add workspace/tasks/ && git commit -m "chore: task update [ai-assisted]" && git push',
37
+ };
38
+ const cmd = cmds[op];
39
+ if (!cmd) { console.log("可用: 提交 推送 拉取最新 同步 查看状态 提交PRD 提交Spec 提交任务"); return; }
40
+ try { console.log(execSync(cmd, { cwd, encoding: "utf-8", timeout: 30000, stdio: "pipe" }) || `${op} done`); }
41
+ catch (e) { console.log(e.stdout || e.message); }
42
+ }
43
+
44
+ function doInit(name) {
45
+ const cwd = process.cwd();
46
+ const hasExisting = fs.existsSync(path.join(cwd, ".qoder", "scripts", "setup.py"));
47
+ console.log(`\nwlkj init -> ${cwd}${hasExisting ? " (已存在, 增量更新)" : ""}\n`);
48
+
49
+ // === 1. 拷贝完整引擎 (镜像 .qoder/ 结构) ===
50
+ // 源: templates/qoder/* 目标: .qoder/*
51
+ const qoderSrc = path.join(T, "qoder");
52
+ let copied = 0;
53
+ if (fs.existsSync(qoderSrc)) {
54
+ copied = copyDirRecursive(qoderSrc, path.join(cwd, ".qoder"));
55
+ }
56
+ console.log(` 引擎: ${copied} 个文件 (${hasExisting ? "更新" : "新建"})`);
57
+
58
+ // === 2. 根文件 (AGENTS.md, 新手指南.md) ===
59
+ ["root/AGENTS.md", "root/新手指南.md"].forEach(f => {
60
+ const src = path.join(T, f);
61
+ if (fs.existsSync(src)) {
62
+ const dstName = path.basename(f);
63
+ const dst = path.join(cwd, dstName);
64
+ if (!fs.existsSync(dst)) {
65
+ fs.copyFileSync(src, dst);
66
+ }
67
+ }
68
+ });
69
+
70
+ // === 3. workspace 目录结构 ===
71
+ ["workspace/specs/prd", "workspace/tasks", "workspace/constitution",
72
+ "workspace/members", "data/docs/prd", "data/index", "data/code"].forEach(d => {
73
+ fs.mkdirSync(path.join(cwd, d), { recursive: true });
74
+ });
75
+ console.log(` workspace 目录就绪`);
76
+
77
+ // === 4. git init (若没有) ===
78
+ if (!fs.existsSync(path.join(cwd, ".git"))) {
79
+ try { execSync("git init", { cwd, stdio: "pipe" }); console.log(` git init done`); }
80
+ catch (_) { /* git 可能没装, 不阻塞 */ }
81
+ }
82
+
83
+ // === 5. .gitignore (若没有) ===
84
+ const gitignorePath = path.join(cwd, ".gitignore");
85
+ if (!fs.existsSync(gitignorePath)) {
86
+ const baseGitignore = [
87
+ "# Source code repos (cloned by git_sync.py)", "data/code/",
88
+ "", "# AI pipeline runtime", ".qoder/.developer", ".qoder/.current-task",
89
+ ".qoder/.runtime/", "", "# Personal learning", ".qoder/learning/feedback.jsonl",
90
+ "", "# Machine-local", "data/index/.last-sync", "data/index/.prd-collected.json",
91
+ "data/index/.index-meta.json", "data/index/.sync-lock", "data/index/.file-keys.json",
92
+ "data/index/.inverted-cache.json", "data/index/*.corrupt",
93
+ "", "# Identity (secret)", "workspace/members/*/.signing_key", "",
94
+ "# Python", "__pycache__/", "*.pyc", "*.lock", "",
95
+ ].join("\n");
96
+ fs.writeFileSync(gitignorePath, baseGitignore, "utf-8");
97
+ }
98
+
99
+ // === 6. 自动跑 setup.py (名字探测/git/索引/cron/QoderWork) ===
100
+ console.log(`\n--- 自动初始化 ---`);
101
+ const setupPath = path.join(cwd, ".qoder", "scripts", "setup.py");
102
+ if (fs.existsSync(setupPath)) {
103
+ const setupArgs = [name, "pm", "--skip-cron", "--skip-qoderwork"].filter(Boolean);
104
+ try {
105
+ const cmd = `python "${setupPath}" ${setupArgs.map(a => `"${a}"`).join(" ")}`;
106
+ console.log(` 运行: setup.py ${setupArgs.join(" ")}`);
107
+ execSync(cmd, { cwd, stdio: "inherit", timeout: 300000 });
108
+ } catch (e) {
109
+ console.log(` setup 部分失败 (不阻塞): ${(e.message || "").slice(0, 100)}`);
110
+ console.log(` 可手动重跑: python .qoder/scripts/setup.py`);
111
+ }
112
+ } else {
113
+ console.log(` setup.py 未找到, 跳过自动初始化`);
114
+ }
115
+
116
+ console.log(`\n${"=".repeat(50)}`);
117
+ console.log(` 安装完成!`);
118
+ console.log(`${"=".repeat(50)}`);
119
+ console.log(`\n 现在可以开始了:`);
120
+ console.log(` 在 Qoder 里说 "写个 XX 的需求"`);
121
+ console.log(` 或输 / 看命令列表 (/wl-prd /wl-search /wl-task)`);
122
+ console.log(`\n 看不到命令? 新建对话 / 重启 QoderWork`);
123
+ console.log(` 环境问题? python .qoder/scripts/init_doctor.py --fix\n`);
124
+ }
125
+
126
+ // 递归拷贝目录, 返回文件数
127
+ function copyDirRecursive(src, dst) {
128
+ let count = 0;
129
+ if (!fs.existsSync(src)) return 0;
130
+ fs.mkdirSync(dst, { recursive: true });
131
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
132
+ if (entry.name === "__pycache__" || entry.name.endsWith(".pyc")) continue;
133
+ const s = path.join(src, entry.name);
134
+ const d = path.join(dst, entry.name);
135
+ if (entry.isDirectory()) {
136
+ count += copyDirRecursive(s, d);
137
+ } else {
138
+ // 不覆盖已存在的 (增量更新, 保留用户改动)
139
+ // 但 .py 和 .md / config.yaml / rules 始终更新 (引擎文件)
140
+ const isEngine = entry.name.endsWith(".py") || entry.name.endsWith(".md") ||
141
+ entry.name.endsWith(".yaml") || entry.name.endsWith(".yml") ||
142
+ entry.name.endsWith(".toml") || entry.name.endsWith(".json") ||
143
+ entry.name.endsWith(".html") || entry.name.endsWith(".txt");
144
+ if (isEngine || !fs.existsSync(d)) {
145
+ fs.copyFileSync(s, d);
146
+ }
147
+ count++;
148
+ }
149
+ }
150
+ return count;
151
+ }
152
+
153
+ function doStatus() {
154
+ console.log("");
155
+ const dev = path.join(process.cwd(), ".qoder", ".developer");
156
+ if (fs.existsSync(dev)) {
157
+ const line = fs.readFileSync(dev, "utf-8").split("\n")[0];
158
+ console.log("dev: " + line.split("=")[1]?.trim());
159
+ } else { console.log("dev: - (run: npx wlkj init <name>)"); }
160
+
161
+ process.stdout.write(py("task.py", ["current", "--source"]));
162
+
163
+ const wsSpecs = path.join(process.cwd(), "workspace", "specs", "prd");
164
+ if (fs.existsSync(wsSpecs)) {
165
+ const n = fs.readdirSync(wsSpecs).filter(f => f.endsWith(".md")).length;
166
+ console.log(`PRDs: ${n}`);
167
+ }
168
+ console.log("");
169
+ }
170
+
171
+ function doHelp() {
172
+ console.log("");
173
+ console.log("wlkj - AI 产品研发工作流 (v2.0)");
174
+ console.log("");
175
+ console.log("=== 一键安装 (新项目第一步) ===");
176
+ console.log(" npx wlkj init [你的名字] 安装完整引擎 + 自动初始化");
177
+ console.log(" 例: npx wlkj init 小王");
178
+ console.log(" 例: npx wlkj init (从 git 探测名字)");
179
+ console.log("");
180
+ console.log("=== 安装后怎么用 ===");
181
+ console.log(" 在 Qoder (IDE/Quest/QoderWork) 里:");
182
+ console.log(" 说中文: '写个 XX 的需求' '查一下 XX 代码' '建个任务'");
183
+ console.log(" 或斜杠: /wl-prd /wl-search /wl-task /wl-status /wl-report");
184
+ console.log("");
185
+ console.log("=== 状态 ===");
186
+ console.log(" npx wlkj status 查看当前状态");
187
+ console.log("");
188
+ console.log("=== 环境修复 ===");
189
+ console.log(" python .qoder/scripts/init_doctor.py --fix 自动修复环境");
190
+ console.log(" python .qoder/scripts/setup.py 重新初始化");
191
+ console.log("");
192
+ console.log("=== Git (中文命令) ===");
193
+ console.log(" npx wlkj 提交PRD 提交 PRD");
194
+ console.log(" npx wlkj 提交任务 提交任务");
195
+ console.log(" npx wlkj 提交 全部提交并推送");
196
+ console.log(" npx wlkj 拉取最新 / 同步 git pull");
197
+ console.log("");
198
+ }
199
+
200
+ const [,, cmd, ...rest] = process.argv;
201
+ const mapped = rest.map(a => a === "-p" ? "--priority" : a);
202
+
203
+ switch (cmd) {
204
+ case "init": doInit(rest[0]); break;
205
+ case "task": process.stdout.write(py("task.py", mapped)); break;
206
+ case "status": doStatus(); break;
207
+ case "session": process.stdout.write(py("add_session.py", rest)); break;
208
+ case "help": case "--help": case "-h": doHelp(); break;
209
+ case "提交": case "推送": case "拉取最新": case "同步":
210
+ case "查看状态": case "提交PRD": case "提交Spec": case "提交任务":
211
+ gitOp(cmd); break;
212
+ default: doHelp();
213
+ }
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@hupan56/wlkj",
3
+ "version": "2.0.0",
4
+ "description": "AI 产品研发工作流 - PRD/原型/搜索/任务/报告 一键就绪",
5
+ "bin": { "wlkj": "./bin/cli.js" },
6
+ "files": ["bin/", "templates/"],
7
+ "keywords": ["workflow", "ai", "prd", "pipeline", "qoder", "product", "team"],
8
+ "license": "MIT",
9
+ "publishConfig": { "access": "public" },
10
+ "engines": { "node": ">=16" }
11
+ }
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ // wlkj - workflow toolkit
3
+ // 参考 Trellis 架构:用户看到的极简,背后 scripts/hooks 精确控制
4
+
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+ const { execSync } = require("child_process");
8
+
9
+ const T = path.join(__dirname, "..", "templates");
10
+
11
+ // --- helpers ---
12
+ function py(script, args = []) {
13
+ const p = path.join(process.cwd(), ".qoder", "scripts", script);
14
+ if (!fs.existsSync(p)) {
15
+ console.log("请先运行: npx wlkj init");
16
+ process.exit(1);
17
+ }
18
+ try {
19
+ return execSync(
20
+ `python "${p}" ${args.map(a => `"${a}"`).join(" ")}`,
21
+ { cwd: process.cwd(), encoding: "utf-8", timeout: 15000 }
22
+ );
23
+ } catch (e) {
24
+ return (e.stdout || e.message);
25
+ }
26
+ }
27
+
28
+ function cp(src, dest) {
29
+ const s = path.join(T, src);
30
+ const d = path.join(process.cwd(), dest);
31
+ if (!fs.existsSync(s)) return;
32
+ fs.mkdirSync(path.dirname(d), { recursive: true });
33
+ if (!fs.existsSync(d)) { fs.copyFileSync(s, d); return true; }
34
+ return false;
35
+ }
36
+
37
+ function cpDir(src, dest) {
38
+ const s = path.join(T, src);
39
+ const d = path.join(process.cwd(), dest);
40
+ if (!fs.existsSync(s)) return;
41
+ fs.mkdirSync(d, { recursive: true });
42
+ const items = fs.readdirSync(s);
43
+ items.forEach(item => {
44
+ const srcPath = path.join(s, item);
45
+ const destPath = path.join(d, item);
46
+ if (fs.statSync(srcPath).isDirectory()) {
47
+ cpDir(path.join(src, item), path.join(dest, item));
48
+ } else if (!fs.existsSync(destPath)) {
49
+ fs.copyFileSync(srcPath, destPath);
50
+ }
51
+ });
52
+ }
53
+
54
+ // --- init ---
55
+ function doInit(name) {
56
+ const cwd = process.cwd();
57
+ console.log(``);
58
+ console.log(`wlkj init -> ${cwd}`);
59
+
60
+ // 目录结构
61
+ const dirs = [
62
+ ".qoder/rules", ".qoder/context", ".qoder/scripts/common",
63
+ ".qoder/scripts/hooks", ".qoder/skills/prd-generator",
64
+ ".qoder/skills/spec-generator", ".qoder/skills/spec-coder",
65
+ ".qoder/skills/test-generator", ".qoder/agents", ".qoder/hooks",
66
+ ".qoder/tasks", ".qoder/archive", ".qoder/workspace",
67
+ ".qoder/.runtime/sessions",
68
+ ".codex/hooks", ".codex/agents",
69
+ "docs/ai/prd", "docs/ai/specs", "src", "tests"
70
+ ];
71
+ dirs.forEach(d => fs.mkdirSync(path.join(cwd, d), { recursive: true }));
72
+
73
+ // 模板文件(flat list,一目了然)
74
+ let copied = 0;
75
+ const files = [
76
+ // .qoder 核心
77
+ ["qoder/config.yaml", ".qoder/config.yaml"],
78
+ ["qoder/workflow.md", ".qoder/workflow.md"],
79
+ // rules
80
+ ["rules/code-style.md", ".qoder/rules/code-style.md"],
81
+ ["rules/prd-template.md", ".qoder/rules/prd-template.md"],
82
+ ["rules/spec-template.md", ".qoder/rules/spec-template.md"],
83
+ ["rules/testing.md", ".qoder/rules/testing.md"],
84
+ // context
85
+ ["context/architecture.md", ".qoder/context/architecture.md"],
86
+ ["context/data-dictionary.md",".qoder/context/data-dictionary.md"],
87
+ // skills
88
+ ["skills/prd-generator/SKILL.md", ".qoder/skills/prd-generator/SKILL.md"],
89
+ ["skills/spec-generator/SKILL.md", ".qoder/skills/spec-generator/SKILL.md"],
90
+ ["skills/spec-coder/SKILL.md", ".qoder/skills/spec-coder/SKILL.md"],
91
+ ["skills/test-generator/SKILL.md", ".qoder/skills/test-generator/SKILL.md"],
92
+ // agents
93
+ ["agents/qoder-spec-gen.toml", ".qoder/agents/qoder-spec-gen.toml"],
94
+ ["agents/qoder-coder.toml", ".qoder/agents/qoder-coder.toml"],
95
+ ["agents/qoder-test-gen.toml", ".qoder/agents/qoder-test-gen.toml"],
96
+ // hooks
97
+ ["hooks/post-prd-push.py", ".qoder/hooks/post-prd-push.py"],
98
+ // scripts
99
+ ["scripts/init_developer.py", ".qoder/scripts/init_developer.py"],
100
+ ["scripts/task.py", ".qoder/scripts/task.py"],
101
+ ["scripts/add_session.py", ".qoder/scripts/add_session.py"],
102
+ ["scripts/common/__init__.py", ".qoder/scripts/common/__init__.py"],
103
+ ["scripts/common/paths.py", ".qoder/scripts/common/paths.py"],
104
+ ["scripts/common/developer.py", ".qoder/scripts/common/developer.py"],
105
+ ["scripts/common/active_task.py",".qoder/scripts/common/active_task.py"],
106
+ ["scripts/common/task_utils.py", ".qoder/scripts/common/task_utils.py"],
107
+ // codex hooks
108
+ ["codex/config.toml", ".codex/config.toml"],
109
+ ["codex/hooks.json", ".codex/hooks.json"],
110
+ ["codex/hooks/inject-pipeline-state.py", ".codex/hooks/inject-pipeline-state.py"],
111
+ ["codex/agents/qoder-spec-gen.toml", ".codex/agents/qoder-spec-gen.toml"],
112
+ ["codex/agents/qoder-coder.toml", ".codex/agents/qoder-coder.toml"],
113
+ ["codex/agents/qoder-test-gen.toml", ".codex/agents/qoder-test-gen.toml"],
114
+ // root
115
+ ["root/AGENTS.md", "AGENTS.md"],
116
+ ];
117
+
118
+ files.forEach(([s, d]) => { if (cp(s, d)) copied++; });
119
+ console.log(` copied ${copied} files`);
120
+
121
+ // git init
122
+ if (!fs.existsSync(path.join(cwd, ".git"))) {
123
+ try {
124
+ execSync("git init", { cwd, stdio: "pipe" });
125
+ console.log(" git init done");
126
+ } catch (_) {}
127
+ }
128
+
129
+ // developer
130
+ if (name) {
131
+ console.log("");
132
+ process.stdout.write(py("init_developer.py", [name]));
133
+ } else {
134
+ console.log("");
135
+ console.log(" next: npx wlkj init <your-name>");
136
+ }
137
+ console.log("");
138
+ }
139
+
140
+ // --- status ---
141
+ function doStatus() {
142
+ console.log("");
143
+ // developer
144
+ const dev = path.join(process.cwd(), ".qoder", ".developer");
145
+ if (fs.existsSync(dev)) {
146
+ const line = fs.readFileSync(dev, "utf-8").split("\n")[0];
147
+ console.log("dev: " + line.split("=")[1]?.trim());
148
+ } else {
149
+ console.log("dev: -");
150
+ }
151
+ // task
152
+ process.stdout.write(py("task.py", ["current", "--source"]));
153
+ // docs
154
+ const count = (d) => {
155
+ const p = path.join(process.cwd(), d);
156
+ return fs.existsSync(p) ? fs.readdirSync(p).filter(f => f.endsWith(".md")).length : 0;
157
+ };
158
+ console.log(`prd: ${count("docs/ai/prd")} spec: ${count("docs/ai/specs")}`);
159
+ console.log("");
160
+ }
161
+
162
+ // --- main ---
163
+ const [,, cmd, ...rest] = process.argv;
164
+
165
+ switch (cmd) {
166
+ case "init":
167
+ doInit(rest[0]);
168
+ break;
169
+ case "task":
170
+ // 映射 -p 到 --priority(CLI 便捷)
171
+ const mapped = rest.map(a => a === "-p" ? "--priority" : a);
172
+ process.stdout.write(py("task.py", mapped));
173
+ break;
174
+ case "status":
175
+ doStatus();
176
+ break;
177
+ case "session":
178
+ process.stdout.write(py("add_session.py", rest));
179
+ break;
180
+ default:
181
+ console.log("");
182
+ console.log("wlkj - workflow toolkit");
183
+ console.log("");
184
+ console.log(" npx wlkj init [name] init workflow + developer");
185
+ console.log(" npx wlkj status show status");
186
+ console.log(" npx wlkj task <cmd> task management");
187
+ console.log(" npx wlkj session <args> record session log");
188
+ console.log("");
189
+ console.log("task commands:");
190
+ console.log(' create "Title" [-p P0|P1|P2|P3] [--assignee who]');
191
+ console.log(" list [--mine] [--status planning|in_progress]");
192
+ console.log(" start <name>");
193
+ console.log(" finish");
194
+ console.log(" current");
195
+ console.log(" archive <name>");
196
+ console.log(" add-subtask <parent> <child>");
197
+ console.log("");
198
+ }
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: wl-code
3
+ description: "Implement code following a Spec strictly. Needs confirmation."
4
+ argument-hint: "[REQ-ID or spec-filename]"
5
+ auto-approve: false
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
7
+ ---
8
+
9
+ # /wl-code - Implement Code
10
+
11
+ User input: $ARGUMENTS
12
+
13
+ ## Step 1: Load Spec
14
+
15
+ If REQ-ID provided, search workspace/specs/ for matching spec.
16
+ If no argument, check .qoder/.current-task for active task.
17
+ Read the spec file to understand requirements.
18
+
19
+ ## Step 2: Search Related Code
20
+
21
+ Run: python .qoder/scripts/search_index.py <keyword>
22
+ Find existing code that this implementation relates to.
23
+ Read relevant files for context.
24
+
25
+ ## Step 3: Implement
26
+
27
+ Follow the spec strictly:
28
+ 1. Read spec-coder skill for coding workflow
29
+ 2. Implement each requirement from spec
30
+ 3. Follow team conventions from data/code/ patterns
31
+ 4. Write clean, commented code
32
+
33
+ ## Step 4: Self-Check
34
+
35
+ After implementation:
36
+ - [ ] All spec requirements covered
37
+ - [ ] No TODO/FIXME left
38
+ - [ ] Code follows project conventions
39
+ - [ ] Error handling in place
40
+
41
+ ## Step 5: Report
42
+
43
+ Tell user what was implemented and suggest /wl-test next.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: wl-commit
3
+ description: "Git commit, push, and sync. Includes pre-commit quality check."
4
+ argument-hint: "[commit message]"
5
+ auto-approve: false
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
7
+ ---
8
+
9
+ # /wl-commit - Git Commit + Push + Sync
10
+
11
+ User input: $ARGUMENTS (commit message)
12
+
13
+ ## Flow
14
+ 1. Check git status for uncommitted changes
15
+ 2. Auto-stage changed files
16
+ 3. Generate commit message if not provided:
17
+ - Format: [ai-generated] {type}: {description}
18
+ - Types: feat/fix/docs/refactor/test
19
+ 4. Ask user to confirm commit message
20
+ 5. Commit with message
21
+ 6. Pull latest from remote (sync)
22
+ 7. Push to remote
23
+ 8. Record to learning system
24
+
25
+ ## Pre-commit Quality Gate
26
+ Before committing, check:
27
+ - [ ] All tests pass (if /wl-test was run)
28
+ - [ ] No TODO/FIXME left in code
29
+ - [ ] Commit tagged [ai-generated] or [ai-assisted]
30
+ - [ ] Amount fields use BigDecimal (if applicable)
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: wl-init
3
+ description: "Initialize or switch developer + full environment health check. Idempotent."
4
+ argument-hint: "[name] [role]"
5
+ auto-approve: true
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
7
+ ---
8
+
9
+ # /wl-init - One-stop Init (Developer + Environment Doctor)
10
+
11
+ Usage:
12
+ /wl-init - 体检环境 + 显示状态
13
+ /wl-init 小王 - 切换/注册开发者 + 体检 + 自动修复
14
+ /wl-init 小王 pm - 注册新开发者(带角色) + 体检 + 自动修复
15
+
16
+ ## Engine: init_doctor.py (幂等, 增量, 永不重头再来)
17
+
18
+ ### Case 1: /wl-init (no arguments)
19
+ ```bash
20
+ python .qoder/scripts/init_doctor.py
21
+ ```
22
+ Report the doctor's findings to the user in friendly language.
23
+ If it found fixable issues, ask: "发现 N 个问题,要我自动修复吗?"
24
+ then run with --fix.
25
+
26
+ ### Case 2/3: /wl-init 小王 [pm]
27
+ ```bash
28
+ python .qoder/scripts/init_doctor.py --fix 小王 [pm]
29
+ ```
30
+ This single command does ALL of:
31
+ 1. 注册/切换开发者 (.qoder/.developer, key=value 格式) + 个人空间目录
32
+ 2. 拉取团队最新 (team_sync pull - 拿到别人的 PRD 和最新知识图谱)
33
+ 3. 克隆缺失的源码仓库 (按 config.yaml git_sync.projects)
34
+ 4. 知识图谱: 7天内新鲜->跳过 / 过期->增量同步 / 缺失->全量构建(仅首次)
35
+ 5. 校验风格约束文件 + PRD 模板
36
+ 6. 检查周五自动构建状态, 给出本系统的设置命令
37
+
38
+ After doctor finishes:
39
+ - Create member.json with role if not exists (workspace/members/{name}/member.json)
40
+ - Run team.py add if it's a new member
41
+
42
+ ## Incremental Guarantee (为什么不会重头再来)
43
+
44
+ | 状态 | 行为 |
45
+ |------|------|
46
+ | 本周已 init 过 / 周五任务跑过 | 索引新鲜(≤7天) -> 全部跳过, 秒级完成 |
47
+ | 周五任务没跑/失效 | 索引过期 -> git diff 增量更新, 只处理变更文件 |
48
+ | 全新机器 | 克隆仓库 + 全量构建一次, 之后永远增量 |
49
+ | 团队其他人已更新图谱 | team_sync pull 直接拿到, 本机不用构建 |
50
+
51
+ ## .developer File Format (MANDATORY)
52
+
53
+ ALWAYS write `.qoder/.developer` in key=value format:
54
+ ```
55
+ name=小王
56
+ role=pm
57
+ initialized_at=2026-06-12T10:00:00
58
+ ```
59
+ Do NOT use `name: value` style. (Parsers accept both for legacy files,
60
+ but new writes must use `=`.)
61
+
62
+ ## Git Author Check
63
+
64
+ The doctor reports if `git config user.name` differs from the developer
65
+ name (it breaks /wl-report's commit statistics). If the user agrees,
66
+ run: `git config user.name {name}`. Do not change it without confirmation.
67
+
68
+ ## Status Display
69
+ After any operation, show:
70
+ ```
71
+ Developer: 小王 (pm)
72
+ Team: 3 members (1 PM, 2 Dev)
73
+ Active tasks: 2 (1 in progress, 1 draft)
74
+ 知识图谱: 2 天前更新 (新鲜)
75
+ ```
76
+
77
+ ## Safety Rules
78
+ - NEVER delete workspace/ content
79
+ - NEVER overwrite member.json or task.json
80
+ - Full index build ONLY when index is completely missing (doctor handles this)
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: wl-insight
3
+ description: "Data insight: user feedback analysis + product metrics review in one command"
4
+ argument-hint: "[type: feedback/metrics/all] [data or description]"
5
+ auto-approve: true
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit]
7
+ ---
8
+
9
+ # /wl-insight - Data Insight (Feedback + Metrics)
10
+
11
+ User input: $ARGUMENTS
12
+
13
+ ## Type Detection
14
+ - "feedback" or paste feedback text -> Feedback analysis
15
+ - "metrics" or paste data table -> Metrics review
16
+ - "all" or no keyword -> Both (if data available)
17
+ - Default: ask user what they want to analyze
18
+
19
+ ## Feedback Analysis
20
+ 1. Parse feedback data (text/CSV/paste)
21
+ 2. Classify: feature-request / bug / UX / performance / praise / question
22
+ 3. Sentiment: positive / neutral / negative
23
+ 4. Extract pain points (frequency x severity)
24
+ 5. Output:
25
+ - Category breakdown
26
+ - Top 10 pain points with representative quotes
27
+ - Prioritized improvement recommendations
28
+ 6. Save to: workspace/members/{dev}/drafts/wl-insight-feedback-{date}.md
29
+
30
+ ## Metrics Review
31
+ 1. Parse metrics data
32
+ 2. North Star decomposition (North Star -> L1 -> L2)
33
+ 3. Growth: DAU/WAU/MAU + user segments
34
+ 4. Retention: D1/D7/D30 vs industry benchmarks
35
+ 5. Conversion funnel: bottleneck identification
36
+ 6. A/B experiment interpretation (if data provided)
37
+ 7. OKR alignment check
38
+ 8. Anomaly attribution (if metric changed)
39
+ 9. Output:
40
+ - Health overview table
41
+ - Key insights (data-backed)
42
+ - Action recommendations
43
+ 10. Save to: workspace/members/{dev}/drafts/wl-insight-metrics-{date}.md
44
+
45
+ ## Combined (all)
46
+ Run both analyses. Cross-reference:
47
+ - Metrics anomaly + user feedback = root cause
48
+ - Feedback trends + metric changes = validation
49
+
50
+ ## Output
51
+ Always end with: "Use /wl-prd to convert insights into action"