@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.
- package/bin/cli.js +375 -78
- package/package.json +29 -29
- package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +23 -0
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +23 -0
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +1 -0
- package/templates/.qoder/learning/eval-history.jsonl +28 -0
- package/templates/data/index/wiki-index.json +8 -0
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +159 -161
- package/templates/qoder/commands/optional/wl-report.md +4 -4
- package/templates/qoder/commands/optional/wl-status.md +2 -2
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +2 -2
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +2 -2
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +8 -8
- package/templates/qoder/commands/wl-task.md +17 -17
- package/templates/qoder/commands/wl-test.md +41 -15
- package/templates/qoder/config.yaml +17 -1
- package/templates/qoder/hooks/session-start.py +12 -22
- package/templates/qoder/nul +4 -0
- package/templates/qoder/rules/wl-pipeline.md +22 -48
- package/templates/qoder/scripts/README.md +139 -0
- package/templates/qoder/scripts/common/autotest_auth.py +109 -0
- package/templates/qoder/scripts/common/bootstrap.py +145 -0
- package/templates/qoder/scripts/common/check_publish.py +98 -0
- package/templates/qoder/scripts/common/cmd_registry.py +112 -0
- package/templates/qoder/scripts/common/config.py +187 -0
- package/templates/qoder/scripts/common/contract.py +317 -0
- package/templates/qoder/scripts/common/developer.py +2 -1
- package/templates/qoder/scripts/common/feishu.py +10 -9
- package/templates/qoder/scripts/common/guard.py +159 -0
- package/templates/qoder/scripts/common/identity.py +121 -2
- package/templates/qoder/scripts/common/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/common/mcp_base.py +268 -0
- package/templates/qoder/scripts/common/paths.py +187 -1
- package/templates/qoder/scripts/common/result.py +223 -0
- package/templates/qoder/scripts/common/roles.py +60 -0
- package/templates/qoder/scripts/common/task_utils.py +21 -9
- package/templates/qoder/scripts/common/test_extract.py +115 -0
- package/templates/qoder/scripts/kg/__init__.py +11 -0
- package/templates/qoder/scripts/kg/build_entity_registry.py +196 -0
- package/templates/qoder/scripts/kg/build_relations.py +127 -0
- package/templates/qoder/scripts/{build_style_index.py → kg/build_style_index.py} +39 -10
- package/templates/qoder/scripts/kg/build_workflows.py +144 -0
- package/templates/qoder/scripts/{context_pack.py → kg/context_pack.py} +18 -11
- package/templates/qoder/scripts/{enrich_prompt.py → kg/enrich_prompt.py} +232 -226
- package/templates/qoder/scripts/{extract_api_params.py → kg/extract.py} +398 -246
- package/templates/qoder/scripts/{kg.py → kg/kg.py} +638 -708
- package/templates/qoder/scripts/{kg_build.py → kg/kg_build.py} +618 -612
- package/templates/qoder/scripts/{kg_build_db.py → kg/kg_build_db.py} +333 -327
- package/templates/qoder/scripts/{kg_duckdb.py → kg/kg_duckdb.py} +38 -37
- package/templates/qoder/scripts/{kg_incremental.py → kg/kg_incremental.py} +420 -393
- package/templates/qoder/scripts/{kg_link_db.py → kg/kg_link_db.py} +230 -224
- package/templates/qoder/scripts/{kg_semantic.py → kg/kg_semantic.py} +156 -150
- package/templates/qoder/scripts/kg/prefetch.py +359 -0
- package/templates/qoder/scripts/{search_index.py → kg/search_index.py} +70 -14
- package/templates/qoder/scripts/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/{kg_mcp_server.py → mcp/kg_mcp_server.py} +77 -272
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → mcp/lanhu_stdio_wrapper.py} +125 -119
- package/templates/qoder/scripts/{check_mcp.py → mcp/mcp_doctor.py} +515 -298
- package/templates/qoder/scripts/{mcp_launcher.py → mcp/mcp_launcher.py} +442 -414
- package/templates/qoder/scripts/{mysql_mcp_server.py → mcp/mysql_mcp_server.py} +347 -396
- package/templates/qoder/scripts/{zentao_mcp_server.py → mcp/zentao_mcp_server.py} +384 -424
- package/templates/qoder/scripts/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → report/add_session.py} +250 -244
- package/templates/qoder/scripts/{archive_prd.py → report/archive_prd.py} +383 -377
- package/templates/qoder/scripts/{eval_prd.py → report/eval_prd.py} +73 -11
- package/templates/qoder/scripts/{export.py → report/export.py} +63 -0
- package/templates/qoder/scripts/{fill_prototype.py → report/fill_prototype.py} +6 -0
- package/templates/qoder/scripts/{gen_design_doc.py → report/gen_design_doc.py} +400 -394
- package/templates/qoder/scripts/{learn.py → report/learn.py} +152 -146
- package/templates/qoder/scripts/{learn_aggregate.py → report/learn_aggregate.py} +207 -201
- package/templates/qoder/scripts/{report.py → report/report.py} +287 -281
- package/templates/qoder/scripts/report/req.py +222 -0
- package/templates/qoder/scripts/report/role.py +33 -0
- package/templates/qoder/scripts/{status.py → report/status.py} +634 -628
- package/templates/qoder/scripts/setup/__init__.py +11 -0
- package/templates/qoder/scripts/setup/carriers.py +662 -0
- package/templates/qoder/scripts/{init_doctor.py → setup/init_doctor.py} +63 -26
- package/templates/qoder/scripts/{install_qoderwork.py → setup/install_qoderwork.py} +36 -26
- package/templates/qoder/scripts/{platform_doctor.py → setup/platform_doctor.py} +265 -259
- package/templates/qoder/scripts/{repo_root.py → setup/repo_root.py} +112 -106
- package/templates/qoder/scripts/{setup.py → setup/setup.py} +147 -0
- package/templates/qoder/scripts/{setup_lanhu.py → setup/setup_lanhu.py} +973 -963
- package/templates/qoder/scripts/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → task/git_sync.py} +52 -31
- package/templates/qoder/scripts/{syncgate.py → task/syncgate.py} +6 -0
- package/templates/qoder/scripts/task/task.py +221 -0
- package/templates/qoder/scripts/task/task_lifecycle.py +596 -0
- package/templates/qoder/scripts/task/task_query.py +161 -0
- package/templates/qoder/scripts/task/task_relations.py +424 -0
- package/templates/qoder/scripts/{team_sync.py → task/team_sync.py} +93 -20
- package/templates/qoder/scripts/test/__init__.py +11 -0
- package/templates/qoder/scripts/{autotest.py → test/autotest.py} +1174 -1751
- package/templates/qoder/scripts/{autotest_batch.py → test/autotest_batch.py} +242 -224
- package/templates/qoder/scripts/test/autotest_data.py +675 -0
- package/templates/qoder/scripts/{autotest_run.py → test/autotest_run.py} +309 -297
- package/templates/qoder/scripts/{benchmark.py → test/benchmark.py} +6 -0
- package/templates/qoder/scripts/{kg_auto_login.py → test/kg_auto_login.py} +202 -196
- package/templates/qoder/scripts/{kg_test_runner.py → test/kg_test_runner.py} +7 -1
- package/templates/qoder/scripts/{page_probe.py → test/page_probe.py} +465 -459
- package/templates/qoder/scripts/wlkj.py +116 -0
- package/templates/qoder/settings.json +1 -10
- package/templates/qoder/skills/design-import/SKILL.md +226 -226
- package/templates/qoder/skills/design-review/SKILL.md +82 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +26 -16
- package/templates/qoder/skills/prd-review/SKILL.md +5 -5
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -256
- package/templates/qoder/skills/spec-coder/SKILL.md +4 -4
- package/templates/qoder/skills/spec-generator/SKILL.md +4 -4
- package/templates/qoder/skills/test-generator/SKILL.md +5 -5
- package/templates/qoder/skills/wl-code/SKILL.md +4 -4
- package/templates/qoder/skills/wl-commit/SKILL.md +4 -4
- package/templates/qoder/skills/wl-design/SKILL.md +3 -3
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +5 -5
- package/templates/qoder/skills/wl-prd-full/SKILL.md +6 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +6 -6
- package/templates/qoder/skills/wl-prd-review/SKILL.md +4 -4
- package/templates/qoder/skills/wl-report/SKILL.md +7 -7
- package/templates/qoder/skills/wl-search/SKILL.md +13 -13
- package/templates/qoder/skills/wl-spec/SKILL.md +5 -5
- package/templates/qoder/skills/wl-status/SKILL.md +5 -5
- package/templates/qoder/skills/wl-task/SKILL.md +6 -6
- package/templates/qoder/skills/wl-test/SKILL.md +102 -39
- package/templates/root/AGENTS.md +39 -40
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- /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
|
-
|