@hupan56/wlkj 2.7.11 → 3.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 (152) hide show
  1. package/bin/cli.js +375 -78
  2. package/package.json +29 -29
  3. package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +23 -0
  4. package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +23 -0
  5. package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +1 -0
  6. package/templates/.qoder/learning/eval-history.jsonl +28 -0
  7. package/templates/data/index/wiki-index.json +8 -0
  8. package/templates/qoder/agents/insight-planning.md +1 -1
  9. package/templates/qoder/agents/insight-research.md +28 -15
  10. package/templates/qoder/commands/optional/wl-insight.md +159 -161
  11. package/templates/qoder/commands/optional/wl-report.md +4 -4
  12. package/templates/qoder/commands/optional/wl-status.md +2 -2
  13. package/templates/qoder/commands/wl-code.md +2 -2
  14. package/templates/qoder/commands/wl-design.md +2 -2
  15. package/templates/qoder/commands/wl-init.md +3 -3
  16. package/templates/qoder/commands/wl-prd.md +2 -2
  17. package/templates/qoder/commands/wl-req.md +43 -0
  18. package/templates/qoder/commands/wl-search.md +8 -8
  19. package/templates/qoder/commands/wl-task.md +17 -17
  20. package/templates/qoder/commands/wl-test.md +41 -15
  21. package/templates/qoder/config.yaml +17 -1
  22. package/templates/qoder/hooks/session-start.py +12 -22
  23. package/templates/qoder/nul +4 -0
  24. package/templates/qoder/rules/wl-pipeline.md +22 -48
  25. package/templates/qoder/scripts/README.md +139 -0
  26. package/templates/qoder/scripts/common/autotest_auth.py +109 -0
  27. package/templates/qoder/scripts/common/bootstrap.py +145 -0
  28. package/templates/qoder/scripts/common/check_publish.py +98 -0
  29. package/templates/qoder/scripts/common/cmd_registry.py +112 -0
  30. package/templates/qoder/scripts/common/config.py +187 -0
  31. package/templates/qoder/scripts/common/contract.py +317 -0
  32. package/templates/qoder/scripts/common/developer.py +2 -1
  33. package/templates/qoder/scripts/common/feishu.py +10 -9
  34. package/templates/qoder/scripts/common/guard.py +159 -0
  35. package/templates/qoder/scripts/common/identity.py +121 -2
  36. package/templates/qoder/scripts/common/kg_capabilities.py +182 -0
  37. package/templates/qoder/scripts/common/mcp_base.py +268 -0
  38. package/templates/qoder/scripts/common/paths.py +187 -1
  39. package/templates/qoder/scripts/common/result.py +223 -0
  40. package/templates/qoder/scripts/common/roles.py +60 -0
  41. package/templates/qoder/scripts/common/task_utils.py +21 -9
  42. package/templates/qoder/scripts/common/test_extract.py +115 -0
  43. package/templates/qoder/scripts/kg/__init__.py +11 -0
  44. package/templates/qoder/scripts/kg/build_entity_registry.py +196 -0
  45. package/templates/qoder/scripts/kg/build_relations.py +127 -0
  46. package/templates/qoder/scripts/{build_style_index.py → kg/build_style_index.py} +39 -10
  47. package/templates/qoder/scripts/kg/build_workflows.py +144 -0
  48. package/templates/qoder/scripts/{context_pack.py → kg/context_pack.py} +18 -11
  49. package/templates/qoder/scripts/{enrich_prompt.py → kg/enrich_prompt.py} +232 -226
  50. package/templates/qoder/scripts/{extract_api_params.py → kg/extract.py} +398 -246
  51. package/templates/qoder/scripts/{kg.py → kg/kg.py} +638 -708
  52. package/templates/qoder/scripts/{kg_build.py → kg/kg_build.py} +618 -612
  53. package/templates/qoder/scripts/{kg_build_db.py → kg/kg_build_db.py} +333 -327
  54. package/templates/qoder/scripts/{kg_duckdb.py → kg/kg_duckdb.py} +38 -37
  55. package/templates/qoder/scripts/{kg_incremental.py → kg/kg_incremental.py} +420 -393
  56. package/templates/qoder/scripts/{kg_link_db.py → kg/kg_link_db.py} +230 -224
  57. package/templates/qoder/scripts/{kg_semantic.py → kg/kg_semantic.py} +156 -150
  58. package/templates/qoder/scripts/kg/prefetch.py +359 -0
  59. package/templates/qoder/scripts/{search_index.py → kg/search_index.py} +70 -14
  60. package/templates/qoder/scripts/mcp/__init__.py +11 -0
  61. package/templates/qoder/scripts/{kg_mcp_server.py → mcp/kg_mcp_server.py} +77 -272
  62. package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → mcp/lanhu_stdio_wrapper.py} +125 -119
  63. package/templates/qoder/scripts/{check_mcp.py → mcp/mcp_doctor.py} +515 -298
  64. package/templates/qoder/scripts/{mcp_launcher.py → mcp/mcp_launcher.py} +442 -414
  65. package/templates/qoder/scripts/{mysql_mcp_server.py → mcp/mysql_mcp_server.py} +347 -396
  66. package/templates/qoder/scripts/{zentao_mcp_server.py → mcp/zentao_mcp_server.py} +384 -424
  67. package/templates/qoder/scripts/report/__init__.py +11 -0
  68. package/templates/qoder/scripts/{add_session.py → report/add_session.py} +250 -244
  69. package/templates/qoder/scripts/{archive_prd.py → report/archive_prd.py} +383 -377
  70. package/templates/qoder/scripts/{eval_prd.py → report/eval_prd.py} +73 -11
  71. package/templates/qoder/scripts/{export.py → report/export.py} +63 -0
  72. package/templates/qoder/scripts/{fill_prototype.py → report/fill_prototype.py} +6 -0
  73. package/templates/qoder/scripts/{gen_design_doc.py → report/gen_design_doc.py} +400 -394
  74. package/templates/qoder/scripts/{learn.py → report/learn.py} +152 -146
  75. package/templates/qoder/scripts/{learn_aggregate.py → report/learn_aggregate.py} +207 -201
  76. package/templates/qoder/scripts/{report.py → report/report.py} +287 -281
  77. package/templates/qoder/scripts/report/req.py +222 -0
  78. package/templates/qoder/scripts/report/role.py +33 -0
  79. package/templates/qoder/scripts/{status.py → report/status.py} +634 -628
  80. package/templates/qoder/scripts/setup/__init__.py +11 -0
  81. package/templates/qoder/scripts/setup/carriers.py +662 -0
  82. package/templates/qoder/scripts/{init_doctor.py → setup/init_doctor.py} +63 -26
  83. package/templates/qoder/scripts/{install_qoderwork.py → setup/install_qoderwork.py} +36 -26
  84. package/templates/qoder/scripts/{platform_doctor.py → setup/platform_doctor.py} +265 -259
  85. package/templates/qoder/scripts/{repo_root.py → setup/repo_root.py} +112 -106
  86. package/templates/qoder/scripts/{setup.py → setup/setup.py} +147 -0
  87. package/templates/qoder/scripts/{setup_lanhu.py → setup/setup_lanhu.py} +973 -963
  88. package/templates/qoder/scripts/task/__init__.py +11 -0
  89. package/templates/qoder/scripts/{git_sync.py → task/git_sync.py} +52 -31
  90. package/templates/qoder/scripts/{syncgate.py → task/syncgate.py} +6 -0
  91. package/templates/qoder/scripts/task/task.py +221 -0
  92. package/templates/qoder/scripts/task/task_lifecycle.py +596 -0
  93. package/templates/qoder/scripts/task/task_query.py +161 -0
  94. package/templates/qoder/scripts/task/task_relations.py +424 -0
  95. package/templates/qoder/scripts/{team_sync.py → task/team_sync.py} +93 -20
  96. package/templates/qoder/scripts/test/__init__.py +11 -0
  97. package/templates/qoder/scripts/{autotest.py → test/autotest.py} +1174 -1751
  98. package/templates/qoder/scripts/{autotest_batch.py → test/autotest_batch.py} +242 -224
  99. package/templates/qoder/scripts/test/autotest_data.py +675 -0
  100. package/templates/qoder/scripts/{autotest_run.py → test/autotest_run.py} +309 -297
  101. package/templates/qoder/scripts/{benchmark.py → test/benchmark.py} +6 -0
  102. package/templates/qoder/scripts/{kg_auto_login.py → test/kg_auto_login.py} +202 -196
  103. package/templates/qoder/scripts/{kg_test_runner.py → test/kg_test_runner.py} +7 -1
  104. package/templates/qoder/scripts/{page_probe.py → test/page_probe.py} +465 -459
  105. package/templates/qoder/scripts/wlkj.py +116 -0
  106. package/templates/qoder/settings.json +1 -10
  107. package/templates/qoder/skills/design-import/SKILL.md +226 -226
  108. package/templates/qoder/skills/design-review/SKILL.md +82 -82
  109. package/templates/qoder/skills/prd-generator/SKILL.md +26 -16
  110. package/templates/qoder/skills/prd-review/SKILL.md +5 -5
  111. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -256
  112. package/templates/qoder/skills/spec-coder/SKILL.md +4 -4
  113. package/templates/qoder/skills/spec-generator/SKILL.md +4 -4
  114. package/templates/qoder/skills/test-generator/SKILL.md +5 -5
  115. package/templates/qoder/skills/wl-code/SKILL.md +4 -4
  116. package/templates/qoder/skills/wl-commit/SKILL.md +4 -4
  117. package/templates/qoder/skills/wl-design/SKILL.md +3 -3
  118. package/templates/qoder/skills/wl-init/SKILL.md +8 -8
  119. package/templates/qoder/skills/wl-insight/SKILL.md +5 -5
  120. package/templates/qoder/skills/wl-prd-full/SKILL.md +6 -6
  121. package/templates/qoder/skills/wl-prd-quick/SKILL.md +6 -6
  122. package/templates/qoder/skills/wl-prd-review/SKILL.md +4 -4
  123. package/templates/qoder/skills/wl-report/SKILL.md +7 -7
  124. package/templates/qoder/skills/wl-search/SKILL.md +13 -13
  125. package/templates/qoder/skills/wl-spec/SKILL.md +5 -5
  126. package/templates/qoder/skills/wl-status/SKILL.md +5 -5
  127. package/templates/qoder/skills/wl-task/SKILL.md +6 -6
  128. package/templates/qoder/skills/wl-test/SKILL.md +102 -39
  129. package/templates/root/AGENTS.md +39 -40
  130. package/templates/qoder/hooks/inject-workflow-state.py +0 -169
  131. package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
  132. package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
  133. package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
  134. package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
  135. package/templates/qoder/scripts/check_carriers.py +0 -238
  136. package/templates/qoder/scripts/check_mcp_launch.py +0 -183
  137. package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
  138. package/templates/qoder/scripts/collect_prds.py +0 -31
  139. package/templates/qoder/scripts/common/mentions.py +0 -134
  140. package/templates/qoder/scripts/common/utf8.py +0 -38
  141. package/templates/qoder/scripts/extract_routes.py +0 -54
  142. package/templates/qoder/scripts/extract_routes_tree.py +0 -78
  143. package/templates/qoder/scripts/handoff.py +0 -22
  144. package/templates/qoder/scripts/init_developer.py +0 -76
  145. package/templates/qoder/scripts/parse_prds.py +0 -33
  146. package/templates/qoder/scripts/role.py +0 -51
  147. package/templates/qoder/scripts/sync_carriers.py +0 -259
  148. package/templates/qoder/scripts/task.py +0 -1261
  149. package/templates/qoder/scripts/workspace_init.py +0 -102
  150. package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
  151. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  152. /package/templates/qoder/scripts/{secure-ls.js → test/secure-ls.js} +0 -0
