@hupan56/wlkj 3.1.7 → 3.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "3.1.7",
3
+ "version": "3.1.9",
4
4
  "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": {
6
6
  "wlkj": "bin/cli.js"
@@ -270,9 +270,13 @@ class McpMemoryProvider:
270
270
  import hashlib
271
271
  import json
272
272
  import time as _time
273
- from foundation.protocol.duckdb_conn import get_db, init_schema
273
+ # 不能 import init_schema! duckdb_conn 故意不导出它 (领域 DDL 留在各域)。
274
+ # 旧版 from ...duckdb_conn import init_schema → ImportError → 被下面 except 吞掉
275
+ # → INSERT 永不执行 → remember() 静默丢全部写入。
276
+ # learning_events 表已由 self.available 的 CREATE TABLE IF NOT EXISTS 保证存在,
277
+ # 这里直接 INSERT 即可。
278
+ from foundation.protocol.duckdb_conn import get_db
274
279
  con = get_db()
275
- init_schema(con)
276
280
  eid = hashlib.md5(("%s:%s:%s" % (key, content, _time.time())).encode()).hexdigest()[:16]
277
281
  ts = _time.strftime("%Y-%m-%dT%H:%M:%S")
278
282
  data = json.dumps({"content": content}, ensure_ascii=False)
@@ -33,7 +33,9 @@ def build_memory_chain(host: str) -> ProviderChain:
33
33
  from .adapters.cli import FileMemoryCap
34
34
 
35
35
  providers: list = [McpMemoryProvider()]
36
- if host == "qoderwork":
36
+ # host 名是 "qoder-work" (带连字符, 见 registry.py:40 detect_host 返回值)。
37
+ # 旧版写成 "qoderwork" (无连字符) → 永不匹配 → QwMemoryCap 在所有 QoderWork 机器上都不激活。
38
+ if host == "qoder-work":
37
39
  from .adapters.qw import QwMemoryCap
38
40
  providers.append(QwMemoryCap())
39
41
  providers.append(FileMemoryCap())
@@ -158,7 +158,9 @@ def wiki_query(kw):
158
158
  return ("wiki-index.json 不存在。请手动构建(一次性):\n"
159
159
  " python .qoder/scripts/domain/kg/search/search_index.py wiki")
160
160
  try:
161
- from repowiki import search_wiki
161
+ # v3.x: repowiki 在 domain/kg/server/ 子包下, 不能裸 from repowiki import
162
+ # (sys.path 没含 server/, 永远 ModuleNotFoundError → search_wiki 永远报"模块缺失")
163
+ from domain.kg.server.repowiki import search_wiki
162
164
  from foundation.integrations.terms import get_cn_map_with_auto
163
165
  except ImportError as e:
164
166
  return "Wiki 搜索模块缺失: %s" % str(e)
@@ -10,7 +10,7 @@ for _ in range(10):
10
10
  break
11
11
  if _cp not in _s.path: _s.path.insert(0, _cp)
12
12
  from bootstrap import setup; setup()
13
- from foundation.core.paths import get_repo_root
13
+ from foundation.core.paths import get_repo_root
14
14
 
15
15
  """
16
16
  QODER Context Pack - 一次调用返回写 PRD/原型所需的全部上下文
@@ -48,7 +48,7 @@ except (AttributeError, TypeError, OSError, IOError):
48
48
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
49
49
  from foundation.integrations.terms import CN_MAP, BUSINESS_PATH_MAP, CN_TO_EN, get_platform_map, expand_chinese_query
50
50
 
51
- BASE = get_repo_root()
51
+ BASE = get_repo_root()
52
52
  INDEX_DIR = os.path.join(BASE, 'data', 'index')
53
53
  PLATFORM_MAP = get_platform_map()
54
54
  MIN_FUZZY_LEN = 4
@@ -309,7 +309,7 @@ def _compute_and_print(query, platform, page_type):
309
309
  code_dir = os.path.join(BASE, 'data', 'code')
310
310
  wiki_results = []
311
311
  try:
312
- from repowiki import search_wiki, build_wiki_index
312
+ from domain.kg.server.repowiki import search_wiki, build_wiki_index
313
313
  from foundation.integrations.terms import get_cn_map_with_auto
314
314
  wiki_idx_path = os.path.join(BUILD_CACHE_DIR, 'wiki-index.json')
315
315
  if not os.path.isfile(wiki_idx_path):
@@ -296,7 +296,7 @@ def _print_merged(query, platform, primary_cn, en_words, collected):
296
296
  code_dir = os.path.join(BASE, 'data', 'code')
297
297
  wiki_results = []
298
298
  try:
299
- from repowiki import search_wiki, build_wiki_index
299
+ from domain.kg.server.repowiki import search_wiki, build_wiki_index
300
300
  wiki_idx_path = os.path.join(BUILD_CACHE_DIR, 'wiki-index.json')
301
301
  if not os.path.isfile(wiki_idx_path):
302
302
  build_wiki_index(code_dir, wiki_idx_path)
@@ -768,7 +768,7 @@ def show_vben():
768
768
  def search_wiki_cli(query):
769
769
  """搜 Repo Wiki (Qoder IDE 生成的语义级模块文档)。"""
770
770
  try:
771
- from repowiki import search_wiki, build_wiki_index
771
+ from domain.kg.server.repowiki import search_wiki, build_wiki_index
772
772
  from foundation.integrations.terms import get_cn_map_with_auto
773
773
  except ImportError:
774
774
  print('Wiki 搜索不可用 (common.repowiki 模块缺失)')
@@ -141,16 +141,25 @@ def snapshot(repo=None):
141
141
  data["index_age_days"] = _index_freshness(meta)
142
142
 
143
143
  # 3. EVA 评估历史 (健康度趋势)
144
- data["eval_history"] = _read_jsonl_tail(
145
- os.path.join(qd, "learning", "eval-history.jsonl"), n=10)
144
+ # ⚠ 真实路径是 data/learning/eval-history.jsonl (旧版写 .qoder/learning/ 是历史 bug,
145
+ # eval_prd.py/learn.py 早改了, 这里要对齐 EVAL_HISTORY_PATH, 否则 /wl-status 永远 null)
146
+ try:
147
+ from foundation.core.paths import EVAL_HISTORY_PATH
148
+ eval_path = str(EVAL_HISTORY_PATH)
149
+ except Exception:
150
+ eval_path = os.path.join(repo, "data", "learning", "eval-history.jsonl")
151
+ data["eval_history"] = _read_jsonl_tail(eval_path, n=10)
146
152
 
147
- # 4. 学习反馈
148
- data["feedback_count"] = None
149
- fb = os.path.join(qd, "learning", "feedback.jsonl")
150
- if os.path.exists(fb):
153
+ # 4. 学习反馈 (扫所有成员的 journal/feedback.jsonl, learn.py 写这里)
154
+ data["feedback_count"] = 0
155
+ members_dir = os.path.join(repo, "workspace", "members")
156
+ if os.path.isdir(members_dir):
151
157
  try:
152
- with open(fb, encoding="utf-8") as f:
153
- data["feedback_count"] = sum(1 for _ in f)
158
+ for m in os.listdir(members_dir):
159
+ fb = os.path.join(members_dir, m, "journal", "feedback.jsonl")
160
+ if os.path.isfile(fb):
161
+ with open(fb, encoding="utf-8") as f:
162
+ data["feedback_count"] += sum(1 for _ in f)
154
163
  except Exception:
155
164
  pass
156
165
 
@@ -172,8 +181,8 @@ def snapshot(repo=None):
172
181
  # 8. 任务目录结构
173
182
  data["tasks_dir"] = _list_dir(os.path.join(qd, "workspace", "tasks"))
174
183
 
175
- # 9. 学习模式
176
- data["patterns"] = _list_dir(os.path.join(qd, "learning", "patterns"))
184
+ # 9. 学习模式 (data/learning/patterns/, 非 .qoder/learning/)
185
+ data["patterns"] = _list_dir(os.path.join(repo, "data", "learning", "patterns"))
177
186
 
178
187
  # 10. 成员目录 (团队规模)
179
188
  data["members"] = _list_dir(os.path.join(qd, "workspace", "members"))
@@ -10,7 +10,7 @@ for _ in range(10):
10
10
  break
11
11
  if _cp not in _s.path: _s.path.insert(0, _cp)
12
12
  from bootstrap import setup; setup()
13
- from foundation.core.paths import get_repo_root
13
+ from foundation.core.paths import get_repo_root
14
14
 
15
15
  """
16
16
  archive_prd.py - PRD 按时间归档到个人工作区
@@ -45,44 +45,10 @@ except (AttributeError, TypeError, OSError, IOError):
45
45
  except Exception:
46
46
  pass
47
47
 
48
- # common/ 可被 import(直接当脚本跑时)
49
- _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
50
- _COMMON = os.path.join(_THIS_DIR, "foundation")
51
- if _COMMON not in sys.path:
52
- sys.path.insert(0, _COMMON)
53
-
54
- try:
55
- from paths import get_repo_root, get_developer, MEMBERS_DIR
56
- except ImportError:
57
- # 极端兜底:直接算路径
58
- def get_repo_root():
59
- current = Path(_THIS_DIR).resolve()
60
- for _ in range(10):
61
- if (current / ".qoder").is_dir():
62
- return current
63
- parent = current.parent
64
- if parent == current:
65
- break
66
- current = parent
67
- return Path(_THIS_DIR).parent.parent
68
-
69
- def get_developer(repo_root=None):
70
- dev = (repo_root or get_repo_root()) / ".qoder" / ".developer"
71
- if not dev.is_file():
72
- return None
73
- for enc in ("utf-8", "gbk"):
74
- try:
75
- for line in dev.read_text(encoding=enc).splitlines():
76
- line = line.strip()
77
- if "=" in line and line.split("=")[0].strip() == "name":
78
- return line.split("=", 1)[1].strip() or None
79
- if ":" in line and line.split(":")[0].strip() == "name":
80
- return line.split(":", 1)[1].strip() or None
81
- except (OSError, IOError, UnicodeDecodeError):
82
- continue
83
- return None
84
-
85
- MEMBERS_DIR = None # 下面用 repo_root 拼
48
+ # v3.x: 顶部 bootstrap 已 setup, 直接 import foundation.* (旧版重复尝试 from paths
49
+ # + 手写兜底, 兜底会与 foundation.core.paths 静默漂移, 这里统一走真源)
50
+ from foundation.core.paths import get_repo_root as _grr, get_developer, MEMBERS_DIR
51
+ get_repo_root = _grr # 已在顶部 import, 这里显式重绑确保本模块可见
86
52
 
87
53
 
88
54
  # ============================================================
@@ -60,15 +60,38 @@ class ZentaoClient:
60
60
 
61
61
  @classmethod
62
62
  def from_config(cls) -> Optional['ZentaoClient']:
63
- """从 .qoder/config.yaml 的 zentao 段构造(CLI 用)。凭据不全返回 None。"""
63
+ """从 .qoder/config.yaml 的 zentao 段构造(CLI 用)。凭据不全返回 None。
64
+
65
+ v3.1.4 设计: url 团队共享(放 config.yaml), user/password 个人凭据(放
66
+ workspace/members/{dev}/.private/secrets/zentao.env, gitignored)。
67
+ 所以这里 url 从 config 读, user/password 从 zentao.env 读 ——
68
+ 与 mcp_launcher.launch_zentao() 完全对齐 (旧版从 config 读 user/password
69
+ → 永远 None, 因为 config 故意不放个人凭据)。
70
+ """
71
+ # ① url 从 config.yaml
72
+ url = ''
64
73
  try:
65
74
  from foundation.core.config import load_config_section, SECTIONS
66
75
  cfg = load_config_section(SECTIONS.ZENTAO)
76
+ url = (cfg.get('url') or '').strip()
67
77
  except Exception:
68
- return None
69
- url = (cfg.get('url') or '').strip()
70
- user = (cfg.get('user') or '').strip()
71
- pwd = (cfg.get('password') or '')
78
+ url = ''
79
+ # user/password 从当前开发者的 .private/secrets/zentao.env
80
+ user = pwd = ''
81
+ try:
82
+ from foundation.core.paths import get_secrets_dir, get_developer
83
+ dev = get_developer()
84
+ if dev:
85
+ env_file = os.path.join(get_secrets_dir(dev), 'zentao.env')
86
+ if os.path.isfile(env_file):
87
+ for line in open(env_file, encoding='utf-8'):
88
+ line = line.strip()
89
+ if line.startswith('ZENTAO_USER='):
90
+ user = line.split('=', 1)[1].strip().strip('"').strip("'")
91
+ elif line.startswith('ZENTAO_PASSWORD='):
92
+ pwd = line.split('=', 1)[1].strip().strip('"').strip("'")
93
+ except Exception:
94
+ pass
72
95
  if not url or not user or not pwd:
73
96
  return None
74
97
  return cls(url, user, pwd)
@@ -86,6 +86,23 @@ def _ensure_imports():
86
86
  from foundation.protocol.mcp_base import BaseMCPServer, tool
87
87
 
88
88
 
89
+ def _require_kgdb():
90
+ """检查 kg.duckdb 是否存在。存在返回 None; 不存在返回友好提示字符串。
91
+
92
+ 防止 get_db() 创建空库后查询报 CatalogException (49 台没预建图谱的机器)。
93
+ """
94
+ try:
95
+ from foundation.core.paths import DATA_INDEX_DIR
96
+ db_path = DATA_INDEX_DIR / 'kg.duckdb'
97
+ if not db_path.is_file():
98
+ return ("知识图谱未构建 (kg.duckdb 不存在)。请管理员跑:\n"
99
+ " python .qoder/scripts/orchestration/wlkj.py kg-build\n"
100
+ "或非管理员: npx @hupan56/wlkj update (会 git pull 拉团队已构建的 kg.duckdb)")
101
+ except Exception:
102
+ pass
103
+ return None
104
+
105
+
89
106
  class KnowledgeGraphMCPServer(BaseMCPServer):
90
107
  """知识图谱 MCP Server。
91
108
 
@@ -185,6 +202,11 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
185
202
  q = (args.get("module") or '').strip()
186
203
  if not q:
187
204
  return "请提供模块名。可选: assets/cost/hr/maintenance/operation/qualityAndSafety/veh/settings/dashboard/flow/systems/monitor/quasafe"
205
+ # ⚠ 先检查 kg.duckdb 存在! get_db() 会创建空库 → 查询报 CatalogException
206
+ # (49 台没预建图谱的机器会看到原始异常, 而非友好提示)。
207
+ _hint = _require_kgdb()
208
+ if _hint:
209
+ return _hint
188
210
  con = get_db()
189
211
  try:
190
212
  return workflow_query(con, q)
@@ -209,6 +231,9 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
209
231
  except (TypeError, ValueError):
210
232
  depth = 3
211
233
  depth = max(1, min(5, depth))
234
+ _hint = _require_kgdb()
235
+ if _hint:
236
+ return _hint
212
237
  con = get_db()
213
238
  try:
214
239
  return hop_query(con, sym, depth, multi_hop_fn=multi_hop)
@@ -236,6 +261,9 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
236
261
  def _t_search_field(self, args):
237
262
  from domain.kg.storage.kg_duckdb import get_db
238
263
  field = (args.get("field") or '').strip()
264
+ _hint = _require_kgdb()
265
+ if _hint:
266
+ return _hint
239
267
  con = get_db()
240
268
  try:
241
269
  rows = con.execute(
@@ -260,6 +288,9 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
260
288
  def _t_list_modules(self, args):
261
289
  from domain.kg.storage.kg_duckdb import get_db
262
290
  platform = args.get("platform", "")
291
+ _hint = _require_kgdb()
292
+ if _hint:
293
+ return _hint
263
294
  con = get_db()
264
295
  try:
265
296
  if platform == 'web':
@@ -1,81 +1,81 @@
1
- # Figma 协作指南(设计师专用)
2
-
3
- > 工作流出的 HTML 原型 → Figma 精修 → 录回 spec.json 闭环
4
-
5
- ## 为什么这条路可行
6
-
7
- 工作流用 `fill_prototype.py` 出的 HTML 原型有以下特征,**非常适合 html.to.design 插件转换**:
8
-
9
- - CSS 用标准 CSS 变量(`:root { --primary: ... }`),插件能正确解析成 Figma Variables
10
- - 布局用 Flexbox/Grid,转换后图层结构清晰
11
- - 颜色是 Vben 真实 HSL token(不是 #1890ff),导入后直接是正确配色
12
- - 图标是内联 SVG(Ant Design),导入后是可编辑矢量
13
-
14
- ## 完整流程(4 步)
15
-
16
- ### Step 1: PM 出原型(工作流侧)
17
-
18
- PM 在 QoderWork 说"出个 XX 的原型",工作流输出:
19
-
20
- ```
21
- workspace/members/{pm}/drafts/prototype-{需求名}-web.html
22
- ```
23
-
24
- 设计师从 PM 那拿到这个 HTML 文件。
25
-
26
- ### Step 2: 导入 Figma(设计师侧)
27
-
28
- 1. 打开 Figma,新建一个 Frame
29
- 2. 菜单:**Plugins → html.to.design**(by ‹div›RIOTS)
30
- - 没装的话:Figma Community 搜 "html.to.design" 安装(免费)
31
- 3. 插件面板里选 **File** 标签(不是 URL 标签)
32
- 4. 把 HTML 文件**拖进拖放区**(或点选文件)
33
- 5. 点 **Import**,等几秒
34
-
35
- **结果**:HTML 变成完全可编辑的 Figma 图层——每个元素是独立的 Frame/Text/Rectangle,颜色是正确的 Vben 配色。
36
-
37
- ### Step 3: 设计师精修(Figma 侧)
38
-
39
- 设计师在 Figma 里做只有人能做的精修:
40
-
41
- - 调间距/对齐(AI 的间距可能不精确)
42
- - 优化交互细节(hover 态、动画、过渡)
43
- - 换更合适的图标/插图
44
- - 调整信息层级和视觉重点
45
- - 加设计师特有的"手感"
46
-
47
- **注意**:颜色 token(`--primary` 等)尽量别改——它们是系统统一的,改了前端实现时会对不上。
48
-
49
- ### Step 4: 录回工作流(闭环)
50
-
51
- 设计师精修完,在 QoderWork 里说:
52
-
53
- > "录入设计稿"
54
-
55
- 工作流把 Figma 最终稿的关键决策(布局/配色/组件)录成 `data/style/{需求名}-design-spec.json`。
56
-
57
- 从此:
58
- - 下次同需求出原型,工作流**优先用这份 spec**
59
- - 前端拿到的 HTML 原型**匹配设计师的 Figma 终稿**
60
- - 设计师的劳动成果**沉淀进知识图谱**,不浪费
61
-
62
- ## 常见问题
63
-
64
- **Q: 导入后图层太碎怎么办?**
65
- A: html.to.design 会按 HTML DOM 结构生成图层。fill_prototype 出的 HTML 结构已经很干净(侧边栏/搜索栏/表格各自独立),不会太碎。如果某个区域图层太多,选中后用 Figma 的 "Flatten" 合并。
66
-
67
- **Q: 表格数据是假的怎么办?**
68
- A: 对的,原型里的表格行是示例数据("XX示例1")。这是故意的——原型验证的是布局和交互,不是数据。真实数据前端会从 API 取。
69
-
70
- **Q: 图标导入后变位图了?**
71
- A: 不会。工作流用的图标是内联 SVG(不是图片标签),html.to.design 会保留为矢量。如果个别图标显示异常,从 `data/index/icon-reference.json` 找原始 SVG 手动替换。
72
-
73
- **Q: APP 端原型也能导入吗?**
74
- A: 能。`prototype-{需求名}-app.html` 同样是标准 HTML,导入方式一样。APP 原型用的是 Vant 风格(移动端组件库)。
75
-
76
- ## html.to.design 插件安装
77
-
78
- - Figma Community 搜索:`html.to.design`
79
- - 或直接访问:https://www.figma.com/community/plugin/1159123024924461424
80
- - 免费版每次导入有限制,团队版无限
81
- - 也有 Chrome 扩展版(适合截取在线页面)
1
+ # Figma 协作指南(设计师专用)
2
+
3
+ > 工作流出的 HTML 原型 → Figma 精修 → 录回 spec.json 闭环
4
+
5
+ ## 为什么这条路可行
6
+
7
+ 工作流用 `fill_prototype.py` 出的 HTML 原型有以下特征,**非常适合 html.to.design 插件转换**:
8
+
9
+ - CSS 用标准 CSS 变量(`:root { --primary: ... }`),插件能正确解析成 Figma Variables
10
+ - 布局用 Flexbox/Grid,转换后图层结构清晰
11
+ - 颜色是 Vben 真实 HSL token(不是 #1890ff),导入后直接是正确配色
12
+ - 图标是内联 SVG(Ant Design),导入后是可编辑矢量
13
+
14
+ ## 完整流程(4 步)
15
+
16
+ ### Step 1: PM 出原型(工作流侧)
17
+
18
+ PM 在 QoderWork 说"出个 XX 的原型",工作流输出:
19
+
20
+ ```
21
+ workspace/members/{pm}/drafts/prototype-{需求名}-web.html
22
+ ```
23
+
24
+ 设计师从 PM 那拿到这个 HTML 文件。
25
+
26
+ ### Step 2: 导入 Figma(设计师侧)
27
+
28
+ 1. 打开 Figma,新建一个 Frame
29
+ 2. 菜单:**Plugins → html.to.design**(by ‹div›RIOTS)
30
+ - 没装的话:Figma Community 搜 "html.to.design" 安装(免费)
31
+ 3. 插件面板里选 **File** 标签(不是 URL 标签)
32
+ 4. 把 HTML 文件**拖进拖放区**(或点选文件)
33
+ 5. 点 **Import**,等几秒
34
+
35
+ **结果**:HTML 变成完全可编辑的 Figma 图层——每个元素是独立的 Frame/Text/Rectangle,颜色是正确的 Vben 配色。
36
+
37
+ ### Step 3: 设计师精修(Figma 侧)
38
+
39
+ 设计师在 Figma 里做只有人能做的精修:
40
+
41
+ - 调间距/对齐(AI 的间距可能不精确)
42
+ - 优化交互细节(hover 态、动画、过渡)
43
+ - 换更合适的图标/插图
44
+ - 调整信息层级和视觉重点
45
+ - 加设计师特有的"手感"
46
+
47
+ **注意**:颜色 token(`--primary` 等)尽量别改——它们是系统统一的,改了前端实现时会对不上。
48
+
49
+ ### Step 4: 录回工作流(闭环)
50
+
51
+ 设计师精修完,在 QoderWork 里说:
52
+
53
+ > "录入设计稿"
54
+
55
+ 工作流把 Figma 最终稿的关键决策(布局/配色/组件)录成 `data/style/{需求名}-design-spec.json`。
56
+
57
+ 从此:
58
+ - 下次同需求出原型,工作流**优先用这份 spec**
59
+ - 前端拿到的 HTML 原型**匹配设计师的 Figma 终稿**
60
+ - 设计师的劳动成果**沉淀进知识图谱**,不浪费
61
+
62
+ ## 常见问题
63
+
64
+ **Q: 导入后图层太碎怎么办?**
65
+ A: html.to.design 会按 HTML DOM 结构生成图层。fill_prototype 出的 HTML 结构已经很干净(侧边栏/搜索栏/表格各自独立),不会太碎。如果某个区域图层太多,选中后用 Figma 的 "Flatten" 合并。
66
+
67
+ **Q: 表格数据是假的怎么办?**
68
+ A: 对的,原型里的表格行是示例数据("XX示例1")。这是故意的——原型验证的是布局和交互,不是数据。真实数据前端会从 API 取。
69
+
70
+ **Q: 图标导入后变位图了?**
71
+ A: 不会。工作流用的图标是内联 SVG(不是图片标签),html.to.design 会保留为矢量。如果个别图标显示异常,从 `data/index/ref-icon.json` 找原始 SVG 手动替换。
72
+
73
+ **Q: APP 端原型也能导入吗?**
74
+ A: 能。`prototype-{需求名}-app.html` 同样是标准 HTML,导入方式一样。APP 原型用的是 Vant 风格(移动端组件库)。
75
+
76
+ ## html.to.design 插件安装
77
+
78
+ - Figma Community 搜索:`html.to.design`
79
+ - 或直接访问:https://www.figma.com/community/plugin/1159123024924461424
80
+ - 免费版每次导入有限制,团队版无限
81
+ - 也有 Chrome 扩展版(适合截取在线页面)
@@ -32,21 +32,21 @@ Generate production-ready HTML prototypes that match the project's actual UI sty
32
32
  - 若 `.qoder/.current-task` 存在,原型命名带上任务关键词
33
33
  - 平台(Web/APP/Both)必须明确;如未指定,先问用户(见 prd-generator Step 0)
34
34
 
35
- ## ⚠️ 前置门: 用户是否选了"画原型"?(v3.1 新增)
36
-
37
- 本 skill **只在用户明确选择"画原型"后才进入**。
38
-
39
- - 如果上游 `/wl-prd-full` 或 `/wl-prd` 在「批量确认」环节已决定**不画**(PRD 第 1 行
40
- `@contract` 头标记 `prototype: none`),**立即退出本 skill**,不生成任何 HTML。
41
- - 如果上游没有 contract 头(独立调用本 skill),**先问用户**:
42
- `这个需求要画原型吗?(画 / 不画)`
43
- 用户答"不画"→ 退出;用户答"画"→ 继续。
44
- - **绝不擅自决定画/不画**。决策权在用户,本 skill 只执行。
45
-
46
- > 这条门治"AI 默默画了个塑料骨架"的反模式——比如"出交车点检固定为总部全局规则"
47
- > 这种纯规则下发型需求,AI 不该自己往里塞一个配置页原型。
48
-
49
- ---
35
+ ## ⚠️ 前置门: 用户是否选了"画原型"?(v3.1 新增)
36
+
37
+ 本 skill **只在用户明确选择"画原型"后才进入**。
38
+
39
+ - 如果上游 `/wl-prd-full` 或 `/wl-prd` 在「批量确认」环节已决定**不画**(PRD 第 1 行
40
+ `@contract` 头标记 `prototype: none`),**立即退出本 skill**,不生成任何 HTML。
41
+ - 如果上游没有 contract 头(独立调用本 skill),**先问用户**:
42
+ `这个需求要画原型吗?(画 / 不画)`
43
+ 用户答"不画"→ 退出;用户答"画"→ 继续。
44
+ - **绝不擅自决定画/不画**。决策权在用户,本 skill 只执行。
45
+
46
+ > 这条门治"AI 默默画了个塑料骨架"的反模式——比如"出交车点检固定为总部全局规则"
47
+ > 这种纯规则下发型需求,AI 不该自己往里塞一个配置页原型。
48
+
49
+ ---
50
50
  ## Step 0: Know the Platform
51
51
 
52
52
  The platform MUST already be decided (asked by /wl-prd-full Step 0).
@@ -229,12 +229,12 @@ Key design tokens already in the templates (do not override):
229
229
 
230
230
  ## ⚠️ Icons: NEVER emoji, ALWAYS the project's real icon system
231
231
 
232
- Source of truth: **data/index/icon-reference.json** (real usage counts +
232
+ Source of truth: **data/index/ref-icon.json** (real usage counts +
233
233
  ready-to-paste SVGs extracted from the actual projects).
234
234
 
235
- - **Web (fywl-ui)**: Ant Design icons. Inline the SVG from icon-reference.json:
235
+ - **Web (fywl-ui)**: Ant Design icons. Inline the SVG from ref-icon.json:
236
236
  ```html
237
- <span class="anticon">{svg from icon-reference.json web.svg.outlined[...]}</span>
237
+ <span class="anticon">{svg from ref-icon.json web.svg.outlined[...]}</span>
238
238
  <style>.anticon svg{width:1em;height:1em;fill:currentColor}</style>
239
239
  ```
240
240
  Most-used in the real app: Download(303x), Plus(171x), Delete, Search, Edit...
@@ -260,31 +260,31 @@ ready-to-paste SVGs extracted from the actual projects).
260
260
  4. ICS 的菜单来自后端接口(`access_mode: backend`),静态代码里抓不到完整菜单树,
261
261
  所以菜单层级**必须由需求/PRD 提供**,不要去代码里找。
262
262
 
263
- ## 🔒 Step 3.5: 来源锚定 + UTF-8(硬门 #3,eval A2 检查)
264
-
265
- ### 来源锚定(必做)
266
-
267
- 生成的 HTML 必须在 `<head>` 里写一行注释,标注样式来源的真实文件:
268
-
269
- ```html
270
- <!-- @prototype-source: fywl-ui/apps/ics/src/views/xxx/index.vue, fywl-ui/apps/ics/src/api/xxx.ts -->
271
- <meta name="x-source-pages" content="fywl-ui/.../index.vue">
272
- ```
273
-
274
- - 来源文件**必须来自 Step 2 读过的真实 Vue/TS 文件**(prefetch 返回的 page_examples)
275
- - 写不出具体文件名 = 你没做 Step 2 = 违规
276
- - eval A2 会检查:HTML 含 `@prototype-source:` 或 `x-source-pages` → 通过;否则 A2 扣 5 分
277
-
278
- ### UTF-8 强制(治编码乱码)
279
-
280
- 生成的 HTML **必须用 UTF-8 写入**,否则中文 `<title>`/注释会变成 `鍑轰氦杞?` 乱码。
281
-
282
- - Write 工具调用时,内容里的中文必须是原始 UTF-8(不要从 GBK 源复制)
283
- - 如果用 Python 写文件:`open(path, 'w', encoding='utf-8')`
284
- - **绝不要从 bash 输出里复制中文再粘贴**(bash 在 Windows 上可能转 GBK)→ 优先让
285
- `fill_prototype` 脚本直接出文件,AI 只 Edit 占位符
286
-
287
- ---
263
+ ## 🔒 Step 3.5: 来源锚定 + UTF-8(硬门 #3,eval A2 检查)
264
+
265
+ ### 来源锚定(必做)
266
+
267
+ 生成的 HTML 必须在 `<head>` 里写一行注释,标注样式来源的真实文件:
268
+
269
+ ```html
270
+ <!-- @prototype-source: fywl-ui/apps/ics/src/views/xxx/index.vue, fywl-ui/apps/ics/src/api/xxx.ts -->
271
+ <meta name="x-source-pages" content="fywl-ui/.../index.vue">
272
+ ```
273
+
274
+ - 来源文件**必须来自 Step 2 读过的真实 Vue/TS 文件**(prefetch 返回的 page_examples)
275
+ - 写不出具体文件名 = 你没做 Step 2 = 违规
276
+ - eval A2 会检查:HTML 含 `@prototype-source:` 或 `x-source-pages` → 通过;否则 A2 扣 5 分
277
+
278
+ ### UTF-8 强制(治编码乱码)
279
+
280
+ 生成的 HTML **必须用 UTF-8 写入**,否则中文 `<title>`/注释会变成 `鍑轰氦杞?` 乱码。
281
+
282
+ - Write 工具调用时,内容里的中文必须是原始 UTF-8(不要从 GBK 源复制)
283
+ - 如果用 Python 写文件:`open(path, 'w', encoding='utf-8')`
284
+ - **绝不要从 bash 输出里复制中文再粘贴**(bash 在 Windows 上可能转 GBK)→ 优先让
285
+ `fill_prototype` 脚本直接出文件,AI 只 Edit 占位符
286
+
287
+ ---
288
288
  ## Step 4: Highlight Changes + Interactivity
289
289
 
290
290
  - Mark NEW/CHANGED elements with the template's `highlight-new` class
@@ -37,7 +37,19 @@ git status && git diff HEAD --stat
37
37
 
38
38
  ### Step 2: 质量门禁(提交前自检)
39
39
 
40
- 逐项检查,任何一项不过都暂停问用户:
40
+ **2a. 程序级强制门禁(syncgate)—— 先 git add 再跑**
41
+ ```bash
42
+ # 1. 精确 add (不用 git add .)
43
+ git add <相关文件>
44
+ # 2. 对暂存文件跑零信任门禁 (身份/作者/秘密扫描/REQ-ID)
45
+ python -c "import sys; sys.path.insert(0, r'$R/.qoder/scripts'); from domain.task.syncgate import run_gates; from foundation.core.paths import get_developer, get_repo_root; import subprocess; staged=subprocess.check_output(['git','diff','--cached','--name-only'],cwd=r'$R',text=True).split(); ok,reason=run_gates(staged, get_developer(), str(get_repo_root())); print('GATE_PASS' if ok else ('GATE_FAIL: '+reason)); sys.exit(0 if ok else 2)"
46
+ ```
47
+ - `GATE_PASS` → 继续 2b
48
+ - `GATE_FAIL: <原因>` → 停下,把原因告诉用户(如"检出疑似密钥 .env"),问修复还是强制。默认修复。
49
+ (这与 team_sync.py push 走的是同一道 syncgate,源码提交也不绕过零信任门禁。)
50
+
51
+ **2b. 人工自检(grep 类)**
52
+ 逐项检查:
41
53
  - [ ] 无 TODO / FIXME 残留(grep 一下)
42
54
  - [ ] 金额字段用了 BigDecimal(不出现 double/float 存钱)
43
55
  - [ ] 命名/风格跟周围代码一致
@@ -60,8 +72,8 @@ type 选 feat / fix / refactor / docs / test / chore。
60
72
  **用户没明确说"提交/确认"之前,不要真的 commit。**
61
73
 
62
74
  ### Step 5: 提交 + 推送
75
+ (文件已在 Step 2a git add 过,门禁已过)
63
76
  ```bash
64
- git add <相关文件> # 精确 add,不用 git add .
65
77
  git commit -m "<确认后的信息>"
66
78
  git pull --rebase # 先拉远端
67
79
  git push
@@ -1,275 +1,275 @@
1
- # QODER Pipeline
2
-
3
- ## 🏗️ 架构: 引擎与数据分离 (Trellis 化)
4
-
5
- `.qoder/` 一分为二 (v2.8.0+):
6
-
7
- | 类别 | 内容 | 来源 | 进团队 git? |
8
- |------|------|------|------------|
9
- | **引擎代码** | `.qoder/scripts/` `skills/` `commands/` `agents/` `contracts/` `hooks/` `templates/` `rules/` | `npx @hupan56/wlkj init/update` 生成 | ❌ gitignore |
10
- | **团队数据** | `.qoder/config.yaml` `config.toml` `settings.json` `learning/` + `data/` (kg.duckdb/索引) + `workspace/` (PRD/任务) | git 跟踪, 团队共享 | ✅ |
11
-
12
- - 引擎代码**只由 npm 包生成**, 不再进团队 git → 彻底消除"引擎更新污染团队 git"和"新机 init 产生 unrelated histories"。
13
- - Qoder 全系列仍读 `.qoder/` (路径不变), 只是引擎来源从 git 变成 npm 生成。
14
- - 老团队升级 (管理员跑一次): `npx @hupan56/wlkj migrate`, 然后团队成员各自 `npx @hupan56/wlkj update`。
15
-
16
- ---
17
-
18
- ## ⚠️ CRITICAL RULE #1: ALWAYS ASK PLATFORM FIRST
19
-
20
- **When user asks for PRD or prototype, you MUST ask this question FIRST:**
21
-
22
- ```
23
- 这个需求是针对哪个平台?
24
-
25
- 1. **Web 管理端** (fywl-ui) - Ant Design Vue + VxeGrid 风格
26
- 2. **APP 移动端** (Carmg-H5) - Vant 风格
27
- 3. **两端都要**
28
-
29
- 请选择 (1/2/3):
30
- ```
31
-
32
- **DO NOT skip this question. DO NOT auto-detect. ALWAYS ASK.**
33
- **Wait for user answer before doing ANYTHING else.**
34
- **DO NOT search/read/analyze in the same turn as asking — the reply MUST end after the question.**
35
- Asking platform + simultaneously searching code in the same response is a VIOLATION.
36
-
37
- ---
38
-
39
- ## CRITICAL RULE #2: Use /wl-search to find code
40
-
41
- When user asks about code, business, API, feature:
42
- 1. **ALWAYS** use `/wl-search <keyword>` command first
43
- 2. Then read only the specific files returned
44
- 3. **NEVER** grep the entire codebase directly
45
-
46
- Examples:
47
- - "考勤代码在哪" -> /wl-search attendance
48
- - "保险定时任务" -> /wl-search insurance
49
- - "薪资API" -> /wl-search --api salary
50
-
51
- ## CRITICAL RULE #2.5: 触发方式(非侵入式 — v3.0)
52
-
53
- 工作流**只在显式调用时启动**,不主动干预普通对话。
54
-
55
- **两种触发方式(且仅此两种):**
56
- 1. **斜杠命令**:`/wl-prd` `/wl-search` `/wl-req` 等 → 启动工作流
57
- 2. **npx**:`npx @hupan56/wlkj init/update` → 安装/同步
58
-
59
- **自然语言不触发工作流。** 用户说"写个需求""查下代码"等,AI 正常回答,不自动拉入工作流。
60
- (工作流启动后的过程内交互 — 如选平台、确认需求 — 仍可用自然语言。)
61
-
62
- ### 禅道自然查询(非命令,直接回答)
63
-
64
- 用户随口问禅道("我的任务""看下#301""Bug 状态""我的需求")—— **直接用禅道 MCP 工具自然回答**,
65
- 不拉入 `/wl-task` 工作流:
66
- - QoderWork: `qw_mcp_call("my_workbench", {})` 一行看全(任务+需求+Bug)
67
- - CLI/其他: `cap.mcp.call("my_workbench")`
68
-
69
- 只有**显式工序**(`/wl-task` 建需求/记工时/改状态)才走工作流。
70
- > 工具用真实工具名,别拿需求冒充任务回答(问任务查 task 域,查不到如实说"没有")。
71
-
72
-
73
- 注意区分:"同步/保存产出" = team_sync(push);"提交/commit 代码" = wl-commit。
74
-
75
- ## CRITICAL RULE #3: User NEVER touches git
76
-
77
- PMs on this team do not know git. ALL git operations are invisible:
78
- - After any publish/save action (/wl-prd publish, /wl-task create/finish,
79
- /wl-report save), run: `python .qoder/scripts/task/team_sync.py push`
80
- - To get teammates' latest work: `python .qoder/scripts/task/team_sync.py pull`
81
- - If team_sync prints SYNC_CONFLICT, YOU (the AI) resolve the conflict
82
- yourself (it tells you the commands). NEVER ask the user to run git.
83
- - When the user says 同步/保存/提交 in natural language -> team_sync, not raw git.
84
-
85
- ## Platform Filtering
86
-
87
- After user selects platform, use it for search:
88
-
89
- ```bash
90
- # Search by platform
91
- /wl-search 保险 --platform web # Only fywl-ui (PC)
92
- /wl-search 保险 --platform app # Only Carmg-H5 (Mobile)
93
- /wl-search 保险 # Both platforms
94
-
95
- # Platform shortcuts
96
- web/pc/管理端 -> fywl-ui
97
- app/mobile/h5/移动端 -> Carmg-H5
98
- ```
99
-
100
- ## Commands: 7 Core (小而美) + Optional
101
-
102
- > 认知面只暴露 7 个核心命令,每个是一道工序站,每个带模式参数。
103
- > **一个命令当多个用**——记不住参数也没关系,自然语言一样路由。
104
- > 模块契约(输入/输出/校验)见 `.qoder/contracts/`。
105
-
106
- ### 7 Core Commands(记住这 7 个就够)
107
-
108
- | Command | 工序站 | 模式参数(第一个词路由) | Auto | 背后 skill |
109
- |---------|--------|------------------------|------|-----------|
110
- | `/wl-init` | 身份 | (无) | YES | wl-init |
111
- | `/wl-prd` | 需求 | 语义路由: 完整档/小改动/评审 (原 wl-prd-full/quick/review 合并) | YES | prd-generator/prd-review |
112
- | `/wl-design` | 设计 | `import`/`generate`/`review` 或默认(import) | YES | design-import/prototype-generator/design-review |
113
- | `/wl-task` | 任务 | `create`/`list`/`start`/`finish` | YES | wl-task |
114
- | `/wl-code` | 编码 | (无,按 spec 实现) | NO | spec-coder |
115
- | `/wl-test` | 测试 | `quick`/`browser`/`unit` 或默认(quick) | NO | wl-test / test-generator |
116
- | `/wl-commit` | 提交 | (无) | NO | wl-commit |
117
-
118
- ### Optional Commands(长大了再开,非执行链路必经)
119
-
120
- > 物理上归在 `.qoder/commands/optional/` 子目录(Qoder IDE 会按子目录归类显示),
121
- > QoderWork 安装脚本已支持递归扫描,4 个照样能装到用户级。
122
-
123
- | Command | 用途 | 何时用 |
124
- |---------|------|--------|
125
- | `/wl-search` | 代码索引搜索 | 全员工具,按需,非工序站(物理在 `commands/` 顶层) |
126
- | `/wl-req` | 需求 CRUD + 发布 | 需求生命周期管理(创建/编辑/发布/归档) |
127
- | `/wl-spec` | PRD→技术规格 | 需求复杂时(小团队可直接 `/wl-code`) |
128
- | `/wl-status` | 项目健康度/路线图 | 需要 PMO/汇报时 |
129
- | `/wl-report` | 日报周报 | 需要向上汇报时 |
130
- | `/wl-insight` | 需求澄清对焦(默认)/调研/规划 | 需求模糊听不懂、要做调研或规划时(位于 /wl-prd 之前)。最高频:产品听不懂用户时快速对焦(必要时出原型图帮助理解),对齐后再问要不要调研 |
131
-
132
- > **新人只需记 7 个核心命令**,能把活从需求干到提交。可选 6 个按场景启用。
133
-
134
- ## Knowledge Index
135
-
136
- 3 projects: Carmg-H5(1378 files), fywl-ics(8973 files, 3903 APIs), fywl-ui(3270 files)
137
- Search: /wl-search <keyword> returns compact results (~200 bytes)
138
- Weekly sync: python .qoder/scripts/task/git_sync.py
139
-
140
- ### MCP Servers (QoderWork / Qoder IDE)
141
- 注册在 `~/.qoderwork/mcp.json`,setup.py 一键配齐:
142
- - `qoder-knowledge-graph` (stdio) — 本仓库知识图谱,13 个工具 (search_code/api, get_impact, context_360, fill_prototype, get_design_system...)。自动拉起。
143
- - `playwright` (stdio) — 浏览器自动化,快且稳。自动拉起。
144
- - `qoder-mysql` (stdio) — 只读查真实数据库结构/数据。自动拉起。
145
- - `lanhu` (STDIO) — 蓝湖设计稿直读,设计师发链接 AI 提取颜色/尺寸/字体/切图。
146
- **STDIO 模式:开 QoderWork 自动起、关自动停**(QoderWork 原生进程管理,无需手动 start)。
147
- cookie 按角色隔离(UI 能改稿/PM 只读):wrapper 读当前 `.developer` → 该角色的 `workspace/members/{dev}/.secrets/lanhu.env`(gitignored)。换角色改 `.developer` + 配该角色 cookie → 重启 QoderWork。首次配置跑 `python .qoder/scripts/setup/setup_lanhu.py`。
148
-
149
- ### Style Index (Prototype Reference)
150
- UI patterns from Vue files: Tables(718), Forms(301), Detail(129), Dashboard(90)
151
- Top components: Space(583), Modal(270), Select(172), Input(134)
152
- Form components: Input(2262), Select(1049), ApiSelect(725), DatePicker(265)
153
- Field map: 2383 entries
154
-
155
- Style search:
156
- python .qoder/scripts/kg/search_index.py --style table/form/modal/dashboard
157
- python .qoder/scripts/kg/search_index.py --field <field_name>
158
- python .qoder/scripts/kg/search_index.py --components
159
- python .qoder/scripts/kg/search_index.py --vben # Vben Admin CSS variables
160
-
161
- Prototype templates:
162
- .qoder/templates/prototype-web.html # Web管理端 (Vben Admin + Ant Design)
163
- .qoder/templates/prototype-app.html # APP移动端 (Vant)
164
-
165
- Prototype output: workspace/members/{dev}/drafts/prototype-{feature}.html
166
- Style priority: Code > PDF > Default
167
-
168
- ## Workflow: 契约网(非强制流水线)
169
-
170
- 模块间**不强制顺序**,靠标准契约文件对接(详见 `.qoder/contracts/`)。
171
- 完整链路**可以**这么走,但任何模块都能独立开机:
172
-
173
- ```
174
- /wl-init → /wl-prd (ASK PLATFORM!) → /wl-design → /wl-task → /wl-code → /wl-test → /wl-commit
175
- 身份 需求 设计 任务 编码 测试 提交
176
- ```
177
-
178
- **独立性**:每个模块认文件不认流程。缺上游 → 手动喂个契约文件,或用自然语言 +
179
- `context_pack.py` 让 AI 自检索补全(见 `.qoder/contracts/fallback.md`)。
180
-
181
- ## Qoder 全系列兼容(IDE / Quest / CLI / QoderWork)
182
-
183
- This pipeline works on Windows + macOS, across the **whole Qoder family**.
184
- Each product loads rules from a different carrier — keep them in sync:
185
-
186
- | 产品/模式 | 规则载体 | 命令载体 |
187
- |-----------|---------|---------|
188
- | Qoder IDE 默认编辑器 | .qoder/rules/wl-pipeline.md (always_on) | .qoder/commands/wl-*.md |
189
- | Qoder Quest 模式 | .qoder/rules/ + .qoder/skills/ + .qoder/agents/ | 自然语言 -> skills |
190
- | Qoder CLI (qodercli) | 本文件 AGENTS.md | 自然语言 -> scripts |
191
- | QoderWork 桌面 Agent | 安装 .qoder/skills/ 下的技能 | 自然语言 -> skills |
192
-
193
- > 本流水线聚焦 Qoder 全系列,4 种载体功能等价。知识图谱 13 个能力(影响分析/
194
- > 覆盖矩阵/功能画像/业务流程/多跳遍历…)通过统一 CLI `python .qoder/scripts/kg/kg.py`
195
- > 全系列触达;QoderWork 环境也可直接调 MCP 工具。
196
-
197
- ## QoderWork Integration: Optional Enhancement Only (Progressive Enhancement)
198
-
199
- > 🔒 **核心原则:本流水线绝不强依赖 QoderWork。**
200
- > 团队里有人用 Qoder IDE、有人用 Qoder CLI、有人用 QoderWork —— 必须保证前两者开箱即用、功能完整。
201
- > QoderWork 的「连接器 / 专家套件 / 定时任务」只能是**可选增强**:有就锦上添花,没有就静默回退,绝不报错、绝不阻塞。
202
-
203
- 具体落实:
204
- - **本地数据是唯一事实源**:task.json / git log / drafts / index 永远是真相,连接器只是它的"投影/补充"
205
- - **增强段统一标记 `🧩 QoderWork 增强(可选)`**:出现在 wl-report / wl-task / wl-status 三个 skill 里,每段都明确写"无连接器则自动回退 + 不影响纯 Qoder 使用"
206
- - **探测而非假设**:只在运行环境真正提供了「日历/待办/邮件」工具可读时才启用增强;看不到就跳过,补一句 `[xx 未启用:非 QoderWork 或未授权连接器]`
207
- - 已接入的增强点:
208
- - `/wl-report` 读日历补"会议/评审"到日报(补 PM/设计岗无提交的空窗)
209
- - `/wl-task` 把任务同步为待办 + finish 后标记完成(本地仍为准)
210
- - `/wl-status` 用真实日历节点校准"按时交付""团队同步"两维健康度
211
- - 未来加任何 QoderWork 增强都必须遵循同样原则:**先保证纯 Qoder 可跑,再叠加可选层。**
212
-
213
- - THE rule file: `.qoder/rules/wl-pipeline.md` — 修改流程规则时同步更新它和本文件
214
- - Quest 修改 agents/skills 后需重启 Quest 窗口/新建对话才能生效(Qoder 已知限制)
215
- - **Repo Wiki** (`.qoder/repowiki/`, Qoder IDE 生成): 存在则作为模块级知识源,
216
- 写 PRD 前优先查相关模块页。索引管"定位", Wiki 管"理解"。
217
- ⚠️ Wiki 由宿主从代码自动生成、会被重生成覆盖, **且 .gitignore 已排除** → 不可手写、不进团队 git
218
- (团队共享知识走 `data/learning/patterns/` 和 `data/docs/`, 那里可写且进 git)。
219
- - All scripts are pure Python 3 + git, path-autodetecting (no hardcoded
220
- drive letters). Invoke as `python` (Windows) or `python3` (macOS).
221
- - Weekly update: Windows -> scripts/setup_weekly_cron.bat,
222
- macOS/Linux -> scripts/setup_weekly_cron.sh. Only ONE machine per team
223
- needs this; everyone else gets the graph via /wl-init's auto pull.
224
- - Environment problems of any kind -> run:
225
- `python .qoder/scripts/setup/init_doctor.py` (add --fix to auto-repair).
226
-
227
- ## Fast Path: one call to get all context
228
-
229
- `python .qoder/scripts/kg/context_pack.py <业务词> --platform <web|app>` returns
230
- code files + page examples + field specs + PRDs + APIs + style/icon pointers
231
- in ONE call. Use it FIRST; fall back to search_index.py for drill-down.
232
-
233
- ## Icons: NEVER emoji
234
-
235
- Prototypes use the projects' real icon systems (data/index/icon-reference.json):
236
- Web = Ant Design inline SVG, APP = Vant font icons. Emoji in a prototype = FAIL.
237
-
238
- ## Quality Gate (EVA)
239
-
240
- Before publishing any PRD: `python .qoder/scripts/report/eval_prd.py <prd> [prototype]`
241
- must PASS (>=80%). Metrics definition: data/docs/constitution/eva-metrics.md
242
-
243
- ## Workflow Usability (可用性)
244
-
245
- 回答"这套工作流对真实团队用不用得起来"——EVA 管 PRD 写得好不好, 本指标管工作流有没有人用。
246
- ```bash
247
- python .qoder/scripts/orchestration/wlkj.py usability [--days N] # 或 --json
248
- ```
249
- - 采纳(主): U1流程采纳率/U2一次过审率/U3返工轮数/U4规则违反率/U5绕过率/U6活跃度
250
- - 流程(辅): F1端到端闭环率/F2转化漏斗/F3单命令效率/F4流程断点
251
- - 每条指标标【自动/半自动/待埋点】, 算不了的明说不编造
252
- - `/wl-status health` 的健康分含「工作流采纳」维度(权重10%)
253
- - 指标定义 + 公式 + 阈值: `data/docs/constitution/workflow-usability-metrics.md`
254
- - **交互看板**: `wlkj.py usability --html` → QoderWork 嵌入对话流 / CLI 落盘 dashboard.html
255
- - **红灯自动沉淀**: 评测红/黄项自动 append 到 `data/learning/patterns/usability-patterns.md`(进 git 团队共享)
256
-
257
- ## Deterministic Hooks (确定性钩子 · 吸收宿主能力)
258
-
259
- 把"靠 AI 自觉"的规则升级为"宿主确定性执行"(不漂移、不依赖 LLM 服从性)。配置在 `.qoder/settings.json`:
260
-
261
- | Hook | 时机 | 作用 | 阻断? |
262
- |------|------|------|-------|
263
- | `SessionStart` | 开会话 | 注入项目上下文 + 可用性红灯到 `<qoder-context>` | 否 |
264
- | `UserPromptSubmit` | 用户提交 | `/wl-prd-*` 未带平台 → **exit 2 阻断**, 强制先问平台(治违规率) | 是 |
265
- | `PostToolUse` | 工具执行后 | 写完 PRD/建完任务 → **自动埋点**(治漏斗断链) | 否 |
266
- | `Stop` | Agent 说完成 | 产出 PRD 未跑 eval → **exit 2 阻断**, 强制 eval≥80%(带防递归) | 是 |
267
-
268
- > 钩子契约: `exit 0` 放行 / `exit 2` 阻断(原因写 stderr) / 失败绝不崩会话。脚本草稿在 `.qoder/hooks/`。
269
-
270
- ## Permission Model
271
-
272
- | Level | Commands | Auto |
273
- |-------|---------|------|
274
- | SAFE | /wl-init, /wl-prd, /wl-design, /wl-task, /wl-search, /wl-req, /wl-spec, /wl-status, /wl-insight, /wl-report | YES |
275
- | DANGEROUS | /wl-code, /wl-test, /wl-commit | NO |
1
+ # QODER Pipeline
2
+
3
+ ## 🏗️ 架构: 引擎与数据分离 (Trellis 化)
4
+
5
+ `.qoder/` 一分为二 (v2.8.0+):
6
+
7
+ | 类别 | 内容 | 来源 | 进团队 git? |
8
+ |------|------|------|------------|
9
+ | **引擎代码** | `.qoder/scripts/` `skills/` `commands/` `agents/` `contracts/` `hooks/` `templates/` `rules/` | `npx @hupan56/wlkj init/update` 生成 | ❌ gitignore |
10
+ | **团队数据** | `.qoder/config.yaml` `config.toml` `settings.json` `learning/` + `data/` (kg.duckdb/索引) + `workspace/` (PRD/任务) | git 跟踪, 团队共享 | ✅ |
11
+
12
+ - 引擎代码**只由 npm 包生成**, 不再进团队 git → 彻底消除"引擎更新污染团队 git"和"新机 init 产生 unrelated histories"。
13
+ - Qoder 全系列仍读 `.qoder/` (路径不变), 只是引擎来源从 git 变成 npm 生成。
14
+ - 老团队升级 (管理员跑一次): `npx @hupan56/wlkj migrate`, 然后团队成员各自 `npx @hupan56/wlkj update`。
15
+
16
+ ---
17
+
18
+ ## ⚠️ CRITICAL RULE #1: ALWAYS ASK PLATFORM FIRST
19
+
20
+ **When user asks for PRD or prototype, you MUST ask this question FIRST:**
21
+
22
+ ```
23
+ 这个需求是针对哪个平台?
24
+
25
+ 1. **Web 管理端** (fywl-ui) - Ant Design Vue + VxeGrid 风格
26
+ 2. **APP 移动端** (Carmg-H5) - Vant 风格
27
+ 3. **两端都要**
28
+
29
+ 请选择 (1/2/3):
30
+ ```
31
+
32
+ **DO NOT skip this question. DO NOT auto-detect. ALWAYS ASK.**
33
+ **Wait for user answer before doing ANYTHING else.**
34
+ **DO NOT search/read/analyze in the same turn as asking — the reply MUST end after the question.**
35
+ Asking platform + simultaneously searching code in the same response is a VIOLATION.
36
+
37
+ ---
38
+
39
+ ## CRITICAL RULE #2: Use /wl-search to find code
40
+
41
+ When user asks about code, business, API, feature:
42
+ 1. **ALWAYS** use `/wl-search <keyword>` command first
43
+ 2. Then read only the specific files returned
44
+ 3. **NEVER** grep the entire codebase directly
45
+
46
+ Examples:
47
+ - "考勤代码在哪" -> /wl-search attendance
48
+ - "保险定时任务" -> /wl-search insurance
49
+ - "薪资API" -> /wl-search --api salary
50
+
51
+ ## CRITICAL RULE #2.5: 触发方式(非侵入式 — v3.0)
52
+
53
+ 工作流**只在显式调用时启动**,不主动干预普通对话。
54
+
55
+ **两种触发方式(且仅此两种):**
56
+ 1. **斜杠命令**:`/wl-prd` `/wl-search` `/wl-req` 等 → 启动工作流
57
+ 2. **npx**:`npx @hupan56/wlkj init/update` → 安装/同步
58
+
59
+ **自然语言不触发工作流。** 用户说"写个需求""查下代码"等,AI 正常回答,不自动拉入工作流。
60
+ (工作流启动后的过程内交互 — 如选平台、确认需求 — 仍可用自然语言。)
61
+
62
+ ### 禅道自然查询(非命令,直接回答)
63
+
64
+ 用户随口问禅道("我的任务""看下#301""Bug 状态""我的需求")—— **直接用禅道 MCP 工具自然回答**,
65
+ 不拉入 `/wl-task` 工作流:
66
+ - QoderWork: `qw_mcp_call("my_workbench", {})` 一行看全(任务+需求+Bug)
67
+ - CLI/其他: `cap.mcp.call("my_workbench")`
68
+
69
+ 只有**显式工序**(`/wl-task` 建需求/记工时/改状态)才走工作流。
70
+ > 工具用真实工具名,别拿需求冒充任务回答(问任务查 task 域,查不到如实说"没有")。
71
+
72
+
73
+ 注意区分:"同步/保存产出" = team_sync(push);"提交/commit 代码" = wl-commit。
74
+
75
+ ## CRITICAL RULE #3: User NEVER touches git
76
+
77
+ PMs on this team do not know git. ALL git operations are invisible:
78
+ - After any publish/save action (/wl-prd publish, /wl-task create/finish,
79
+ /wl-report save), run: `python .qoder/scripts/task/team_sync.py push`
80
+ - To get teammates' latest work: `python .qoder/scripts/task/team_sync.py pull`
81
+ - If team_sync prints SYNC_CONFLICT, YOU (the AI) resolve the conflict
82
+ yourself (it tells you the commands). NEVER ask the user to run git.
83
+ - When the user says 同步/保存/提交 in natural language -> team_sync, not raw git.
84
+
85
+ ## Platform Filtering
86
+
87
+ After user selects platform, use it for search:
88
+
89
+ ```bash
90
+ # Search by platform
91
+ /wl-search 保险 --platform web # Only fywl-ui (PC)
92
+ /wl-search 保险 --platform app # Only Carmg-H5 (Mobile)
93
+ /wl-search 保险 # Both platforms
94
+
95
+ # Platform shortcuts
96
+ web/pc/管理端 -> fywl-ui
97
+ app/mobile/h5/移动端 -> Carmg-H5
98
+ ```
99
+
100
+ ## Commands: 7 Core (小而美) + Optional
101
+
102
+ > 认知面只暴露 7 个核心命令,每个是一道工序站,每个带模式参数。
103
+ > **一个命令当多个用**——记不住参数也没关系,自然语言一样路由。
104
+ > 模块契约(输入/输出/校验)见 `.qoder/contracts/`。
105
+
106
+ ### 7 Core Commands(记住这 7 个就够)
107
+
108
+ | Command | 工序站 | 模式参数(第一个词路由) | Auto | 背后 skill |
109
+ |---------|--------|------------------------|------|-----------|
110
+ | `/wl-init` | 身份 | (无) | YES | wl-init |
111
+ | `/wl-prd` | 需求 | 语义路由: 完整档/小改动/评审 (原 wl-prd-full/quick/review 合并) | YES | prd-generator/prd-review |
112
+ | `/wl-design` | 设计 | `import`/`generate`/`review` 或默认(import) | YES | design-import/prototype-generator/design-review |
113
+ | `/wl-task` | 任务 | `create`/`list`/`start`/`finish` | YES | wl-task |
114
+ | `/wl-code` | 编码 | (无,按 spec 实现) | NO | spec-coder |
115
+ | `/wl-test` | 测试 | `quick`/`browser`/`unit` 或默认(quick) | NO | wl-test / test-generator |
116
+ | `/wl-commit` | 提交 | (无) | NO | wl-commit |
117
+
118
+ ### Optional Commands(长大了再开,非执行链路必经)
119
+
120
+ > 物理上归在 `.qoder/commands/optional/` 子目录(Qoder IDE 会按子目录归类显示),
121
+ > QoderWork 安装脚本已支持递归扫描,4 个照样能装到用户级。
122
+
123
+ | Command | 用途 | 何时用 |
124
+ |---------|------|--------|
125
+ | `/wl-search` | 代码索引搜索 | 全员工具,按需,非工序站(物理在 `commands/` 顶层) |
126
+ | `/wl-req` | 需求 CRUD + 发布 | 需求生命周期管理(创建/编辑/发布/归档) |
127
+ | `/wl-spec` | PRD→技术规格 | 需求复杂时(小团队可直接 `/wl-code`) |
128
+ | `/wl-status` | 项目健康度/路线图 | 需要 PMO/汇报时 |
129
+ | `/wl-report` | 日报周报 | 需要向上汇报时 |
130
+ | `/wl-insight` | 需求澄清对焦(默认)/调研/规划 | 需求模糊听不懂、要做调研或规划时(位于 /wl-prd 之前)。最高频:产品听不懂用户时快速对焦(必要时出原型图帮助理解),对齐后再问要不要调研 |
131
+
132
+ > **新人只需记 7 个核心命令**,能把活从需求干到提交。可选 6 个按场景启用。
133
+
134
+ ## Knowledge Index
135
+
136
+ 3 projects: Carmg-H5(1378 files), fywl-ics(8973 files, 3903 APIs), fywl-ui(3270 files)
137
+ Search: /wl-search <keyword> returns compact results (~200 bytes)
138
+ Weekly sync: python .qoder/scripts/task/git_sync.py
139
+
140
+ ### MCP Servers (QoderWork / Qoder IDE)
141
+ 注册在 `~/.qoderwork/mcp.json`,setup.py 一键配齐:
142
+ - `qoder-knowledge-graph` (stdio) — 本仓库知识图谱,17 个工具 (search_code/api, get_impact, context_360, fill_prototype, get_design_system...)。自动拉起。
143
+ - `playwright` (stdio) — 浏览器自动化,快且稳。自动拉起。
144
+ - `qoder-mysql` (stdio) — 只读查真实数据库结构/数据。自动拉起。
145
+ - `lanhu` (STDIO) — 蓝湖设计稿直读,设计师发链接 AI 提取颜色/尺寸/字体/切图。
146
+ **STDIO 模式:开 QoderWork 自动起、关自动停**(QoderWork 原生进程管理,无需手动 start)。
147
+ cookie 按角色隔离(UI 能改稿/PM 只读):wrapper 读当前 `.developer` → 该角色的 `workspace/members/{dev}/.secrets/lanhu.env`(gitignored)。换角色改 `.developer` + 配该角色 cookie → 重启 QoderWork。首次配置跑 `python .qoder/scripts/setup/setup_lanhu.py`。
148
+
149
+ ### Style Index (Prototype Reference)
150
+ UI patterns from Vue files: Tables(718), Forms(301), Detail(129), Dashboard(90)
151
+ Top components: Space(583), Modal(270), Select(172), Input(134)
152
+ Form components: Input(2262), Select(1049), ApiSelect(725), DatePicker(265)
153
+ Field map: 2383 entries
154
+
155
+ Style search:
156
+ python .qoder/scripts/kg/search_index.py --style table/form/modal/dashboard
157
+ python .qoder/scripts/kg/search_index.py --field <field_name>
158
+ python .qoder/scripts/kg/search_index.py --components
159
+ python .qoder/scripts/kg/search_index.py --vben # Vben Admin CSS variables
160
+
161
+ Prototype templates:
162
+ .qoder/templates/prototype-web.html # Web管理端 (Vben Admin + Ant Design)
163
+ .qoder/templates/prototype-app.html # APP移动端 (Vant)
164
+
165
+ Prototype output: workspace/members/{dev}/drafts/prototype-{feature}.html
166
+ Style priority: Code > PDF > Default
167
+
168
+ ## Workflow: 契约网(非强制流水线)
169
+
170
+ 模块间**不强制顺序**,靠标准契约文件对接(详见 `.qoder/contracts/`)。
171
+ 完整链路**可以**这么走,但任何模块都能独立开机:
172
+
173
+ ```
174
+ /wl-init → /wl-prd (ASK PLATFORM!) → /wl-design → /wl-task → /wl-code → /wl-test → /wl-commit
175
+ 身份 需求 设计 任务 编码 测试 提交
176
+ ```
177
+
178
+ **独立性**:每个模块认文件不认流程。缺上游 → 手动喂个契约文件,或用自然语言 +
179
+ `context_pack.py` 让 AI 自检索补全(见 `.qoder/contracts/fallback.md`)。
180
+
181
+ ## Qoder 全系列兼容(IDE / Quest / CLI / QoderWork)
182
+
183
+ This pipeline works on Windows + macOS, across the **whole Qoder family**.
184
+ Each product loads rules from a different carrier — keep them in sync:
185
+
186
+ | 产品/模式 | 规则载体 | 命令载体 |
187
+ |-----------|---------|---------|
188
+ | Qoder IDE 默认编辑器 | .qoder/rules/wl-pipeline.md (always_on) | .qoder/commands/wl-*.md |
189
+ | Qoder Quest 模式 | .qoder/rules/ + .qoder/skills/ + .qoder/agents/ | 自然语言 -> skills |
190
+ | Qoder CLI (qodercli) | 本文件 AGENTS.md | 自然语言 -> scripts |
191
+ | QoderWork 桌面 Agent | 安装 .qoder/skills/ 下的技能 | 自然语言 -> skills |
192
+
193
+ > 本流水线聚焦 Qoder 全系列,4 种载体功能等价。知识图谱 17 个能力(影响分析/
194
+ > 覆盖矩阵/功能画像/业务流程/多跳遍历…)通过统一 CLI `python .qoder/scripts/kg/kg.py`
195
+ > 全系列触达;QoderWork 环境也可直接调 MCP 工具。
196
+
197
+ ## QoderWork Integration: Optional Enhancement Only (Progressive Enhancement)
198
+
199
+ > 🔒 **核心原则:本流水线绝不强依赖 QoderWork。**
200
+ > 团队里有人用 Qoder IDE、有人用 Qoder CLI、有人用 QoderWork —— 必须保证前两者开箱即用、功能完整。
201
+ > QoderWork 的「连接器 / 专家套件 / 定时任务」只能是**可选增强**:有就锦上添花,没有就静默回退,绝不报错、绝不阻塞。
202
+
203
+ 具体落实:
204
+ - **本地数据是唯一事实源**:task.json / git log / drafts / index 永远是真相,连接器只是它的"投影/补充"
205
+ - **增强段统一标记 `🧩 QoderWork 增强(可选)`**:出现在 wl-report / wl-task / wl-status 三个 skill 里,每段都明确写"无连接器则自动回退 + 不影响纯 Qoder 使用"
206
+ - **探测而非假设**:只在运行环境真正提供了「日历/待办/邮件」工具可读时才启用增强;看不到就跳过,补一句 `[xx 未启用:非 QoderWork 或未授权连接器]`
207
+ - 已接入的增强点:
208
+ - `/wl-report` 读日历补"会议/评审"到日报(补 PM/设计岗无提交的空窗)
209
+ - `/wl-task` 把任务同步为待办 + finish 后标记完成(本地仍为准)
210
+ - `/wl-status` 用真实日历节点校准"按时交付""团队同步"两维健康度
211
+ - 未来加任何 QoderWork 增强都必须遵循同样原则:**先保证纯 Qoder 可跑,再叠加可选层。**
212
+
213
+ - THE rule file: `.qoder/rules/wl-pipeline.md` — 修改流程规则时同步更新它和本文件
214
+ - Quest 修改 agents/skills 后需重启 Quest 窗口/新建对话才能生效(Qoder 已知限制)
215
+ - **Repo Wiki** (`.qoder/repowiki/`, Qoder IDE 生成): 存在则作为模块级知识源,
216
+ 写 PRD 前优先查相关模块页。索引管"定位", Wiki 管"理解"。
217
+ ⚠️ Wiki 由宿主从代码自动生成、会被重生成覆盖, **且 .gitignore 已排除** → 不可手写、不进团队 git
218
+ (团队共享知识走 `data/learning/patterns/` 和 `data/docs/`, 那里可写且进 git)。
219
+ - All scripts are pure Python 3 + git, path-autodetecting (no hardcoded
220
+ drive letters). Invoke as `python` (Windows) or `python3` (macOS).
221
+ - Weekly update: Windows -> scripts/setup_weekly_cron.bat,
222
+ macOS/Linux -> scripts/setup_weekly_cron.sh. Only ONE machine per team
223
+ needs this; everyone else gets the graph via /wl-init's auto pull.
224
+ - Environment problems of any kind -> run:
225
+ `python .qoder/scripts/setup/init_doctor.py` (add --fix to auto-repair).
226
+
227
+ ## Fast Path: one call to get all context
228
+
229
+ `python .qoder/scripts/kg/context_pack.py <业务词> --platform <web|app>` returns
230
+ code files + page examples + field specs + PRDs + APIs + style/icon pointers
231
+ in ONE call. Use it FIRST; fall back to search_index.py for drill-down.
232
+
233
+ ## Icons: NEVER emoji
234
+
235
+ Prototypes use the projects' real icon systems (data/index/icon-reference.json):
236
+ Web = Ant Design inline SVG, APP = Vant font icons. Emoji in a prototype = FAIL.
237
+
238
+ ## Quality Gate (EVA)
239
+
240
+ Before publishing any PRD: `python .qoder/scripts/report/eval_prd.py <prd> [prototype]`
241
+ must PASS (>=80%). Metrics definition: data/docs/constitution/eva-metrics.md
242
+
243
+ ## Workflow Usability (可用性)
244
+
245
+ 回答"这套工作流对真实团队用不用得起来"——EVA 管 PRD 写得好不好, 本指标管工作流有没有人用。
246
+ ```bash
247
+ python .qoder/scripts/orchestration/wlkj.py usability [--days N] # 或 --json
248
+ ```
249
+ - 采纳(主): U1流程采纳率/U2一次过审率/U3返工轮数/U4规则违反率/U5绕过率/U6活跃度
250
+ - 流程(辅): F1端到端闭环率/F2转化漏斗/F3单命令效率/F4流程断点
251
+ - 每条指标标【自动/半自动/待埋点】, 算不了的明说不编造
252
+ - `/wl-status health` 的健康分含「工作流采纳」维度(权重10%)
253
+ - 指标定义 + 公式 + 阈值: `data/docs/constitution/workflow-usability-metrics.md`
254
+ - **交互看板**: `wlkj.py usability --html` → QoderWork 嵌入对话流 / CLI 落盘 dashboard.html
255
+ - **红灯自动沉淀**: 评测红/黄项自动 append 到 `data/learning/patterns/usability-patterns.md`(进 git 团队共享)
256
+
257
+ ## Deterministic Hooks (确定性钩子 · 吸收宿主能力)
258
+
259
+ 把"靠 AI 自觉"的规则升级为"宿主确定性执行"(不漂移、不依赖 LLM 服从性)。配置在 `.qoder/settings.json`:
260
+
261
+ | Hook | 时机 | 作用 | 阻断? |
262
+ |------|------|------|-------|
263
+ | `SessionStart` | 开会话 | 注入项目上下文 + 可用性红灯到 `<qoder-context>` | 否 |
264
+ | `UserPromptSubmit` | 用户提交 | `/wl-prd-*` 未带平台 → **exit 2 阻断**, 强制先问平台(治违规率) | 是 |
265
+ | `PostToolUse` | 工具执行后 | 写完 PRD/建完任务 → **自动埋点**(治漏斗断链) | 否 |
266
+ | `Stop` | Agent 说完成 | 产出 PRD 未跑 eval → **exit 2 阻断**, 强制 eval≥80%(带防递归) | 是 |
267
+
268
+ > 钩子契约: `exit 0` 放行 / `exit 2` 阻断(原因写 stderr) / 失败绝不崩会话。脚本草稿在 `.qoder/hooks/`。
269
+
270
+ ## Permission Model
271
+
272
+ | Level | Commands | Auto |
273
+ |-------|---------|------|
274
+ | SAFE | /wl-init, /wl-prd, /wl-design, /wl-task, /wl-search, /wl-req, /wl-spec, /wl-status, /wl-insight, /wl-report | YES |
275
+ | DANGEROUS | /wl-code, /wl-test, /wl-commit | NO |