@hupan56/wlkj 3.0.0 → 3.1.2
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 +1435 -1373
- package/package.json +1 -1
- package/templates/qoder/commands/optional/wl-insight.md +6 -4
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +5 -5
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +3 -3
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +10 -10
- package/templates/qoder/config.yaml +35 -13
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +82 -1
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +70 -8
- package/templates/qoder/scripts/README.md +78 -115
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
- package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
- package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
- package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
- package/templates/qoder/scripts/domain/__init__.py +0 -0
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
- package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
- package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
- package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
- package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
- package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
- package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
- package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
- package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
- package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
- package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
- package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
- package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
- package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
- package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
- package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
- package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
- package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
- package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
- package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
- package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
- package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
- package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
- package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
- package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
- package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
- package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
- package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
- package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +30 -0
- package/templates/qoder/skills/design-import/SKILL.md +22 -13
- package/templates/qoder/skills/design-review/SKILL.md +19 -10
- package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
- package/templates/qoder/skills/prd-review/SKILL.md +6 -4
- package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
- package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
- package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
- package/templates/qoder/skills/test-generator/SKILL.md +7 -5
- package/templates/qoder/skills/wl-code/SKILL.md +23 -11
- package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
- package/templates/qoder/skills/wl-design/SKILL.md +5 -3
- package/templates/qoder/skills/wl-init/SKILL.md +6 -6
- package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
- package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
- package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
- package/templates/qoder/skills/wl-report/SKILL.md +25 -23
- package/templates/qoder/skills/wl-search/SKILL.md +149 -78
- package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
- package/templates/qoder/skills/wl-status/SKILL.md +48 -16
- package/templates/qoder/skills/wl-task/SKILL.md +92 -10
- package/templates/qoder/skills/wl-test/SKILL.md +75 -29
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -236
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
- package/templates/.qoder/learning/eval-history.jsonl +0 -28
- package/templates/data/index/wiki-index.json +0 -8
- package/templates/qoder/nul +0 -4
- package/templates/qoder/scripts/kg/extract.py +0 -398
- package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
- package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
- package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
- package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
- package/templates/qoder/scripts/task/__init__.py +0 -11
- package/templates/qoder/scripts/test/__init__.py +0 -11
- package/templates/qoder/scripts/wlkj.py +0 -116
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{test → validation/test}/secure-ls.js +0 -0
|
@@ -1,222 +1,228 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""req.py - 需求生命周期管理 (/wl-req 命令)
|
|
4
|
-
|
|
5
|
-
M3 workspace 重构的核心。解决"找不到产出"痛点:
|
|
6
|
-
一个需求的所有产出(PRD/原型/任务/规格)聚合在 REQ-ID 容器,
|
|
7
|
-
/wl-req 一条命令看到全貌, 不用跨目录翻。
|
|
8
|
-
|
|
9
|
-
设计 (PM 只需记 2 个姿势):
|
|
10
|
-
/wl-req 列出所有需求 (默认=list, 不用记)
|
|
11
|
-
/wl-req REQ-2026-001 显示该需求全部产出 (show)
|
|
12
|
-
/wl-req REQ-2026-001 发布 promote+publish一步到位 (草稿→产出→禅道)
|
|
13
|
-
|
|
14
|
-
promote 不单独暴露(合并进"发布"), list 不用记(无参数即是)。
|
|
15
|
-
|
|
16
|
-
用法:
|
|
17
|
-
python req.py # 列需求
|
|
18
|
-
python req.py REQ-2026-001 # 看某需求全貌
|
|
19
|
-
python req.py REQ-2026-001 发布 # 发布(promote+publish)
|
|
20
|
-
python req.py REQ-2026-001 promote # 仅草稿→产出(内部用)
|
|
21
|
-
"""
|
|
22
|
-
# v3.0 路径自举
|
|
23
|
-
import os as _o, sys as _s
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
from
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
print()
|
|
98
|
-
print("
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
dst
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if not out_found:
|
|
173
|
-
print("
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
print(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""req.py - 需求生命周期管理 (/wl-req 命令)
|
|
4
|
+
|
|
5
|
+
M3 workspace 重构的核心。解决"找不到产出"痛点:
|
|
6
|
+
一个需求的所有产出(PRD/原型/任务/规格)聚合在 REQ-ID 容器,
|
|
7
|
+
/wl-req 一条命令看到全貌, 不用跨目录翻。
|
|
8
|
+
|
|
9
|
+
设计 (PM 只需记 2 个姿势):
|
|
10
|
+
/wl-req 列出所有需求 (默认=list, 不用记)
|
|
11
|
+
/wl-req REQ-2026-001 显示该需求全部产出 (show)
|
|
12
|
+
/wl-req REQ-2026-001 发布 promote+publish一步到位 (草稿→产出→禅道)
|
|
13
|
+
|
|
14
|
+
promote 不单独暴露(合并进"发布"), list 不用记(无参数即是)。
|
|
15
|
+
|
|
16
|
+
用法:
|
|
17
|
+
python req.py # 列需求
|
|
18
|
+
python req.py REQ-2026-001 # 看某需求全貌
|
|
19
|
+
python req.py REQ-2026-001 发布 # 发布(promote+publish)
|
|
20
|
+
python req.py REQ-2026-001 promote # 仅草稿→产出(内部用)
|
|
21
|
+
"""
|
|
22
|
+
# v3.0 路径自举
|
|
23
|
+
import os as _o, sys as _s
|
|
24
|
+
_f = _o.path.abspath(__file__)
|
|
25
|
+
for _ in range(10):
|
|
26
|
+
_f = _o.path.dirname(_f)
|
|
27
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
28
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
29
|
+
break
|
|
30
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
31
|
+
from bootstrap import setup; setup()
|
|
32
|
+
from foundation.core.paths import get_repo_root
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
import sys
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from foundation.core.paths import (
|
|
38
|
+
get_repo_root, get_developer, MEMBERS_DIR,
|
|
39
|
+
get_time_bucket, get_time_bucket_path, get_req_dir,
|
|
40
|
+
get_outputs_dir, find_req_dirs,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
BASE = get_repo_root()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _list_all_reqs(dev=None):
|
|
47
|
+
"""列出某开发者的所有需求 (跨 drafts/outputs/所有时间桶)。
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
[{req_id, area, bucket, path, files: [文件名]}, ...]
|
|
51
|
+
"""
|
|
52
|
+
dev = dev or get_developer(BASE) or "unknown"
|
|
53
|
+
dev_dir = MEMBERS_DIR / dev
|
|
54
|
+
if not dev_dir.is_dir():
|
|
55
|
+
return []
|
|
56
|
+
seen = {} # req_id → entry (去重, drafts优先)
|
|
57
|
+
for area in ("drafts", "outputs"):
|
|
58
|
+
area_root = dev_dir / area
|
|
59
|
+
if not area_root.is_dir():
|
|
60
|
+
continue
|
|
61
|
+
# 扫所有 REQ-* 目录 (跨时间桶)
|
|
62
|
+
for found in area_root.rglob("REQ-*"):
|
|
63
|
+
if not found.is_dir():
|
|
64
|
+
continue
|
|
65
|
+
req_id = found.name
|
|
66
|
+
# 清理标题后缀只取 REQ-YYYY-NNN (但保留完整名做显示)
|
|
67
|
+
import re
|
|
68
|
+
m = re.match(r"(REQ-\d{4}-\d{3,})", req_id)
|
|
69
|
+
clean_id = m.group(1) if m else req_id
|
|
70
|
+
if clean_id in seen:
|
|
71
|
+
# 已有, 但如果当前是outputs且已有的是drafts, 补充信息
|
|
72
|
+
continue
|
|
73
|
+
files = sorted(f.name for f in found.iterdir() if f.is_file())
|
|
74
|
+
subdirs = sorted(d.name for d in found.iterdir() if d.is_dir())
|
|
75
|
+
rel_bucket = str(found.parent.relative_to(area_root))
|
|
76
|
+
seen[clean_id] = {
|
|
77
|
+
"req_id": req_id,
|
|
78
|
+
"clean_id": clean_id,
|
|
79
|
+
"area": area,
|
|
80
|
+
"bucket": rel_bucket,
|
|
81
|
+
"path": found,
|
|
82
|
+
"files": files,
|
|
83
|
+
"subdirs": subdirs,
|
|
84
|
+
}
|
|
85
|
+
return list(seen.values())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def cmd_list(dev=None):
|
|
89
|
+
"""列出所有需求。"""
|
|
90
|
+
reqs = _list_all_reqs(dev)
|
|
91
|
+
if not reqs:
|
|
92
|
+
dev_name = dev or get_developer(BASE) or "unknown"
|
|
93
|
+
print("暂无需求。写 PRD 时会自动创建 REQ-ID 容器。")
|
|
94
|
+
print(" 开发者: %s" % dev_name)
|
|
95
|
+
return 0
|
|
96
|
+
print("=" * 56)
|
|
97
|
+
print("需求列表 (%d 个)" % len(reqs))
|
|
98
|
+
print("=" * 56)
|
|
99
|
+
for r in sorted(reqs, key=lambda x: x["clean_id"]):
|
|
100
|
+
area_tag = "草稿" if r["area"] == "drafts" else "产出"
|
|
101
|
+
n_files = len(r["files"]) + len(r["subdirs"])
|
|
102
|
+
print(" %s [%s] %s (%d 项)" % (r["clean_id"], area_tag, r["bucket"], n_files))
|
|
103
|
+
print()
|
|
104
|
+
print("看详情: /wl-req <REQ-ID>")
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def cmd_show(req_id, dev=None):
|
|
109
|
+
"""显示某需求的全部产出 (跨 drafts/outputs/历史桶)。"""
|
|
110
|
+
found = find_req_dirs(req_id, dev, BASE)
|
|
111
|
+
if not found:
|
|
112
|
+
print("未找到需求: %s" % req_id)
|
|
113
|
+
print(" 用 /wl-req 看所有需求列表")
|
|
114
|
+
return 1
|
|
115
|
+
print("=" * 56)
|
|
116
|
+
print("需求: %s" % req_id)
|
|
117
|
+
print("=" * 56)
|
|
118
|
+
for entry in found:
|
|
119
|
+
area_cn = "草稿" if entry["area"] == "drafts" else "产出"
|
|
120
|
+
p = entry["path"]
|
|
121
|
+
print()
|
|
122
|
+
print("【%s · %s】" % (area_cn, entry["bucket"]))
|
|
123
|
+
print(" 路径: %s" % p)
|
|
124
|
+
# 列文件
|
|
125
|
+
files = sorted(f.name for f in p.iterdir() if f.is_file())
|
|
126
|
+
subdirs = sorted(d.name for d in p.iterdir() if d.is_dir())
|
|
127
|
+
if files:
|
|
128
|
+
print(" 文件:")
|
|
129
|
+
for f in files:
|
|
130
|
+
print(" - %s" % f)
|
|
131
|
+
if subdirs:
|
|
132
|
+
print(" 子目录:")
|
|
133
|
+
for d in subdirs:
|
|
134
|
+
# 列子目录里的文件数
|
|
135
|
+
sub_path = p / d
|
|
136
|
+
n = sum(1 for _ in sub_path.rglob("*") if _.is_file())
|
|
137
|
+
print(" - %s/ (%d 个文件)" % (d, n))
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def cmd_promote(req_id, dev=None):
|
|
142
|
+
"""草稿 → 产出 (drafts/桶/REQ → outputs/同桶/REQ)。"""
|
|
143
|
+
dev = dev or get_developer(BASE) or "unknown"
|
|
144
|
+
# 找 drafts 里的
|
|
145
|
+
draft_found = [e for e in find_req_dirs(req_id, dev, BASE) if e["area"] == "drafts"]
|
|
146
|
+
if not draft_found:
|
|
147
|
+
print("草稿区未找到: %s (可能已在产出区, 或不存在)" % req_id)
|
|
148
|
+
return 1
|
|
149
|
+
src = draft_found[0]["path"]
|
|
150
|
+
bucket = draft_found[0]["bucket"]
|
|
151
|
+
# 目标: outputs/同桶/REQ
|
|
152
|
+
dev_dir = MEMBERS_DIR / dev
|
|
153
|
+
dst = dev_dir / "outputs" / bucket / src.name
|
|
154
|
+
if dst.exists():
|
|
155
|
+
print("产出区已存在: %s" % dst)
|
|
156
|
+
print(" 如要覆盖, 先手动删除产出区的旧版本")
|
|
157
|
+
return 1
|
|
158
|
+
import shutil
|
|
159
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
shutil.move(str(src), str(dst))
|
|
161
|
+
print("✓ 已提升到产出区:")
|
|
162
|
+
print(" %s" % dst)
|
|
163
|
+
print(" (产出进 team_sync, 团队可见)")
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def cmd_publish(req_id, dev=None):
|
|
168
|
+
"""发布 = promote + 发禅道。PM 只用这个。"""
|
|
169
|
+
dev = dev or get_developer(BASE) or "unknown"
|
|
170
|
+
# 先确保在 outputs (不在则 promote)
|
|
171
|
+
out_found = [e for e in find_req_dirs(req_id, dev, BASE) if e["area"] == "outputs"]
|
|
172
|
+
if not out_found:
|
|
173
|
+
print("草稿 → 产出...")
|
|
174
|
+
rc = cmd_promote(req_id, dev)
|
|
175
|
+
if rc != 0:
|
|
176
|
+
return rc
|
|
177
|
+
out_found = [e for e in find_req_dirs(req_id, dev, BASE) if e["area"] == "outputs"]
|
|
178
|
+
if not out_found:
|
|
179
|
+
print("✗ 发布失败: 产出区找不到 %s" % req_id)
|
|
180
|
+
return 1
|
|
181
|
+
out_path = out_found[0]["path"]
|
|
182
|
+
print()
|
|
183
|
+
print("发禅道 (从产出区: %s)" % out_path)
|
|
184
|
+
print("-" * 40)
|
|
185
|
+
# 找禅道 MCP 创建需求
|
|
186
|
+
try:
|
|
187
|
+
sys.path.insert(0, str(BASE / ".qoder" / "scripts" / "mcp"))
|
|
188
|
+
# 禅道发布: 读 PRD, 调 zentao MCP 建 story
|
|
189
|
+
prd_file = None
|
|
190
|
+
for f in out_path.iterdir():
|
|
191
|
+
if f.is_file() and (f.name.endswith(".md") and "prd" in f.name.lower()):
|
|
192
|
+
prd_file = f
|
|
193
|
+
break
|
|
194
|
+
if f.is_file() and f.suffix == ".md" and not f.name.startswith("_"):
|
|
195
|
+
prd_file = f # 第一个 md 当 PRD
|
|
196
|
+
if prd_file:
|
|
197
|
+
print(" PRD: %s" % prd_file.name)
|
|
198
|
+
print()
|
|
199
|
+
print("⚠️ 禅道发布需要 QoderWork 的禅道 MCP (mcp__qoder-zentao__create_story)。")
|
|
200
|
+
print(" AI 会读产出的 PRD, 调禅道建需求。")
|
|
201
|
+
print(" 如果你在 QoderWork 里说'%s 发布', AI 会自动完成这步。" % req_id)
|
|
202
|
+
return 0
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print("✗ 发布出错: %s" % e)
|
|
205
|
+
return 1
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def main():
|
|
209
|
+
args = sys.argv[1:]
|
|
210
|
+
if not args:
|
|
211
|
+
return cmd_list()
|
|
212
|
+
req_id = args[0]
|
|
213
|
+
action = args[1] if len(args) > 1 else "show"
|
|
214
|
+
if action == "show":
|
|
215
|
+
return cmd_show(req_id)
|
|
216
|
+
elif action in ("发布", "publish", "fabu"):
|
|
217
|
+
return cmd_publish(req_id)
|
|
218
|
+
elif action == "promote":
|
|
219
|
+
return cmd_promote(req_id)
|
|
220
|
+
elif action == "list":
|
|
221
|
+
return cmd_list()
|
|
222
|
+
else:
|
|
223
|
+
# 默认当 show
|
|
224
|
+
return cmd_show(req_id)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
sys.exit(main())
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# 子包初始化: 注入 scripts/根 + 所有子包到 sys.path
|
|
2
|
-
# 让跨包 import (旧式不带前缀) 仍可用, 兼容现有代码
|
|
3
|
-
import os as _os, sys as _sys
|
|
4
|
-
_root = _os.path.dirname(_os.path.dirname(_os.path.abspath(__file__)))
|
|
5
|
-
if _root not in _sys.path:
|
|
6
|
-
_sys.path.insert(0, _root)
|
|
7
|
-
# 注入所有子包目录 (兼容旧式 import xxx)
|
|
8
|
-
for _sub in ('kg', 'build', 'test', 'setup', 'io'):
|
|
9
|
-
_subdir = _os.path.join(_root, _sub)
|
|
10
|
-
if _os.path.isdir(_subdir) and _subdir not in _sys.path:
|
|
11
|
-
_sys.path.insert(0, _subdir)
|
|
1
|
+
# 子包初始化: 注入 scripts/根 + 所有子包到 sys.path
|
|
2
|
+
# 让跨包 import (旧式不带前缀) 仍可用, 兼容现有代码
|
|
3
|
+
import os as _os, sys as _sys
|
|
4
|
+
_root = _os.path.dirname(_os.path.dirname(_os.path.abspath(__file__)))
|
|
5
|
+
if _root not in _sys.path:
|
|
6
|
+
_sys.path.insert(0, _root)
|
|
7
|
+
# 注入所有子包目录 (兼容旧式 import xxx)
|
|
8
|
+
for _sub in ('kg', 'build', 'test', 'setup', 'io'):
|
|
9
|
+
_subdir = _os.path.join(_root, _sub)
|
|
10
|
+
if _os.path.isdir(_subdir) and _subdir not in _sys.path:
|
|
11
|
+
_sys.path.insert(0, _subdir)
|
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
4
|
import os as _o, sys as _s
|
|
5
|
-
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
6
11
|
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
12
|
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
8
14
|
|
|
9
15
|
"""
|
|
10
16
|
Weekly Knowledge Graph Update
|
|
11
17
|
|
|
12
18
|
Usage:
|
|
13
|
-
python git_sync.py # Full update: git + PRD + index
|
|
14
|
-
python git_sync.py --sync-only # Git pull only
|
|
15
|
-
python git_sync.py --index-only # Re-index only (no git pull)
|
|
16
|
-
python git_sync.py --prd-only # Collect + parse PRDs only
|
|
17
|
-
python git_sync.py --project fywl-ics # Sync single project
|
|
19
|
+
python .qoder/scripts/domain/task/git_sync.py # Full update: git + PRD + index
|
|
20
|
+
python .qoder/scripts/domain/task/git_sync.py --sync-only # Git pull only
|
|
21
|
+
python .qoder/scripts/domain/task/git_sync.py --index-only # Re-index only (no git pull)
|
|
22
|
+
python .qoder/scripts/domain/task/git_sync.py --prd-only # Collect + parse PRDs only
|
|
23
|
+
python .qoder/scripts/domain/task/git_sync.py --project fywl-ics # Sync single project
|
|
18
24
|
|
|
19
25
|
What it does (Friday Update):
|
|
20
26
|
1. Git pull latest code for each project
|
|
@@ -27,7 +33,7 @@ Config: .qoder/config.yaml -> git_sync section
|
|
|
27
33
|
"""
|
|
28
34
|
|
|
29
35
|
import os
|
|
30
|
-
from
|
|
36
|
+
from foundation.core.paths import BUILD_CACHE_DIR
|
|
31
37
|
import sys
|
|
32
38
|
import json
|
|
33
39
|
import subprocess
|
|
@@ -38,7 +44,7 @@ from datetime import datetime
|
|
|
38
44
|
if sys.platform == 'win32':
|
|
39
45
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
40
46
|
|
|
41
|
-
BASE =
|
|
47
|
+
BASE = get_repo_root()
|
|
42
48
|
DATA_DIR = os.path.join(BASE, 'data')
|
|
43
49
|
CODE_DIR = os.path.join(DATA_DIR, 'code')
|
|
44
50
|
INDEX_DIR = os.path.join(DATA_DIR, 'index')
|
|
@@ -71,7 +77,7 @@ def save_json(path, data):
|
|
|
71
77
|
def load_config():
|
|
72
78
|
"""Load config from config.yaml (v3.0: 转发到 Config 单例, 带缓存)。"""
|
|
73
79
|
try:
|
|
74
|
-
from
|
|
80
|
+
from foundation.core.config import Config
|
|
75
81
|
return Config.load().raw
|
|
76
82
|
except Exception:
|
|
77
83
|
# 回退: 直接读 (sys.path 未就绪时)
|
|
@@ -203,18 +209,31 @@ def collect_prds():
|
|
|
203
209
|
if not os.path.isdir(user_dir):
|
|
204
210
|
continue
|
|
205
211
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
212
|
+
# 递归扫 drafts + outputs 两个区 (PRD 可能落在时间桶
|
|
213
|
+
# drafts/{YYYY}/{MM}/{Ww}/{REQ-ID}/REQ-*.md, 也可能已 publish 到 outputs/)。
|
|
214
|
+
# outputs 是发布版, 同名优先 outputs; 同区递归找 (os.walk) 兼容旧扁平结构。
|
|
215
|
+
user_prds = {}
|
|
216
|
+
for area in ('drafts', 'outputs'):
|
|
217
|
+
area_dir = os.path.join(user_dir, area)
|
|
218
|
+
if not os.path.isdir(area_dir):
|
|
219
|
+
continue
|
|
220
|
+
for root, dirs, files in os.walk(area_dir):
|
|
221
|
+
# 跳过隐藏目录 / node_modules (保险)
|
|
222
|
+
dirs[:] = [d for d in dirs if not d.startswith('.') and d != 'node_modules']
|
|
223
|
+
for f in files:
|
|
224
|
+
if (f.startswith('REQ-') or f.startswith('PRD-') or f.startswith('prd-')) and f.endswith('.md'):
|
|
225
|
+
filepath = os.path.join(root, f)
|
|
226
|
+
try:
|
|
227
|
+
mtime = os.path.getmtime(filepath)
|
|
228
|
+
except OSError:
|
|
229
|
+
continue
|
|
230
|
+
entry = {'file': f, 'path': filepath, 'user': user_name, 'mtime': mtime, 'area': area}
|
|
231
|
+
existing = user_prds.get(f)
|
|
232
|
+
# outputs 优先; 否则取较新的 mtime
|
|
233
|
+
if existing is None or area == 'outputs' or mtime > existing['mtime']:
|
|
234
|
+
user_prds[f] = entry
|
|
235
|
+
|
|
236
|
+
for prd in user_prds.values():
|
|
218
237
|
key = prd['file']
|
|
219
238
|
is_new = key not in collected
|
|
220
239
|
is_updated = not is_new and collected[key].get('mtime', 0) < prd['mtime']
|