@hupan56/wlkj 2.5.0 → 2.7.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 +289 -12
- package/package.json +1 -1
- package/templates/qoder/agents/insight-planning.md +67 -0
- package/templates/qoder/agents/insight-research.md +61 -0
- package/templates/qoder/agents/prd-quick.md +1 -0
- package/templates/qoder/agents/prd-reference.md +10 -2
- package/templates/qoder/commands/optional/wl-insight.md +275 -0
- package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
- package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
- package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
- package/templates/qoder/commands/wl-code.md +10 -2
- package/templates/qoder/commands/wl-commit.md +1 -1
- package/templates/qoder/commands/wl-design-draw.md +78 -0
- package/templates/qoder/commands/wl-design-scan.md +108 -0
- package/templates/qoder/commands/wl-design-spec.md +154 -0
- package/templates/qoder/commands/wl-design.md +32 -0
- package/templates/qoder/commands/wl-init.md +24 -3
- package/templates/qoder/commands/wl-prd-full.md +226 -0
- package/templates/qoder/commands/wl-prd-quick.md +134 -0
- package/templates/qoder/commands/wl-prd-review.md +104 -0
- package/templates/qoder/commands/wl-prd.md +17 -288
- package/templates/qoder/commands/wl-search.md +66 -30
- package/templates/qoder/commands/wl-task.md +290 -59
- package/templates/qoder/commands/wl-test.md +92 -24
- package/templates/qoder/config.yaml +59 -15
- package/templates/qoder/hooks/inject-workflow-state.py +35 -9
- package/templates/qoder/hooks/session-start.py +144 -62
- package/templates/qoder/rules/wl-pipeline.md +216 -105
- package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/archive_prd.py +377 -0
- package/templates/qoder/scripts/autotest.py +1715 -0
- package/templates/qoder/scripts/autotest_batch.py +224 -0
- package/templates/qoder/scripts/autotest_run.py +297 -0
- package/templates/qoder/scripts/benchmark.py +210 -209
- package/templates/qoder/scripts/build_style_index.py +444 -4
- package/templates/qoder/scripts/check_carriers.py +238 -0
- package/templates/qoder/scripts/check_mcp.py +298 -0
- package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
- package/templates/qoder/scripts/common/developer.py +26 -19
- package/templates/qoder/scripts/common/events.py +46 -0
- package/templates/qoder/scripts/common/extract.py +419 -0
- package/templates/qoder/scripts/common/graph_traverse.py +533 -0
- package/templates/qoder/scripts/common/identity.py +6 -1
- package/templates/qoder/scripts/common/paths.py +89 -0
- package/templates/qoder/scripts/common/pip_install.py +144 -0
- package/templates/qoder/scripts/common/platform_guard.py +61 -0
- package/templates/qoder/scripts/common/search_engine.py +205 -205
- package/templates/qoder/scripts/common/terms.py +57 -0
- package/templates/qoder/scripts/common/ts_extract.py +536 -0
- package/templates/qoder/scripts/context_pack.py +73 -13
- package/templates/qoder/scripts/enrich_prompt.py +226 -0
- package/templates/qoder/scripts/eval_prd.py +318 -225
- package/templates/qoder/scripts/export.py +487 -487
- package/templates/qoder/scripts/extract_api_params.py +246 -0
- package/templates/qoder/scripts/extract_routes.py +54 -0
- package/templates/qoder/scripts/extract_routes_tree.py +78 -0
- package/templates/qoder/scripts/fill_prototype.py +707 -0
- package/templates/qoder/scripts/gen_design_doc.py +394 -0
- package/templates/qoder/scripts/git_sync.py +27 -15
- package/templates/qoder/scripts/init_doctor.py +306 -41
- package/templates/qoder/scripts/install_qoderwork.py +366 -9
- package/templates/qoder/scripts/kg.py +708 -0
- package/templates/qoder/scripts/kg_auto_login.py +196 -0
- package/templates/qoder/scripts/kg_build.py +612 -0
- package/templates/qoder/scripts/kg_build_db.py +327 -0
- package/templates/qoder/scripts/kg_duckdb.py +549 -0
- package/templates/qoder/scripts/kg_incremental.py +393 -0
- package/templates/qoder/scripts/kg_link_db.py +224 -0
- package/templates/qoder/scripts/kg_mcp_server.py +801 -0
- package/templates/qoder/scripts/kg_semantic.py +150 -0
- package/templates/qoder/scripts/kg_test_runner.py +241 -0
- package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
- package/templates/qoder/scripts/learn.py +118 -39
- package/templates/qoder/scripts/learn_aggregate.py +201 -0
- package/templates/qoder/scripts/mcp_launcher.py +359 -0
- package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
- package/templates/qoder/scripts/repo_root.py +106 -0
- package/templates/qoder/scripts/role.py +12 -0
- package/templates/qoder/scripts/run_weekly_update.bat +5 -0
- package/templates/qoder/scripts/run_weekly_update.sh +5 -0
- package/templates/qoder/scripts/search_index.py +307 -60
- package/templates/qoder/scripts/secure-ls.js +5640 -0
- package/templates/qoder/scripts/setup.py +706 -453
- package/templates/qoder/scripts/setup_lanhu.py +963 -0
- package/templates/qoder/scripts/status.py +250 -11
- package/templates/qoder/scripts/sync_carriers.py +259 -0
- package/templates/qoder/scripts/syncgate.py +5 -4
- package/templates/qoder/scripts/task.py +75 -0
- package/templates/qoder/scripts/team_sync.py +60 -4
- package/templates/qoder/scripts/workspace_init.py +1 -1
- package/templates/qoder/skills/design-import/SKILL.md +226 -0
- package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
- package/templates/qoder/skills/design-review/SKILL.md +82 -25
- package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
- package/templates/qoder/skills/prd-review/SKILL.md +18 -1
- package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
- package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
- package/templates/qoder/skills/test-generator/SKILL.md +15 -2
- package/templates/qoder/skills/wl-code/SKILL.md +55 -36
- package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
- package/templates/qoder/skills/wl-design/SKILL.md +55 -0
- package/templates/qoder/skills/wl-init/SKILL.md +76 -67
- package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
- package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
- package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
- package/templates/qoder/skills/wl-report/SKILL.md +131 -107
- package/templates/qoder/skills/wl-search/SKILL.md +141 -75
- package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
- package/templates/qoder/skills/wl-status/SKILL.md +83 -61
- package/templates/qoder/skills/wl-task/SKILL.md +132 -58
- package/templates/qoder/skills/wl-test/SKILL.md +406 -40
- package/templates/qoder/templates/prd-full-template.md +2 -0
- package/templates/qoder/templates/prd-quick-template.md +1 -0
- package/templates/qoder/templates/prototype-app.html +13 -8
- package/templates/qoder/templates/prototype-web.html +376 -93
- package/templates/root/AGENTS.md +89 -34
- package/templates/root/requirements.txt +21 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
- package/templates/qoder/agents/prd-planning.md +0 -56
- package/templates/qoder/agents/prd-research.md +0 -33
- package/templates/qoder/commands/wl-insight.md +0 -51
- package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
|
@@ -15,6 +15,31 @@ BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)
|
|
|
15
15
|
_CACHE = {}
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def _append_hook_error(log_path, line):
|
|
19
|
+
"""写一行错误到 hook-errors.log, 超 100KB 截断保留最后 50KB (防无限膨胀)。
|
|
20
|
+
|
|
21
|
+
best-effort: 任何 IO 失败都静默 (不能让日志写入失败导致 hook 二次崩溃)。
|
|
22
|
+
"""
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
try:
|
|
25
|
+
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
26
|
+
# 轮转: 文件过大则截断
|
|
27
|
+
if os.path.isfile(log_path):
|
|
28
|
+
try:
|
|
29
|
+
if os.path.getsize(log_path) > 100 * 1024:
|
|
30
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
31
|
+
tail = f.read()[-50 * 1024:]
|
|
32
|
+
with open(log_path, 'w', encoding='utf-8') as f:
|
|
33
|
+
f.write(tail)
|
|
34
|
+
except OSError:
|
|
35
|
+
pass
|
|
36
|
+
with open(log_path, 'a', encoding='utf-8') as f:
|
|
37
|
+
f.write('[{}] inject-workflow-state: {}\n'.format(
|
|
38
|
+
datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(line)[:200]))
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
18
43
|
def _read_developer():
|
|
19
44
|
"""读当前开发者名 (容错: UTF-8/GBK)."""
|
|
20
45
|
df = os.path.join(BASE, '.qoder', '.developer')
|
|
@@ -88,8 +113,8 @@ def get_status():
|
|
|
88
113
|
|
|
89
114
|
# Statuses written by task.py: planning -> in_progress -> completed
|
|
90
115
|
GUIDE = {
|
|
91
|
-
None: 'No active task. Use /wl-prd to start, or /wl-task create.',
|
|
92
|
-
'planning': 'Task in planning. Use /wl-prd to write the PRD, then /wl-task start.',
|
|
116
|
+
None: 'No active task. Use /wl-prd-full to start, or /wl-task create.',
|
|
117
|
+
'planning': 'Task in planning. Use /wl-prd-full to write the PRD, then /wl-task start.',
|
|
93
118
|
'in_progress': 'Dev in progress. /wl-spec to generate spec, /wl-code to implement.',
|
|
94
119
|
'completed': 'Task completed. /wl-task archive to archive, or start the next one.',
|
|
95
120
|
}
|
|
@@ -106,7 +131,10 @@ def main():
|
|
|
106
131
|
if os.path.isfile(state_file):
|
|
107
132
|
last_sig = open(state_file, 'r', encoding='utf-8').read().strip()
|
|
108
133
|
if last_sig == current_sig:
|
|
109
|
-
|
|
134
|
+
# 状态没变, 不重复注入 (省 token), 但仍输出健康标记
|
|
135
|
+
# QoderWork 看不到任何输出时分不清"缓存跳过"还是"hook 没跑"
|
|
136
|
+
print('<!-- [hook-ok] inject-workflow-state: state unchanged (cached) -->')
|
|
137
|
+
return
|
|
110
138
|
open(state_file, 'w', encoding='utf-8').write(current_sig)
|
|
111
139
|
except OSError:
|
|
112
140
|
pass
|
|
@@ -119,6 +147,8 @@ def main():
|
|
|
119
147
|
parts.append('Status: ' + str(st))
|
|
120
148
|
parts.append('Do: ' + g)
|
|
121
149
|
parts.append('</qoder-workflow>')
|
|
150
|
+
# Hook-1: 健康标记 — 与 session-start 一致, QoderWork 看不到说明 hook 没生效
|
|
151
|
+
parts.append('<!-- [hook-ok] inject-workflow-state hook ran -->')
|
|
122
152
|
print(NL.join(parts))
|
|
123
153
|
|
|
124
154
|
|
|
@@ -129,12 +159,8 @@ if __name__ == '__main__':
|
|
|
129
159
|
# 落盘到日志 (stdout 一次性, 追溯用)
|
|
130
160
|
try:
|
|
131
161
|
from pathlib import Path
|
|
132
|
-
|
|
133
|
-
log_path
|
|
134
|
-
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
135
|
-
with open(log_path, 'a', encoding='utf-8') as f:
|
|
136
|
-
f.write('[{}] inject-workflow-state: {}\n'.format(
|
|
137
|
-
datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(e)[:200]))
|
|
162
|
+
log_path = str(Path(__file__).resolve().parents[1] / '.runtime' / 'hook-errors.log')
|
|
163
|
+
_append_hook_error(log_path, str(e)[:200])
|
|
138
164
|
except Exception:
|
|
139
165
|
pass
|
|
140
166
|
print('<qoder-workflow>')
|
|
@@ -10,10 +10,62 @@ if sys.platform == 'win32':
|
|
|
10
10
|
NL = chr(10)
|
|
11
11
|
BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
12
12
|
|
|
13
|
+
HOOK_ERRORS_LOG = os.path.join(BASE, '.qoder', '.runtime', 'hook-errors.log')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _append_hook_error(log_path, line):
|
|
17
|
+
"""写一行错误到 hook-errors.log, 超 100KB 截断保留最后 50KB (防无限膨胀)。best-effort。"""
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
try:
|
|
20
|
+
os.makedirs(os.path.dirname(log_path), exist_ok=True)
|
|
21
|
+
if os.path.isfile(log_path):
|
|
22
|
+
try:
|
|
23
|
+
if os.path.getsize(log_path) > 100 * 1024:
|
|
24
|
+
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
25
|
+
tail = f.read()[-50 * 1024:]
|
|
26
|
+
with open(log_path, 'w', encoding='utf-8') as f:
|
|
27
|
+
f.write(tail)
|
|
28
|
+
except OSError:
|
|
29
|
+
pass
|
|
30
|
+
with open(log_path, 'a', encoding='utf-8') as f:
|
|
31
|
+
f.write('[{}] session-start: {}\n'.format(
|
|
32
|
+
datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(line)[:200]))
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _recent_hook_errors(hours=24, limit=3):
|
|
38
|
+
"""读最近 N 小时的 hook 错误, 返回最多 limit 条 (供会话注入告警)。
|
|
39
|
+
|
|
40
|
+
错误之前只落盘到 .runtime/hook-errors.log, PM 永远看不到 → hook 坏了无感知。
|
|
41
|
+
改后每次开会话把最近错误摘要注入上下文。
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
from datetime import datetime, timedelta
|
|
45
|
+
if not os.path.isfile(HOOK_ERRORS_LOG):
|
|
46
|
+
return []
|
|
47
|
+
cutoff = datetime.now() - timedelta(hours=hours)
|
|
48
|
+
recent = []
|
|
49
|
+
with open(HOOK_ERRORS_LOG, encoding='utf-8', errors='replace') as f:
|
|
50
|
+
for line in f:
|
|
51
|
+
line = line.strip()
|
|
52
|
+
if not line or '[' not in line or ']' not in line:
|
|
53
|
+
continue
|
|
54
|
+
try:
|
|
55
|
+
ts = line.split(']')[0].lstrip('[')
|
|
56
|
+
dt = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S')
|
|
57
|
+
if dt >= cutoff:
|
|
58
|
+
recent.append(line)
|
|
59
|
+
except (ValueError, IndexError):
|
|
60
|
+
continue
|
|
61
|
+
return recent[-limit:] if recent else []
|
|
62
|
+
except Exception:
|
|
63
|
+
return []
|
|
64
|
+
|
|
13
65
|
# Injected as REAL OUTPUT below (a comment here would never reach the AI)
|
|
14
66
|
CRITICAL_RULE = NL.join([
|
|
15
67
|
'## CRITICAL RULE',
|
|
16
|
-
'When user asks for /wl-prd, ALWAYS ask platform FIRST:',
|
|
68
|
+
'When user asks for /wl-prd-full, ALWAYS ask platform FIRST:',
|
|
17
69
|
'"这个需求是针对哪个平台?',
|
|
18
70
|
' 1. Web 管理端 (fywl-ui) - Ant Design Vue + VxeGrid 风格',
|
|
19
71
|
' 2. APP 移动端 (Carmg-H5) - Vant 风格',
|
|
@@ -90,63 +142,94 @@ def get_index_info():
|
|
|
90
142
|
except OSError:
|
|
91
143
|
pass
|
|
92
144
|
|
|
93
|
-
lines.append('## Knowledge
|
|
94
|
-
lines.append('
|
|
145
|
+
lines.append('## Knowledge Graph')
|
|
146
|
+
lines.append('MCP工具 (QoderWork自动调用): search_code / get_impact / coverage_matrix / get_workflow 等11个')
|
|
95
147
|
lines.append(' python .qoder/scripts/search_index.py <keyword> [--platform web|app]')
|
|
96
|
-
lines.append(' python .qoder/scripts/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
lines.append(' python .qoder/scripts/context_pack.py <keyword> # 一次打包全部上下文')
|
|
149
|
+
|
|
150
|
+
# DuckDB 知识图谱状态检查
|
|
151
|
+
# Read role from .developer file
|
|
152
|
+
role = 'pm'
|
|
153
|
+
try:
|
|
154
|
+
dev_file = os.path.join(BASE, '.qoder', '.developer')
|
|
155
|
+
if os.path.isfile(dev_file):
|
|
156
|
+
for _line in open(dev_file, encoding='utf-8'):
|
|
157
|
+
if _line.strip().startswith('role='):
|
|
158
|
+
role = _line.split('=', 1)[1].strip()
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
db_path = os.path.join(BASE, 'data', 'index', 'kg.duckdb')
|
|
162
|
+
|
|
163
|
+
if os.path.isfile(db_path):
|
|
164
|
+
import time as _time
|
|
165
|
+
db_mtime = os.path.getmtime(db_path)
|
|
166
|
+
db_age_hours = (_time.time() - db_mtime) / 3600
|
|
167
|
+
|
|
168
|
+
# 检查代码是否比 DuckDB 新 (采样几个文件)
|
|
169
|
+
code_dir = os.path.join(BASE, 'data', 'code')
|
|
170
|
+
code_newer = False
|
|
171
|
+
if os.path.isdir(code_dir):
|
|
172
|
+
for root, dirs, files in os.walk(code_dir):
|
|
173
|
+
dirs[:] = [d for d in dirs if d not in ('node_modules', '.git', 'dist', '__pycache__')]
|
|
174
|
+
depth = root[len(code_dir):].count(os.sep)
|
|
175
|
+
if depth >= 3:
|
|
176
|
+
dirs[:] = []
|
|
177
|
+
continue
|
|
178
|
+
for fn in files:
|
|
179
|
+
if fn.endswith(('.vue', '.java', '.ts')):
|
|
180
|
+
try:
|
|
181
|
+
if os.path.getmtime(os.path.join(root, fn)) > db_mtime:
|
|
182
|
+
code_newer = True
|
|
183
|
+
break
|
|
184
|
+
except OSError:
|
|
185
|
+
pass
|
|
186
|
+
if code_newer:
|
|
187
|
+
break
|
|
188
|
+
|
|
189
|
+
lines.append(' kg.duckdb: %.0f小时前构建' % db_age_hours)
|
|
190
|
+
|
|
191
|
+
if role in ('admin', 'dev'):
|
|
192
|
+
# 管理员/开发: 能构建
|
|
193
|
+
if code_newer or db_age_hours > 168: # 7天 = 168小时
|
|
194
|
+
lines.append(' [WARN] 知识图谱过期! 代码有新变更.')
|
|
195
|
+
lines.append(' → 管理员请运行: python .qoder/scripts/kg_build.py')
|
|
196
|
+
lines.append(' → 完成后: python .qoder/scripts/team_sync.py push (共享给团队)')
|
|
197
|
+
else:
|
|
198
|
+
lines.append(' ✓ 知识图谱最新')
|
|
199
|
+
else:
|
|
200
|
+
# 非管理员: 只拉取
|
|
201
|
+
if code_newer or db_age_hours > 168:
|
|
202
|
+
lines.append(' [WARN] 知识图谱可能过期.')
|
|
203
|
+
lines.append(' → 运行: python .qoder/scripts/team_sync.py pull (获取管理员最新构建)')
|
|
204
|
+
else:
|
|
205
|
+
lines.append(' ✓ 知识图谱最新')
|
|
206
|
+
|
|
207
|
+
# 检查是否有未标注的模块 (提示AI做语义标注)
|
|
208
|
+
try:
|
|
209
|
+
import subprocess as _sp
|
|
210
|
+
r = _sp.run([sys.executable, os.path.join(BASE, '.qoder', 'scripts', 'kg_semantic.py'), '--check'],
|
|
211
|
+
capture_output=True, text=True, encoding='utf-8', timeout=5)
|
|
212
|
+
if r.stdout and 'semantic_labeling_needed' in r.stdout:
|
|
213
|
+
import json as _json
|
|
214
|
+
data = _json.loads(r.stdout)
|
|
215
|
+
unlabeled = len(data.get('modules', []))
|
|
216
|
+
if unlabeled > 0:
|
|
217
|
+
lines.append(' [提示] %d个模块未标注中文名, 运行 kg_semantic.py --check 查看' % unlabeled)
|
|
218
|
+
except Exception:
|
|
219
|
+
pass
|
|
220
|
+
else:
|
|
221
|
+
lines.append(' [MISSING] kg.duckdb 不存在!')
|
|
222
|
+
if role in ('admin', 'dev'):
|
|
223
|
+
lines.append(' → 运行: python .qoder/scripts/kg_build.py')
|
|
224
|
+
else:
|
|
225
|
+
lines.append(' → 运行: python .qoder/scripts/team_sync.py pull')
|
|
143
226
|
|
|
144
227
|
return NL.join(lines)
|
|
145
228
|
|
|
146
229
|
|
|
147
230
|
def get_style_info():
|
|
148
231
|
"""Read style metadata for quick reference"""
|
|
149
|
-
meta = load_json_safe(os.path.join(BASE, 'data', 'index', 'style-meta.json'))
|
|
232
|
+
meta = load_json_safe(os.path.join(BASE, 'data', 'index', 'ui-style-meta.json'))
|
|
150
233
|
if not meta:
|
|
151
234
|
return None
|
|
152
235
|
|
|
@@ -174,6 +257,14 @@ def get_style_info():
|
|
|
174
257
|
def main():
|
|
175
258
|
parts = []
|
|
176
259
|
parts.append('<qoder-context>')
|
|
260
|
+
# Hook-3: 上次会话以来的 hook 错误告警 (不只落盘, 要让用户/AI 看到)
|
|
261
|
+
# hook 坏了会导致上下文注入失败 (任务状态/平台规则缺失), 必须可见
|
|
262
|
+
recent_errors = _recent_hook_errors(hours=24, limit=3)
|
|
263
|
+
if recent_errors:
|
|
264
|
+
parts.append('⚠️ [HOOK-WARN] 过去 24h 有 hook 错误 (上下文注入可能失效):')
|
|
265
|
+
for err in recent_errors:
|
|
266
|
+
parts.append(' ' + err)
|
|
267
|
+
parts.append(' 详情见 .qoder/.runtime/hook-errors.log; 检查 Python 环境或跑 init_doctor --fix')
|
|
177
268
|
dev = get_developer()
|
|
178
269
|
if dev:
|
|
179
270
|
name = dev.get('name', 'unknown')
|
|
@@ -216,17 +307,8 @@ if __name__ == '__main__':
|
|
|
216
307
|
main()
|
|
217
308
|
except Exception as e:
|
|
218
309
|
# A broken hook must never kill the session - emit minimal context
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
from pathlib import Path
|
|
222
|
-
log_path = Path(__file__).resolve().parents[1] / '.runtime' / 'hook-errors.log'
|
|
223
|
-
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
224
|
-
from datetime import datetime
|
|
225
|
-
with open(log_path, 'a', encoding='utf-8') as f:
|
|
226
|
-
f.write('[{}] session-start: {}\n'.format(
|
|
227
|
-
datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(e)[:200]))
|
|
228
|
-
except Exception:
|
|
229
|
-
pass # 日志写失败不影响降级
|
|
310
|
+
# 同时落盘到日志 (带轮转), 方便下次会话告警
|
|
311
|
+
_append_hook_error(HOOK_ERRORS_LOG, str(e)[:200])
|
|
230
312
|
print('<qoder-context>')
|
|
231
313
|
print('session-start hook error: ' + str(e)[:200])
|
|
232
314
|
print(' (详情见 .qoder/.runtime/hook-errors.log)')
|