@hupan56/wlkj 2.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 +213 -0
- package/package.json +11 -0
- package/templates/cli.js +198 -0
- package/templates/qoder/commands/wl-code.md +43 -0
- package/templates/qoder/commands/wl-commit.md +30 -0
- package/templates/qoder/commands/wl-init.md +80 -0
- package/templates/qoder/commands/wl-insight.md +51 -0
- package/templates/qoder/commands/wl-prd.md +199 -0
- package/templates/qoder/commands/wl-report.md +166 -0
- package/templates/qoder/commands/wl-search.md +52 -0
- package/templates/qoder/commands/wl-spec.md +18 -0
- package/templates/qoder/commands/wl-status.md +51 -0
- package/templates/qoder/commands/wl-task.md +71 -0
- package/templates/qoder/commands/wl-test.md +42 -0
- package/templates/qoder/config.toml +5 -0
- package/templates/qoder/config.yaml +141 -0
- package/templates/qoder/hooks/inject-workflow-state.py +117 -0
- package/templates/qoder/hooks/session-start.py +204 -0
- package/templates/qoder/rules/wl-pipeline.md +105 -0
- package/templates/qoder/scripts/add_session.py +245 -0
- package/templates/qoder/scripts/benchmark.py +209 -0
- package/templates/qoder/scripts/build_style_index.py +268 -0
- package/templates/qoder/scripts/code_index.py +41 -0
- package/templates/qoder/scripts/collect_prds.py +31 -0
- package/templates/qoder/scripts/common/__init__.py +0 -0
- package/templates/qoder/scripts/common/active_task.py +230 -0
- package/templates/qoder/scripts/common/atomicio.py +172 -0
- package/templates/qoder/scripts/common/developer.py +161 -0
- package/templates/qoder/scripts/common/eval_api.py +144 -0
- package/templates/qoder/scripts/common/feishu.py +278 -0
- package/templates/qoder/scripts/common/filelock.py +211 -0
- package/templates/qoder/scripts/common/identity.py +285 -0
- package/templates/qoder/scripts/common/mentions.py +134 -0
- package/templates/qoder/scripts/common/paths.py +311 -0
- package/templates/qoder/scripts/common/reqid.py +218 -0
- package/templates/qoder/scripts/common/search_engine.py +205 -0
- package/templates/qoder/scripts/common/task_utils.py +342 -0
- package/templates/qoder/scripts/common/terms.py +234 -0
- package/templates/qoder/scripts/common/utf8.py +38 -0
- package/templates/qoder/scripts/context_pack.py +196 -0
- package/templates/qoder/scripts/eval_prd.py +225 -0
- package/templates/qoder/scripts/export.py +487 -0
- package/templates/qoder/scripts/git_sync.py +1087 -0
- package/templates/qoder/scripts/handoff.py +22 -0
- package/templates/qoder/scripts/init_developer.py +76 -0
- package/templates/qoder/scripts/init_doctor.py +527 -0
- package/templates/qoder/scripts/install_qoderwork.py +339 -0
- package/templates/qoder/scripts/learn.py +67 -0
- package/templates/qoder/scripts/notify.py +5 -0
- package/templates/qoder/scripts/parse_prds.py +33 -0
- package/templates/qoder/scripts/report.py +281 -0
- package/templates/qoder/scripts/role.py +39 -0
- package/templates/qoder/scripts/run_weekly_update.bat +17 -0
- package/templates/qoder/scripts/run_weekly_update.sh +20 -0
- package/templates/qoder/scripts/search_index.py +352 -0
- package/templates/qoder/scripts/setup.py +453 -0
- package/templates/qoder/scripts/setup_weekly_cron.bat +22 -0
- package/templates/qoder/scripts/setup_weekly_cron.sh +19 -0
- package/templates/qoder/scripts/status.py +389 -0
- package/templates/qoder/scripts/syncgate.py +330 -0
- package/templates/qoder/scripts/task.py +954 -0
- package/templates/qoder/scripts/team.py +29 -0
- package/templates/qoder/scripts/team_sync.py +419 -0
- package/templates/qoder/scripts/workspace_init.py +102 -0
- package/templates/qoder/settings.json +53 -0
- package/templates/qoder/skills/design-review/SKILL.md +25 -0
- package/templates/qoder/skills/prd-generator/SKILL.md +180 -0
- package/templates/qoder/skills/prd-review/SKILL.md +36 -0
- package/templates/qoder/skills/prototype-generator/SKILL.md +141 -0
- package/templates/qoder/skills/spec-coder/SKILL.md +69 -0
- package/templates/qoder/skills/spec-generator/SKILL.md +67 -0
- package/templates/qoder/skills/test-generator/SKILL.md +72 -0
- package/templates/qoder/skills/wl-commit/SKILL.md +76 -0
- package/templates/qoder/skills/wl-init/SKILL.md +67 -0
- package/templates/qoder/skills/wl-insight/SKILL.md +81 -0
- package/templates/qoder/skills/wl-report/SKILL.md +87 -0
- package/templates/qoder/skills/wl-search/SKILL.md +75 -0
- package/templates/qoder/skills/wl-status/SKILL.md +61 -0
- package/templates/qoder/skills/wl-task/SKILL.md +58 -0
- package/templates/qoder/templates/prd-full-template.md +103 -0
- package/templates/qoder/templates/prd-quick-template.md +69 -0
- package/templates/qoder/templates/prototype-app.html +344 -0
- package/templates/qoder/templates/prototype-web.html +310 -0
- package/templates/root/AGENTS.md +182 -0
- package/templates/root/README-pipeline.md +56 -0
- package/templates/root/ROLES.md +85 -0
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
roles:
|
|
2
|
+
pm:
|
|
3
|
+
name: Product Manager
|
|
4
|
+
permissions: [create_task, write_prd, approve_prd, review_spec]
|
|
5
|
+
skills: [prd-generator, prd-review]
|
|
6
|
+
design:
|
|
7
|
+
name: Designer
|
|
8
|
+
permissions: [upload_design, approve_design]
|
|
9
|
+
skills: [design-review]
|
|
10
|
+
dev:
|
|
11
|
+
name: Developer
|
|
12
|
+
permissions: [write_spec, implement, commit, review_code]
|
|
13
|
+
skills: [spec-coder, test-generator]
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
workspace_dir: workspace
|
|
17
|
+
prd_dir: specs/prd
|
|
18
|
+
spec_dir: specs
|
|
19
|
+
task_dir: tasks
|
|
20
|
+
data_dir: data
|
|
21
|
+
auto_spec_on_prd_push: true
|
|
22
|
+
req_id_start: 1
|
|
23
|
+
|
|
24
|
+
# ── Platforms (知识图谱/搜索的平台映射, 换项目改这里即可) ──
|
|
25
|
+
# search_index.py --platform <key或alias> 会过滤到对应 project 目录
|
|
26
|
+
platforms:
|
|
27
|
+
web:
|
|
28
|
+
project: fywl-ui
|
|
29
|
+
aliases: [pc, 管理端, 后台]
|
|
30
|
+
ui_stack: "Ant Design Vue + VxeGrid (Vben Admin)"
|
|
31
|
+
prototype_template: .qoder/templates/prototype-web.html
|
|
32
|
+
app:
|
|
33
|
+
project: Carmg-H5
|
|
34
|
+
aliases: [mobile, h5, 移动端]
|
|
35
|
+
ui_stack: "Vant 3"
|
|
36
|
+
prototype_template: .qoder/templates/prototype-app.html
|
|
37
|
+
|
|
38
|
+
# ── Git Sync (Weekly) ──
|
|
39
|
+
# 每个项目在 data/code/ 下有自己的 git 仓库
|
|
40
|
+
# 运行: python .qoder/scripts/git_sync.py
|
|
41
|
+
# 或设 cron: 每周五 18:00
|
|
42
|
+
#
|
|
43
|
+
# ★ 源码仓库配置 (重要) ★
|
|
44
|
+
# 把下面的 url 改成你团队的 git 地址 (Gitea/GitLab/GitHub 均可)。
|
|
45
|
+
# branch 改成你的主分支。
|
|
46
|
+
# 不知道填什么? 注释掉整个 projects 段, 手动把代码放到 data/code/<项目名>/ 即可。
|
|
47
|
+
# 填完后跑: python .qoder/scripts/setup.py 会自动克隆+建索引。
|
|
48
|
+
git_sync:
|
|
49
|
+
schedule: "0 18 * * 5"
|
|
50
|
+
projects:
|
|
51
|
+
Carmg-H5:
|
|
52
|
+
remote: origin
|
|
53
|
+
url: http://10.87.106.228:8877/pengxicheng/Carmg-H5.git # ← 改成你的
|
|
54
|
+
branch: jlzDEV # ← 改成你的主分支
|
|
55
|
+
fywl-ics:
|
|
56
|
+
remote: origin
|
|
57
|
+
url: http://10.87.106.228:8877/fywl-ics/fywl-ics.git # ← 改成你的
|
|
58
|
+
branch: release-fix # ← 改成你的主分支
|
|
59
|
+
fywl-ui:
|
|
60
|
+
remote: origin
|
|
61
|
+
url: http://10.87.106.228:8877/fywl-ics/fywl-ui.git # ← 改成你的
|
|
62
|
+
branch: release-fix # ← 改成你的主分支
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
prd_modes:
|
|
66
|
+
reference:
|
|
67
|
+
steps: [scan, analyze, reflect, confirm, generate]
|
|
68
|
+
product_types: [B2C, B2B, internal, platform]
|
|
69
|
+
quality_checklist:
|
|
70
|
+
- background_not_vague
|
|
71
|
+
- goals_quantifiable
|
|
72
|
+
- user_personas_specific
|
|
73
|
+
- business_rules_exhaustive
|
|
74
|
+
- exceptions_covered
|
|
75
|
+
- ac_testable
|
|
76
|
+
- data_events_complete
|
|
77
|
+
- no_tech_implementation
|
|
78
|
+
- priority_tagged
|
|
79
|
+
- schedule_grounded
|
|
80
|
+
built_in:
|
|
81
|
+
- competitive_scan
|
|
82
|
+
- story_breakdown
|
|
83
|
+
# Brainstorm mode (dispatches .qoder/agents/prd-research.md sub-agents)
|
|
84
|
+
brainstorm:
|
|
85
|
+
min_agents: 2
|
|
86
|
+
default_agents: 4
|
|
87
|
+
built_in:
|
|
88
|
+
- SCAMPER
|
|
89
|
+
- 5Whys
|
|
90
|
+
- constraint_innovation
|
|
91
|
+
- impact_effort_screening
|
|
92
|
+
planning:
|
|
93
|
+
steps: [current_state, ideal_state, gap_analysis, plans]
|
|
94
|
+
quick:
|
|
95
|
+
steps: [locate, generate, confirm]
|
|
96
|
+
|
|
97
|
+
task:
|
|
98
|
+
prioritization: RICE
|
|
99
|
+
auto_rank: true
|
|
100
|
+
|
|
101
|
+
status:
|
|
102
|
+
dimensions:
|
|
103
|
+
- on_time_delivery
|
|
104
|
+
- scope_stability
|
|
105
|
+
- dependency_health
|
|
106
|
+
- pipeline_flow
|
|
107
|
+
- learning_growth
|
|
108
|
+
|
|
109
|
+
insight:
|
|
110
|
+
types: [feedback, metrics]
|
|
111
|
+
|
|
112
|
+
# ── Feishu (飞书) 通知集成 (阶段 D) ──
|
|
113
|
+
# 群机器人 webhook: 在飞书群设置 > 群机器人 > 添加自定义机器人 > 拿 webhook URL
|
|
114
|
+
# 支持环境变量覆盖: export FEISHU_WEBHOOK_URL=...
|
|
115
|
+
feishu:
|
|
116
|
+
enabled: false # 设 true 并配 webhook_url 后才推送
|
|
117
|
+
webhook_url: "" # 或填群机器人 webhook, 或用环境变量 FEISHU_WEBHOOK_URL
|
|
118
|
+
secret: "" # 签名校验密钥 (机器人启用签名校验时填), 或 FEISHU_SECRET
|
|
119
|
+
# 推送哪些事件 (不配则全推)
|
|
120
|
+
events:
|
|
121
|
+
- task_finished # 任务完成
|
|
122
|
+
- prd_published # PRD 发布
|
|
123
|
+
- eval_rejected # EVA 门禁拒绝
|
|
124
|
+
- deadline_warning # 截止日期临近 (由 /wl-status 触发)
|
|
125
|
+
# 卡片主题色 (default/green/orange/red)
|
|
126
|
+
theme:
|
|
127
|
+
task_finished: green
|
|
128
|
+
prd_published: blue
|
|
129
|
+
eval_rejected: red
|
|
130
|
+
deadline_warning: orange
|
|
131
|
+
|
|
132
|
+
feedback_categories: [feature_request, bug, UX, performance, praise, question]
|
|
133
|
+
metrics_analyses: [growth, retention, funnel, ab_experiment, okr]
|
|
134
|
+
|
|
135
|
+
notification:
|
|
136
|
+
console:
|
|
137
|
+
type: console
|
|
138
|
+
enabled: true
|
|
139
|
+
|
|
140
|
+
codex:
|
|
141
|
+
dispatch_mode: inline
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# inject-workflow-state.py - Inject workflow state on every message
|
|
2
|
+
import os, json, sys
|
|
3
|
+
|
|
4
|
+
if sys.platform == 'win32':
|
|
5
|
+
try:
|
|
6
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
7
|
+
except Exception:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
NL = chr(10)
|
|
11
|
+
BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
12
|
+
|
|
13
|
+
# mtime 缓存 (审计 H5: 每条用户消息都跑这个 hook, 缓存避免重复读盘)
|
|
14
|
+
# 格式: {filepath: (mtime, parsed_value)}
|
|
15
|
+
_CACHE = {}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _read_developer():
|
|
19
|
+
"""读当前开发者名 (容错: UTF-8/GBK)."""
|
|
20
|
+
df = os.path.join(BASE, '.qoder', '.developer')
|
|
21
|
+
if not os.path.isfile(df):
|
|
22
|
+
return None
|
|
23
|
+
for enc in ('utf-8', 'gbk', 'utf-8-sig'):
|
|
24
|
+
try:
|
|
25
|
+
with open(df, encoding=enc) as f:
|
|
26
|
+
for line in f:
|
|
27
|
+
line = line.strip()
|
|
28
|
+
if line.startswith('name='):
|
|
29
|
+
return line.split('=', 1)[1].strip()
|
|
30
|
+
if line.startswith('name:') or ':' in line:
|
|
31
|
+
# 容错 name: x 格式
|
|
32
|
+
for sep in ('=', ':'):
|
|
33
|
+
if sep in line:
|
|
34
|
+
k, v = line.split(sep, 1)
|
|
35
|
+
if k.strip() == 'name':
|
|
36
|
+
return v.strip()
|
|
37
|
+
except (OSError, UnicodeDecodeError):
|
|
38
|
+
continue
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _cached_read(path, parser):
|
|
43
|
+
"""带 mtime 缓存的文件读。parser: callable(content_str) -> value."""
|
|
44
|
+
try:
|
|
45
|
+
mtime = os.path.getmtime(path)
|
|
46
|
+
except OSError:
|
|
47
|
+
return None
|
|
48
|
+
cached = _CACHE.get(path)
|
|
49
|
+
if cached and cached[0] == mtime:
|
|
50
|
+
return cached[1]
|
|
51
|
+
# 重读
|
|
52
|
+
for enc in ('utf-8', 'gbk', 'utf-8-sig'):
|
|
53
|
+
try:
|
|
54
|
+
with open(path, encoding=enc) as f:
|
|
55
|
+
content = f.read()
|
|
56
|
+
value = parser(content)
|
|
57
|
+
_CACHE[path] = (mtime, value)
|
|
58
|
+
return value
|
|
59
|
+
except (OSError, UnicodeDecodeError):
|
|
60
|
+
continue
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_status():
|
|
65
|
+
# 优先读按开发者隔离的 current-task 文件
|
|
66
|
+
dev = _read_developer()
|
|
67
|
+
ct_candidates = []
|
|
68
|
+
if dev:
|
|
69
|
+
safe = ''.join(c for c in dev if c.isalnum() or c in '_-') or 'anon'
|
|
70
|
+
ct_candidates.append(os.path.join(BASE, '.qoder', '.runtime', 'current-task.' + safe))
|
|
71
|
+
ct_candidates.append(os.path.join(BASE, '.qoder', '.current-task')) # 旧格式回退
|
|
72
|
+
|
|
73
|
+
rel = None
|
|
74
|
+
for ct in ct_candidates:
|
|
75
|
+
rel = _cached_read(ct, lambda c: c.strip() or None)
|
|
76
|
+
if rel:
|
|
77
|
+
break
|
|
78
|
+
if not rel:
|
|
79
|
+
return None, None
|
|
80
|
+
|
|
81
|
+
tj = os.path.join(BASE, rel, 'task.json')
|
|
82
|
+
if os.path.isfile(tj):
|
|
83
|
+
d = _cached_read(tj, lambda c: json.loads(c) if c.strip() else None)
|
|
84
|
+
if d:
|
|
85
|
+
return rel, d.get('status', 'unknown')
|
|
86
|
+
return rel, None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# Statuses written by task.py: planning -> in_progress -> completed
|
|
90
|
+
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.',
|
|
93
|
+
'in_progress': 'Dev in progress. /wl-spec to generate spec, /wl-code to implement.',
|
|
94
|
+
'completed': 'Task completed. /wl-task archive to archive, or start the next one.',
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def main():
|
|
99
|
+
tp, st = get_status()
|
|
100
|
+
g = GUIDE.get(st, 'Status "{}" - check task.json, expected planning/in_progress/completed.'.format(st))
|
|
101
|
+
parts = []
|
|
102
|
+
parts.append('<qoder-workflow>')
|
|
103
|
+
if tp:
|
|
104
|
+
parts.append('Task: ' + tp)
|
|
105
|
+
parts.append('Status: ' + str(st))
|
|
106
|
+
parts.append('Do: ' + g)
|
|
107
|
+
parts.append('</qoder-workflow>')
|
|
108
|
+
print(NL.join(parts))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == '__main__':
|
|
112
|
+
try:
|
|
113
|
+
main()
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print('<qoder-workflow>')
|
|
116
|
+
print('hook error: ' + str(e)[:200])
|
|
117
|
+
print('</qoder-workflow>')
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# session-start.py - Inject project context on session start
|
|
2
|
+
import os, json, sys
|
|
3
|
+
|
|
4
|
+
if sys.platform == 'win32':
|
|
5
|
+
try:
|
|
6
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
7
|
+
except Exception:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
NL = chr(10)
|
|
11
|
+
BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
12
|
+
|
|
13
|
+
# Injected as REAL OUTPUT below (a comment here would never reach the AI)
|
|
14
|
+
CRITICAL_RULE = NL.join([
|
|
15
|
+
'## CRITICAL RULE',
|
|
16
|
+
'When user asks for /wl-prd, ALWAYS ask platform FIRST:',
|
|
17
|
+
'"这个需求是针对哪个平台?',
|
|
18
|
+
' 1. Web 管理端 (fywl-ui) - Ant Design Vue + VxeGrid 风格',
|
|
19
|
+
' 2. APP 移动端 (Carmg-H5) - Vant 风格',
|
|
20
|
+
' 3. 两端都要',
|
|
21
|
+
' 请选择 (1/2/3):"',
|
|
22
|
+
'DO NOT skip this question. DO NOT auto-detect. ALWAYS ASK.',
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_json_safe(path):
|
|
27
|
+
try:
|
|
28
|
+
with open(path, encoding='utf-8') as f:
|
|
29
|
+
return json.load(f)
|
|
30
|
+
except Exception:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_developer():
|
|
35
|
+
df = os.path.join(BASE, '.qoder', '.developer')
|
|
36
|
+
if not os.path.isfile(df):
|
|
37
|
+
return None
|
|
38
|
+
try:
|
|
39
|
+
with open(df, encoding='utf-8') as f:
|
|
40
|
+
content = f.read()
|
|
41
|
+
except Exception:
|
|
42
|
+
return None
|
|
43
|
+
info = {}
|
|
44
|
+
for line in content.strip().splitlines():
|
|
45
|
+
for sep in ('=', ':'):
|
|
46
|
+
if sep in line:
|
|
47
|
+
k, v = line.split(sep, 1)
|
|
48
|
+
info[k.strip()] = v.strip()
|
|
49
|
+
break
|
|
50
|
+
return info or None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_active_tasks():
|
|
54
|
+
td = os.path.join(BASE, 'workspace', 'tasks')
|
|
55
|
+
if not os.path.isdir(td):
|
|
56
|
+
return []
|
|
57
|
+
tasks = []
|
|
58
|
+
for d in sorted(os.listdir(td)):
|
|
59
|
+
data = load_json_safe(os.path.join(td, d, 'task.json'))
|
|
60
|
+
if data is None:
|
|
61
|
+
continue
|
|
62
|
+
tasks.append({
|
|
63
|
+
'name': d,
|
|
64
|
+
'title': data.get('title', d),
|
|
65
|
+
'status': data.get('status', 'unknown')
|
|
66
|
+
})
|
|
67
|
+
return tasks
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_team_size():
|
|
71
|
+
md = os.path.join(BASE, 'workspace', 'members')
|
|
72
|
+
if not os.path.isdir(md):
|
|
73
|
+
return 0
|
|
74
|
+
return len([d for d in os.listdir(md) if os.path.isdir(os.path.join(md, d))])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_index_info():
|
|
78
|
+
lines = []
|
|
79
|
+
lines.append('## Knowledge Index')
|
|
80
|
+
lines.append('Use search script (DO NOT read large JSON files directly):')
|
|
81
|
+
lines.append(' python .qoder/scripts/search_index.py <keyword> [--platform web|app]')
|
|
82
|
+
lines.append(' python .qoder/scripts/search_index.py --prd <keyword> # search PRDs')
|
|
83
|
+
lines.append(' python .qoder/scripts/search_index.py --style <table|form> [--platform p]')
|
|
84
|
+
lines.append(' python .qoder/scripts/search_index.py --field <name> # field usage')
|
|
85
|
+
lines.append(' python .qoder/scripts/search_index.py --api <keyword> # API endpoints')
|
|
86
|
+
lines.append(' python .qoder/scripts/search_index.py --components # UI components')
|
|
87
|
+
lines.append(' python .qoder/scripts/search_index.py --modules # project overview')
|
|
88
|
+
lines.append(' python .qoder/scripts/search_index.py --list # top keywords')
|
|
89
|
+
|
|
90
|
+
meta = load_json_safe(os.path.join(BASE, 'data', 'index', '.index-meta.json'))
|
|
91
|
+
if meta:
|
|
92
|
+
projects = meta.get('projects', {})
|
|
93
|
+
if isinstance(projects, dict):
|
|
94
|
+
for proj, info in projects.items():
|
|
95
|
+
if isinstance(info, dict):
|
|
96
|
+
files = info.get('files', 0)
|
|
97
|
+
else:
|
|
98
|
+
files = info # new meta format: {project: file_count}
|
|
99
|
+
lines.append(' ' + str(proj) + ': ' + str(files) + ' files')
|
|
100
|
+
elif isinstance(projects, list):
|
|
101
|
+
for proj in projects:
|
|
102
|
+
lines.append(' ' + str(proj))
|
|
103
|
+
lines.append('Last sync: ' + str(meta.get('last_sync', 'unknown')))
|
|
104
|
+
# Staleness warning (审计发现: cron 从未注册, 索引可能静默过期)
|
|
105
|
+
last_sync = meta.get('last_sync')
|
|
106
|
+
if last_sync:
|
|
107
|
+
try:
|
|
108
|
+
from datetime import datetime
|
|
109
|
+
# 兼容多种格式: "2026-06-12 16:49" / ISO
|
|
110
|
+
ts = str(last_sync).replace('T', ' ').split('.')[0].strip()
|
|
111
|
+
for fmt in ('%Y-%m-%d %H:%M', '%Y-%m-%d %H:%M:%S', '%Y-%m-%dT%H:%M:%S'):
|
|
112
|
+
try:
|
|
113
|
+
sync_dt = datetime.strptime(ts, fmt)
|
|
114
|
+
break
|
|
115
|
+
except ValueError:
|
|
116
|
+
continue
|
|
117
|
+
else:
|
|
118
|
+
sync_dt = None
|
|
119
|
+
if sync_dt:
|
|
120
|
+
age_days = (datetime.now() - sync_dt).days
|
|
121
|
+
if age_days > 30:
|
|
122
|
+
lines.append('[CRITICAL] 索引已 {} 天未更新! 搜索结果严重过期. 跑: python .qoder/scripts/git_sync.py'.format(age_days))
|
|
123
|
+
elif age_days > 7:
|
|
124
|
+
lines.append('[WARN] 索引已 {} 天未更新, 建议刷新: python .qoder/scripts/git_sync.py'.format(age_days))
|
|
125
|
+
except Exception:
|
|
126
|
+
pass # 日期解析失败不阻塞
|
|
127
|
+
if meta.get('failures'):
|
|
128
|
+
lines.append('WARNING: last sync had errors - see data/index/.index-meta.json')
|
|
129
|
+
|
|
130
|
+
return NL.join(lines)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_style_info():
|
|
134
|
+
"""Read style metadata for quick reference"""
|
|
135
|
+
meta = load_json_safe(os.path.join(BASE, 'data', 'index', 'style-meta.json'))
|
|
136
|
+
if not meta:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
lines = []
|
|
140
|
+
lines.append('## UI Style Index')
|
|
141
|
+
lines.append('Style search: python .qoder/scripts/search_index.py --style <table|form|modal>')
|
|
142
|
+
lines.append('Field search: python .qoder/scripts/search_index.py --field <name>')
|
|
143
|
+
|
|
144
|
+
for proj, d in meta.get('projects', {}).items():
|
|
145
|
+
vue = d.get('vue_files', 0)
|
|
146
|
+
if vue > 0:
|
|
147
|
+
pt = d.get('page_types', {})
|
|
148
|
+
types_str = ', '.join([k + ':' + str(v) for k, v in pt.items() if v > 0])
|
|
149
|
+
lines.append(' ' + proj + ': ' + str(vue) + ' Vue (' + types_str + ')')
|
|
150
|
+
|
|
151
|
+
lines.append('Top components: ' + ', '.join(list(meta.get('common_components', {}).keys())[:8]))
|
|
152
|
+
lines.append('Field map: ' + str(meta.get('field_count', 0)) + ' entries')
|
|
153
|
+
|
|
154
|
+
if os.path.isdir(os.path.join(BASE, 'data', 'style')):
|
|
155
|
+
lines.append('Style PDFs: data/style/ (design specs, code takes priority)')
|
|
156
|
+
|
|
157
|
+
return NL.join(lines)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main():
|
|
161
|
+
parts = []
|
|
162
|
+
parts.append('<qoder-context>')
|
|
163
|
+
dev = get_developer()
|
|
164
|
+
if dev:
|
|
165
|
+
name = dev.get('name', 'unknown')
|
|
166
|
+
role = dev.get('role', 'unknown')
|
|
167
|
+
parts.append('Developer: ' + name + ' (' + role + ')')
|
|
168
|
+
parts.append('Workspace: workspace/members/' + name + '/')
|
|
169
|
+
else:
|
|
170
|
+
parts.append('Developer: NOT SET. Run /wl-init to register.')
|
|
171
|
+
team = get_team_size()
|
|
172
|
+
if team > 0:
|
|
173
|
+
parts.append('Team members: ' + str(team))
|
|
174
|
+
tasks = get_active_tasks()
|
|
175
|
+
if tasks:
|
|
176
|
+
parts.append('Active tasks:')
|
|
177
|
+
for t in tasks:
|
|
178
|
+
parts.append(' [' + t['status'] + '] ' + t['name'] + ' - ' + t['title'])
|
|
179
|
+
|
|
180
|
+
parts.append('')
|
|
181
|
+
parts.append(CRITICAL_RULE)
|
|
182
|
+
|
|
183
|
+
idx_info = get_index_info()
|
|
184
|
+
if idx_info:
|
|
185
|
+
parts.append('')
|
|
186
|
+
parts.append(idx_info)
|
|
187
|
+
|
|
188
|
+
style_info = get_style_info()
|
|
189
|
+
if style_info:
|
|
190
|
+
parts.append('')
|
|
191
|
+
parts.append(style_info)
|
|
192
|
+
|
|
193
|
+
parts.append('</qoder-context>')
|
|
194
|
+
print(NL.join(parts))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
if __name__ == '__main__':
|
|
198
|
+
try:
|
|
199
|
+
main()
|
|
200
|
+
except Exception as e:
|
|
201
|
+
# A broken hook must never kill the session - emit minimal context
|
|
202
|
+
print('<qoder-context>')
|
|
203
|
+
print('session-start hook error: ' + str(e)[:200])
|
|
204
|
+
print('</qoder-context>')
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: always_on
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# QODER Pipeline 工作流规则(全模式生效)
|
|
6
|
+
|
|
7
|
+
> 本规则是 wl-pipeline 的核心约束,通过 .qoder/rules/ 在 Qoder IDE 对话、
|
|
8
|
+
> Quest 模式中始终生效。Qoder CLI 读根目录 AGENTS.md(内容一致)。
|
|
9
|
+
> 完整命令定义在 .qoder/commands/wl-*.md,技能在 .qoder/skills/。
|
|
10
|
+
|
|
11
|
+
## 规则 0:自然语言 → 技能 路由表(全端通用)
|
|
12
|
+
|
|
13
|
+
所有 Qoder 产品(IDE 编辑器 / Quest 模式 / QoderWork 桌面端 / CLI)都支持两种触发方式:
|
|
14
|
+
1. **斜杠命令**:输入 `/wl-prd` `/wl-search` 等(commands 和 skills 都可用 `/` 触发)
|
|
15
|
+
2. **自然语言**:直接说"写个需求""查一下代码",AI 按下表自动映射到对应 skill
|
|
16
|
+
|
|
17
|
+
> 注意:Quest/QoderWork 里 `/` 会同时列出 commands(如 `/wl-prd`)和 skills(如 `/prd-generator`)。
|
|
18
|
+
> 两者功能等价,优先用 `/wl-*` 命令(更稳定,参数提示更全)。
|
|
19
|
+
|
|
20
|
+
| 用户说什么 | 触发的 skill/command |
|
|
21
|
+
|------------|---------------------|
|
|
22
|
+
| "写个需求""生成PRD""做个原型""保单这块加个功能" | `/wl-prd` 或 prd-generator + prototype-generator |
|
|
23
|
+
| "查一下""代码在哪""搜保险""考勤的API""字段含义" | `/wl-search` 或 wl-search |
|
|
24
|
+
| "建任务""排优先级""这个任务做完了""开始做这个" | `/wl-task` 或 wl-task |
|
|
25
|
+
| "项目怎么样""进度""健康度""路线图" | `/wl-status` 或 wl-status |
|
|
26
|
+
| "写日报""周报""我这周干了啥""今天做了什么" | `/wl-report` 或 wl-report |
|
|
27
|
+
| "用户反馈""数据怎么样""转化漏斗""留存" | `/wl-insight` 或 wl-insight |
|
|
28
|
+
| "生成规格""写spec""技术方案" | `/wl-spec` 或 spec-generator |
|
|
29
|
+
| "开始写代码""按规格实现""实现这个接口" | `/wl-code` 或 spec-coder |
|
|
30
|
+
| "写单元测试""补测试""覆盖一下" | `/wl-test` 或 test-generator |
|
|
31
|
+
| "评审PRD""PRD质量怎么样" | prd-review |
|
|
32
|
+
| "提交代码""commit""推上去""保存代码" | `/wl-commit` 或 wl-commit |
|
|
33
|
+
| "初始化""换开发者""环境检查""我是谁" | `/wl-init` 或 wl-init |
|
|
34
|
+
|
|
35
|
+
**找不到明确匹配时:先问用户意图,不要乱跑**("你是想写 PRD、查代码、还是建任务?")。
|
|
36
|
+
注意:用户说"同步/保存产出"通常指 team_sync(push),不是 git commit;
|
|
37
|
+
用户说"提交/commit 代码"才是 wl-commit。
|
|
38
|
+
|
|
39
|
+
## 规则 1:PRD/原型需求必须先问平台
|
|
40
|
+
|
|
41
|
+
用户提出 PRD、原型、需求分析类请求时,第一步永远是问(绝不自动判断):
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
这个需求是针对哪个平台?
|
|
45
|
+
1. **Web 管理端** (fywl-ui) - Ant Design Vue + VxeGrid 风格
|
|
46
|
+
2. **APP 移动端** (Carmg-H5) - Vant 风格
|
|
47
|
+
3. **两端都要
|
|
48
|
+
请选择 (1/2/3):
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 规则 2:查代码只用索引,不全库 grep
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python .qoder/scripts/context_pack.py <业务关键词> --platform <web|app> # 一次取全(首选)
|
|
55
|
+
python .qoder/scripts/search_index.py <关键词> --platform <web|app> # 单项搜索
|
|
56
|
+
```
|
|
57
|
+
context_pack 一条命令返回:相关代码文件 + 同类页面示例 + 字段规格 +
|
|
58
|
+
相关 PRD + 设计 Token + 图标参考 + 模板路径。禁止对 data/code/ 全库 grep。
|
|
59
|
+
|
|
60
|
+
## 规则 3:原型风格必须来自真源(含图标)
|
|
61
|
+
|
|
62
|
+
- 起点永远是平台模板:.qoder/templates/prototype-web.html / prototype-app.html
|
|
63
|
+
- 颜色只用真源:data/index/vben-style-reference.json(Web HSL token)、
|
|
64
|
+
chart-style-reference.json(看板/大屏两种系统内风格)、Vant 变量(APP)
|
|
65
|
+
- **图标绝对禁止 emoji**:Web 用 data/index/icon-reference.json 里的
|
|
66
|
+
Ant Design 真实 SVG(内联,fill=currentColor);APP 用 Vant 字体图标
|
|
67
|
+
(CDN: https://cdn.jsdelivr.net/npm/vant@4/lib/index.css + van-icon 类名)
|
|
68
|
+
- 新功能(无现有页面)也必须锚定系统内最近似的真实页面:
|
|
69
|
+
大屏/全国地图类 → dashboard/monitor 先例;工作台卡片类 → dashboard/workbench
|
|
70
|
+
- 原型头部注释必须写明风格来自哪些真实文件,写不出来源 = 风格提取没做
|
|
71
|
+
|
|
72
|
+
## 规则 4:PRD 必须套团队模板 + 过 EVA 门禁
|
|
73
|
+
|
|
74
|
+
- 完整需求用 .qoder/templates/prd-full-template.md,零星需求用 prd-quick-template.md
|
|
75
|
+
- 发布前:`python .qoder/scripts/eval_prd.py <prd> [原型]` 必须 PASS (≥80%)
|
|
76
|
+
- 发现确认采用批量方式(一个编号列表一轮确认),不要逐条问
|
|
77
|
+
|
|
78
|
+
## 规则 5:用户永不接触 git
|
|
79
|
+
|
|
80
|
+
- 产出保存/发布后自动执行 `python .qoder/scripts/team_sync.py push`
|
|
81
|
+
- 拉取团队最新:`python .qoder/scripts/team_sync.py pull`
|
|
82
|
+
- 出现 SYNC_CONFLICT 由 AI 按提示自行解决,绝不让用户跑 git 命令
|
|
83
|
+
|
|
84
|
+
## 规则 6:环境问题找 doctor
|
|
85
|
+
|
|
86
|
+
任何"索引不对/搜不到/环境坏了"类问题:
|
|
87
|
+
`python .qoder/scripts/init_doctor.py`(加 --fix 自动修复,幂等增量,不会重头构建)
|
|
88
|
+
|
|
89
|
+
## 规则 7:Repo Wiki 是补充知识源
|
|
90
|
+
|
|
91
|
+
若存在 .qoder/repowiki/(Qoder IDE 生成的仓库 Wiki),写 PRD 前先查其中
|
|
92
|
+
相关模块页面(架构、模块说明比索引更全面);data/code/ 下各源码仓库若有
|
|
93
|
+
自己的 repowiki 同样优先参考。索引负责"定位文件",Wiki 负责"理解模块"。
|
|
94
|
+
|
|
95
|
+
## 用户侧命令一览(用户只需要知道这些)
|
|
96
|
+
|
|
97
|
+
| 命令 | 作用 |
|
|
98
|
+
|------|------|
|
|
99
|
+
| /wl-init [名字] [角色] | 初始化/切换开发者 + 环境体检 |
|
|
100
|
+
| /wl-prd <需求描述> | 生成 PRD + 原型(最核心) |
|
|
101
|
+
| /wl-search <关键词> | 查代码/PRD/字段/API |
|
|
102
|
+
| /wl-task, /wl-status, /wl-report | 任务/状态/日报周报 |
|
|
103
|
+
| /wl-spec, /wl-code, /wl-test, /wl-commit | 规格/编码/测试/提交(后三个需确认) |
|
|
104
|
+
|
|
105
|
+
用户用自然语言表达即可,AI 负责映射到命令和脚本。
|