@@ -1,134 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- mentions.py - @提及解析与飞书通知 (阶段 D4)
4
-
5
- 从 PRD 评审/任务评论/报告内容里提取 @用户名, 映射到飞书 user_key,
6
- 通过飞书机器人推送提及通知。
7
-
8
- 触发点:
9
- - PRD 评审意见里有 @某人
10
- - 任务评论 (task.py comment, 未来) 有 @某人
11
- - 报告里 @某人
12
-
13
- member.json 扩展字段: feishu_user_key (可选, 飞书 open_id/user_id)
14
- """
15
-
16
- import os
17
- import re
18
- import sys
19
- from typing import List, Tuple, Optional
20
-
21
- _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
22
- if _THIS_DIR not in sys.path:
23
- sys.path.insert(0, _THIS_DIR)
24
-
25
- try:
26
- from .identity import get_member, list_members
27
- from .feishu import send_card, is_enabled
28
- except ImportError:
29
- from identity import get_member, list_members
30
- from feishu import send_card, is_enabled
31
-
32
- __all__ = [
33
- "extract_mentions",
34
- "resolve_to_feishu_keys",
35
- "notify_mentions",
36
- ]
37
-
38
- # @提及模式: @用户名 (支持中文/英文/数字/下划线, 2-32 字符)
39
- MENTION_RE = re.compile(r'@([\w\u4e00-\u9fa5\-]{2,32})')
40
-
41
-
42
- def extract_mentions(text: str, exclude: Optional[str] = None) -> List[str]:
43
- """从文本提取 @提及的用户名。
44
-
45
- Args:
46
- text: 要扫描的文本。
47
- exclude: 排除的用户名 (通常是作者自己, 避免自提及)。
48
-
49
- Returns:
50
- 去重后的用户名列表 (只保留在注册表里的成员)。
51
- """
52
- raw = set(m.group(1) for m in MENTION_RE.finditer(text))
53
- if exclude:
54
- raw.discard(exclude)
55
- # 只保留已注册的成员 (避免 @system @all 等噪音)
56
- members = list_members()
57
- return sorted(name for name in raw if name in members)
58
-
59
-
60
- def resolve_to_feishu_keys(names: List[str]) -> List[Tuple[str, str]]:
61
- """把用户名映射到飞书 user_key。
62
-
63
- Args:
64
- names: 用户名列表。
65
-
66
- Returns:
67
- [(name, feishu_user_key), ...] 只含配了 feishu_user_key 的成员。
68
- """
69
- out = []
70
- for name in names:
71
- m = get_member(name)
72
- if m and m.get('feishu_user_key'):
73
- out.append((name, m['feishu_user_key']))
74
- return out
75
-
76
-
77
- def notify_mentions(
78
- text: str,
79
- context: str,
80
- author: Optional[str] = None,
81
- link: Optional[str] = None,
82
- ) -> int:
83
- """扫描文本里的 @提及, 给每个被提及者发飞书通知。
84
-
85
- Args:
86
- text: 含 @提及的文本 (如 PRD 评审意见)。
87
- context: 上下文描述 (如 "REQ-2026-007 评审")。
88
- author: 提及的发起人 (从 .developer 读)。
89
- link: 相关链接 (可选)。
90
-
91
- Returns:
92
- 成功通知的人数。
93
- """
94
- if not is_enabled():
95
- return 0
96
-
97
- names = extract_mentions(text, exclude=author)
98
- if not names:
99
- return 0
100
-
101
- # 解析到飞书 user_key
102
- keyed = resolve_to_feishu_keys(names)
103
- # 未配 feishu_user_key 的成员, 仍然在通知内容里显示名字 (但不定向)
104
- unkeyed = [n for n in names if n not in dict(keyed)]
105
-
106
- sent = 0
107
- for name, user_key in keyed:
108
- lines = [
109
- f'**你被 @{author or "某人"} 提及了**',
110
- f'上下文: {context}',
111
- ]
112
- if link:
113
- lines.append(f'链接: {link}')
114
- lines.append(f'飞书 ID: {user_key}')
115
- try:
116
- if send_card(f'@提及: {context}', lines, event_type=None, theme='orange'):
117
- sent += 1
118
- except Exception:
119
- pass
120
-
121
- # 未配 key 的合并发一条到群 (作为 fallback)
122
- if unkeyed and not keyed:
123
- lines = [
124
- f'**{", ".join(unkeyed)} 被 @{author or "某人"} 提及**',
125
- f'(以上成员未配置 feishu_user_key, 此为群广播)',
126
- f'上下文: {context}',
127
- ]
128
- try:
129
- send_card(f'@提及: {context}', lines, event_type=None, theme='grey')
130
- sent += 1
131
- except Exception:
132
- pass
133
-
134
- return sent
@@ -1,38 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- utf8.py - 统一的 stdout/stderr UTF-8 配置 (防御性)
4
-
5
- 解决审计 C2: 10 个脚本的裸 sys.stdout.reconfigure(encoding='utf-8') 在
6
- stdout 被捕获时 (Qoder IDE 内嵌终端/管道) 会 AttributeError 崩溃。
7
-
8
- 用法 (替换裸 reconfigure):
9
- from common.utf8 import ensure_utf8_stdio
10
- ensure_utf8_stdio()
11
- """
12
-
13
- import sys
14
-
15
-
16
- def ensure_utf8_stdio() -> None:
17
- """把 stdout/stderr 切到 UTF-8, 失败则忽略 (不崩溃)。
18
-
19
- 安全性:
20
- - TextIOWrapper 有 reconfigure -> 切换
21
- - 其他类型 (管道包装/None) -> 静默跳过
22
- - 永远不抛异常
23
- """
24
- for stream_name in ("stdout", "stderr"):
25
- stream = getattr(sys, stream_name, None)
26
- if stream is None:
27
- continue
28
- reconfigure = getattr(stream, "reconfigure", None)
29
- if reconfigure is None:
30
- continue # 不是 TextIOWrapper, 跳过
31
- try:
32
- reconfigure(encoding="utf-8", errors="replace")
33
- except (TypeError, ValueError, OSError, IOError):
34
- # errors 参数不支持或底层不可重配 -> 尝试仅 encoding
35
- try:
36
- reconfigure(encoding="utf-8")
37
- except Exception:
38
- pass # 彻底失败也不阻塞脚本主逻辑
@@ -1,54 +0,0 @@
1
- import sys, os, re, glob, json
2
- sys.stdout.reconfigure(encoding='utf-8')
3
-
4
- # 扫前端路由文件, 提取 path -> component 映射
5
- router_dir = 'E:/YFHJ/new/AiProject/qoderAll/data/code/fywl-ui/apps/ics/src/router'
6
- routes = []
7
-
8
- for rf in glob.glob(os.path.join(router_dir, '**', '*.ts'), recursive=True):
9
- try:
10
- txt = open(rf, encoding='utf-8', errors='ignore').read()
11
- except:
12
- continue
13
- # 匹配 path: 'xxx' 和 component: () => import('xxx')
14
- # 或 path: 'xxx', name: 'yyy'
15
- for m in re.finditer(
16
- r"path:\s*['\"`]([^'\"`]+)['\"`]"
17
- r"[^}]*?(?:name:\s*['\"]([^'\"]+)['\"])?"
18
- r"[^}]*?(?:component:\s*\([^)]*\)\s*=>\s*import\(['\"]([^'\"]+)['\"]\))?",
19
- txt, re.S):
20
- path, name, component = m.group(1), m.group(2) or '', m.group(3) or ''
21
- if path and not path.startswith(':') and len(path) > 1:
22
- routes.append({'path': path, 'name': name, 'component': component})
23
-
24
- # 也扫后台菜单路由(通常在数据库 sys_menu 表, 但前端可能有硬编码)
25
- # 搜 views 目录下的 Vue 文件路径作为页面标识
26
- vue_views = glob.glob('E:/YFHJ/new/AiProject/qoderAll/data/code/fywl-ui/apps/ics/src/views/**/*.vue', recursive=True)
27
- view_paths = [v.replace('\\', '/').split('/views/')[-1].replace('.vue', '') for v in vue_views]
28
-
29
- print('路由定义: %d 条' % len(routes))
30
- print('Vue 页面: %d 个' % len(view_paths))
31
-
32
- # 保存路由索引
33
- output = {
34
- 'version': '1.0',
35
- 'total_routes': len(routes),
36
- 'total_views': len(view_paths),
37
- 'routes': routes,
38
- 'views': sorted(view_paths),
39
- }
40
- out_path = 'E:/YFHJ/new/AiProject/qoderAll/data/index/frontend-routes.json'
41
- with open(out_path, 'w', encoding='utf-8') as f:
42
- json.dump(output, f, ensure_ascii=False, indent=2)
43
- print('已保存: %s' % out_path)
44
-
45
- # 样本
46
- print('\n样本路由:')
47
- for r in routes[:8]:
48
- print(' %s -> %s' % (r['path'], r['component'][:50] if r['component'] else r['name']))
49
-
50
- # 保险相关
51
- print('\n保险相关路由:')
52
- for r in routes:
53
- if 'insur' in r['path'].lower() or '保险' in r.get('name', ''):
54
- print(' %s' % r['path'])
@@ -1,78 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """extract_routes_tree.py — 从 getRouters API 拉完整菜单树, 递归拼接准确 path, 存成 menu-routes-tree.json.
3
- 解决 menu-routes.json 扁平丢失父子关系、search_index 用 component 猜 URL 导致 404 的问题。
4
- 用法: python extract_routes_tree.py
5
- """
6
- import json, os, sys, requests
7
-
8
- SCRIPTS = os.path.dirname(os.path.abspath(__file__))
9
- PROJECT = os.path.normpath(os.path.join(SCRIPTS, '..', '..'))
10
-
11
-
12
- def read_developer():
13
- f = os.path.join(PROJECT, '.qoder', '.developer')
14
- for ln in open(f, encoding='utf-8'):
15
- if ln.strip().startswith('name='):
16
- return ln.strip().split('=', 1)[1].strip()
17
- raise RuntimeError('无 .developer, 先 /wl-init')
18
-
19
-
20
- dev = read_developer()
21
- auth_file = os.path.join(PROJECT, 'workspace', 'members', dev, 'auth-state.json')
22
- if not os.path.exists(auth_file):
23
- print('无 auth-state, 先登录: python .qoder/scripts/kg_auto_login.py'); sys.exit(1)
24
- auth = json.load(open(auth_file, encoding='utf-8'))
25
- token = auth['access_token']
26
- base = auth['base_url']; prefix = auth.get('api_prefix', '/test-api')
27
-
28
- print('拉取 getRouters...')
29
- r = requests.get(base + prefix + '/system/menu/getRouters',
30
- headers={'Authorization': 'Bearer ' + token, 'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e'},
31
- timeout=20)
32
- d = r.json()
33
- if d.get('code') != 200:
34
- print('getRouters 失败:', d.get('msg')); sys.exit(1)
35
- tree = d.get('data') or []
36
-
37
-
38
- def walk(nodes, parent_path=''):
39
- """递归拼接准确 path, 返回 [{full_path, name, component}].
40
- name 优先用 meta.title(中文), 回退 name(i18n key)."""
41
- out = []
42
- for n in nodes:
43
- seg = n.get('path', '').lstrip('/')
44
- meta = n.get('meta') or {}
45
- name = meta.get('title', '') or n.get('name', '')
46
- comp = n.get('component', '')
47
- full = (parent_path.rstrip('/') + '/' + seg) if parent_path else '/' + seg
48
- while '//' in full:
49
- full = full.replace('//', '/')
50
- kids = n.get('children') or []
51
- if kids:
52
- out.extend(walk(kids, full))
53
- elif comp and comp != '#' and ':' not in full:
54
- out.append({'path': full, 'name': name, 'component': comp, 'i18n_key': n.get('name', '')})
55
- return out
56
-
57
-
58
- pages = walk(tree)
59
- # 去重
60
- seen = set(); uniq = []
61
- for p in pages:
62
- if p['path'] not in seen:
63
- seen.add(p['path']); uniq.append(p)
64
-
65
- out_file = os.path.join(PROJECT, 'data', 'index', 'menu-routes-tree.json')
66
- json.dump({
67
- 'source': 'getRouters API (父子树递归拼接, 准确 path)',
68
- 'total': len(uniq),
69
- 'routes': uniq,
70
- }, open(out_file, 'w', encoding='utf-8'), ensure_ascii=False, indent=2)
71
-
72
- print('已存 %d 条准确路由 -> %s' % (len(uniq), out_file))
73
- print()
74
- # 验证关键页面
75
- for kw in ['abnormalRecords', 'insurance', 'ticket']:
76
- hits = [p for p in uniq if kw.lower() in (p['path'] + p['name']).lower()]
77
- for h in hits[:2]:
78
- print(' %-12s -> %s' % (h['name'][:10], h['path']))
@@ -1,22 +0,0 @@
1
- # handoff.py - Handoff management
2
- import os, json
3
- from datetime import datetime
4
- BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
5
-
6
- def create_handoff(task_id, from_role, to_role, artifacts, checklist):
7
- td = os.path.join(BASE, 'workspace', 'tasks', task_id, 'handoff')
8
- os.makedirs(td, exist_ok=True)
9
- fname = from_role + '-to-' + to_role + '.json'
10
- data = {
11
- 'task_id': task_id,
12
- 'from_role': from_role,
13
- 'to_role': to_role,
14
- 'artifacts': artifacts,
15
- 'checklist': [{'item': c, 'status': 'pending'} for c in checklist],
16
- 'status': 'pending',
17
- 'created_at': datetime.now().isoformat()
18
- }
19
- with open(os.path.join(td, fname), 'w') as f:
20
- json.dump(data, f, indent=2, ensure_ascii=False)
21
- print('OK: handoff ' + from_role + ' -> ' + to_role)
22
- return data
@@ -1,76 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- QODER Pipeline - 开发者初始化
5
-
6
- Usage:
7
- python init_developer.py <developer-name>
8
-
9
- 功能:
10
- - 创建 .qoder/.developer 文件 (gitignored, 包含开发者身份信息)
11
- - 创建 .qoder/workspace/<name>/ 目录结构
12
- - 创建初始日志文件 journal-1.md
13
- - 创建工作空间索引 index.md
14
-
15
- 参考: Trellis 的 init_developer.py 设计
16
-
17
- 示例:
18
- python .qoder/scripts/init_developer.py zhangsan
19
- python .qoder/scripts/init_developer.py john
20
- """
21
-
22
- from __future__ import annotations
23
-
24
- import sys
25
- import os
26
-
27
- # 将 scripts 目录加入路径
28
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
29
-
30
- from common.paths import (
31
- DIR_WORKFLOW,
32
- FILE_DEVELOPER,
33
- get_developer,
34
- get_repo_root,
35
- )
36
- from common.developer import init_developer, show_developer_info
37
-
38
-
39
- def main() -> None:
40
- """CLI 入口。"""
41
- if len(sys.argv) < 2:
42
- print(f"Usage: {sys.argv[0]} <developer-name> [role]")
43
- print()
44
- print("Example:")
45
- print(f" {sys.argv[0]} zhangsan")
46
- print(f" {sys.argv[0]} zhangsan pm")
47
- sys.exit(1)
48
-
49
- name = sys.argv[1]
50
- role = sys.argv[2] if len(sys.argv) > 2 else None
51
-
52
- # 检查是否已初始化
53
- existing = get_developer()
54
- if existing == name:
55
- print(f"Developer already initialized: {existing}")
56
- print()
57
- print("Current developer info:")
58
- show_developer_info()
59
- sys.exit(0)
60
- elif existing:
61
- print(f"Switching developer: {existing} -> {name}")
62
-
63
- # 初始化 (幂等, 也用于切换开发者)
64
- if init_developer(name, role):
65
- print()
66
- print("Next steps:")
67
- print(f" 1. Your workspace is at: workspace/members/{name}/")
68
- print(f" 2. Start creating tasks: python .qoder/scripts/task.py create \"My Task\"")
69
- print(f" 3. Check status: python .qoder/scripts/task.py list")
70
- sys.exit(0)
71
- else:
72
- sys.exit(1)
73
-
74
-
75
- if __name__ == "__main__":
76
- main()
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Parse PRD files and build PRD index (data/index/prd-index.json).
5
-
6
- THIN WRAPPER: the actual parsing logic lives in git_sync.py (single
7
- source of truth - previously this file kept its own drifted copy of
8
- the parser and term maps). Kept for backwards compatibility.
9
-
10
- Usage:
11
- python parse_prds.py # rebuild prd-index.json + prd-code-map.json
12
- """
13
-
14
- import os
15
- import sys
16
-
17
- # UTF-8 stdio (防御性: stdout 被捕获时不崩溃)
18
- try:
19
- sys.stdout.reconfigure(encoding='utf-8', errors='replace')
20
- except (AttributeError, TypeError, OSError, IOError):
21
- try:
22
- sys.stdout.reconfigure(encoding='utf-8')
23
- except Exception:
24
- pass
25
-
26
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
27
-
28
- from git_sync import build_prd_index, build_prd_code_mapping, FAILURES
29
-
30
- if __name__ == '__main__':
31
- build_prd_index()
32
- build_prd_code_mapping()
33
- sys.exit(1 if FAILURES else 0)
@@ -1,51 +0,0 @@
1
- # role.py - Resolve role from member info
2
- import os, json, sys
3
-
4
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
5
- from common.paths import get_developer, get_developer_info
6
-
7
- BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8
-
9
- def get_role(name=None):
10
- if not name:
11
- # Prefer role recorded directly in .developer
12
- info = get_developer_info()
13
- if info and info.get('role'):
14
- return info['role']
15
- name = get_developer()
16
- if not name:
17
- return None
18
- mf = os.path.join(BASE, 'workspace', 'members', name, 'member.json')
19
- if os.path.isfile(mf):
20
- try:
21
- with open(mf, encoding='utf-8') as f:
22
- return json.load(f).get('role')
23
- except (OSError, json.JSONDecodeError):
24
- return None
25
- return None
26
-
27
- def get_permissions(role):
28
- import yaml
29
- cfg = os.path.join(BASE, '.qoder', 'config.yaml')
30
- if not os.path.isfile(cfg):
31
- return []
32
- try:
33
- with open(cfg, encoding='utf-8') as f:
34
- config = yaml.safe_load(f) or {}
35
- except (OSError, yaml.YAMLError):
36
- return []
37
- roles = config.get('roles', {})
38
- r = roles.get(role, {})
39
- return r.get('permissions', [])
40
-
41
-
42
- if __name__ == '__main__':
43
- # CLI: 输出当前角色名 (供 skill 脚本动态读角色用)
44
- # 用法: ROLE=$(python role.py)
45
- try:
46
- sys.stdout.reconfigure(encoding='utf-8')
47
- except (AttributeError, IOError):
48
- pass
49
- r = get_role()
50
- print(r or 'pm') # 无角色默认 pm (软引导, 不报错)
51
-