@hupan56/wlkj 2.7.12 → 3.1.2
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 +1435 -1107
- package/package.json +29 -29
- 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 +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- 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.py +0 -298
- 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_api_params.py +0 -246
- 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/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- 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/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,27 +1,43 @@
|
|
|
1
1
|
# 角色定义 (与 kg.py ROLE_SECTIONS 的 5 角色对齐)
|
|
2
2
|
# commands: 该角色建议主用的命令 (软引导, 不拦截 —— 任何角色都能跑任何命令)
|
|
3
|
-
# permissions: 声明式权限 (
|
|
3
|
+
# permissions: 声明式权限 (遗留字段, 向后兼容)
|
|
4
|
+
#
|
|
5
|
+
# ★ 能力边界 (v3.0+ 解耦核心) ★
|
|
6
|
+
# can_produce: 该角色允许产出的契约类型 (guard.py 强制, 越权则降级)
|
|
7
|
+
# can_consume: 该角色允许读取/消费的能力 (越权则只读降级)
|
|
8
|
+
# 契约类型必须与 contracts/schemas/*.schema.json 的 type 对齐:
|
|
9
|
+
# prd / prototype / spec / design-spec / code / unit-test / test-report / task
|
|
4
10
|
roles:
|
|
5
11
|
pm:
|
|
6
12
|
name: 产品经理
|
|
7
13
|
permissions: [create_task, write_prd, approve_prd, review_spec]
|
|
8
|
-
commands: [wl-init, wl-insight, wl-prd
|
|
14
|
+
commands: [wl-init, wl-insight, wl-prd, wl-design, wl-task, wl-search, wl-status, wl-report]
|
|
15
|
+
can_produce: [prd, prototype]
|
|
16
|
+
can_consume: ["*"] # PM 需读代码做现状分析
|
|
9
17
|
design:
|
|
10
18
|
name: 设计师
|
|
11
19
|
permissions: [upload_design, approve_design]
|
|
12
20
|
commands: [wl-init, wl-design, wl-search, wl-status]
|
|
21
|
+
can_produce: [design-spec, prototype]
|
|
22
|
+
can_consume: [prd, code-search]
|
|
13
23
|
dev:
|
|
14
24
|
name: 开发
|
|
15
25
|
permissions: [write_spec, implement, commit, review_code]
|
|
16
26
|
commands: [wl-init, wl-spec, wl-code, wl-test, wl-search, wl-commit, wl-status]
|
|
27
|
+
can_produce: [spec, code, unit-test]
|
|
28
|
+
can_consume: [prd, prototype, task]
|
|
17
29
|
test:
|
|
18
30
|
name: 测试
|
|
19
31
|
permissions: [run_test, review_acceptance]
|
|
20
32
|
commands: [wl-init, wl-test, wl-search, wl-status]
|
|
33
|
+
can_produce: [test-report, test-cases, unit-test]
|
|
34
|
+
can_consume: [prd, task, prototype]
|
|
21
35
|
admin:
|
|
22
36
|
name: 管理员
|
|
23
37
|
permissions: [manage_index, manage_members, all]
|
|
24
38
|
commands: [wl-init, wl-status, wl-search, wl-report]
|
|
39
|
+
can_produce: ["*"]
|
|
40
|
+
can_consume: ["*"]
|
|
25
41
|
|
|
26
42
|
pipeline:
|
|
27
43
|
workspace_dir: workspace
|
|
@@ -32,20 +48,42 @@ pipeline:
|
|
|
32
48
|
auto_spec_on_prd_push: true
|
|
33
49
|
req_id_start: 1
|
|
34
50
|
|
|
35
|
-
# ── MySQL 只读 MCP (
|
|
51
|
+
# ── MySQL 只读 MCP (团队共享, 多环境) ──
|
|
52
|
+
# AI 查数据库时会先问"查哪个环境",用这里的标签让用户选。
|
|
53
|
+
# 标签名自定义 (test/pre/prod 或 qa/staging...),对应 environments 下的 key。
|
|
54
|
+
# 没配 environments (旧扁平结构) 时兼容:当作只有 default_env 一个环境。
|
|
36
55
|
mysql:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
default_env: test # AI 不指定 / 用户不选时的默认环境
|
|
57
|
+
environments:
|
|
58
|
+
test: # 测试环境
|
|
59
|
+
host: 10.54.6.9
|
|
60
|
+
port: 3306
|
|
61
|
+
user: fywl_ics_test
|
|
62
|
+
password: Fywl_ics_test56476
|
|
63
|
+
env_name: "QAS 测试环境" # 显示标签,会随查询结果返回提醒 AI 当前在哪个环境
|
|
64
|
+
pre: # 预生产环境 (按需填, 不用可整段删)
|
|
65
|
+
host: 10.54.6.10
|
|
66
|
+
port: 3306
|
|
67
|
+
user: fywl_ics_pre_ro
|
|
68
|
+
password: ""
|
|
69
|
+
env_name: "预生产环境"
|
|
70
|
+
prod: # 生产环境 (⚠️ 只读账号! 配了才能查, 查时 AI 会标红提醒)
|
|
71
|
+
host: fywl-mysql.rwlb.rds.aliyuncs.com
|
|
72
|
+
port: 3306
|
|
73
|
+
user: queryuser
|
|
74
|
+
password: "query2025_ics@infore"
|
|
75
|
+
env_name: "生产环境"
|
|
42
76
|
|
|
43
|
-
# ── 禅道 MCP (
|
|
44
|
-
#
|
|
77
|
+
# ── 禅道 MCP (自建内网) ──
|
|
78
|
+
# v3.1.4: 禅道是【每人一个账号】(非团队共享)。
|
|
79
|
+
# - url (服务地址): 团队共享, 留这里。
|
|
80
|
+
# - user/password (个人凭据): 不放这里 (进 git 会泄露)。
|
|
81
|
+
# 每人各自填 workspace/members/{我}/.private/secrets/zentao.env:
|
|
82
|
+
# ZENTAO_USER=<你的禅道账号>
|
|
83
|
+
# ZENTAO_PASSWORD=<你的禅道密码>
|
|
84
|
+
# - 账号名映射: member.json 的 zentao_account (进 git, 用于归属过滤)。
|
|
45
85
|
zentao:
|
|
46
86
|
url: http://10.89.2.91/zentao
|
|
47
|
-
user: hupan
|
|
48
|
-
password: Aa123456
|
|
49
87
|
|
|
50
88
|
# ── Platforms (知识图谱/搜索的平台映射, 换项目改这里即可) ──
|
|
51
89
|
# search_index.py --platform <key或alias> 会过滤到对应 project 目录
|
|
@@ -89,14 +127,14 @@ team_remote:
|
|
|
89
127
|
|
|
90
128
|
# ── Git Sync (Weekly) ──
|
|
91
129
|
# 每个项目在 data/code/ 下有自己的 git 仓库
|
|
92
|
-
# 运行: python .qoder/scripts/
|
|
130
|
+
# 运行: python .qoder/scripts/orchestration/wlkj.py git-sync
|
|
93
131
|
# 或设 cron: 每周五 18:00
|
|
94
132
|
#
|
|
95
133
|
# ★ 源码仓库配置 (重要) ★
|
|
96
134
|
# 把下面的 url 改成你团队的 git 地址 (Gitea/GitLab/GitHub 均可)。
|
|
97
135
|
# branch 改成你的主分支。
|
|
98
136
|
# 不知道填什么? 注释掉整个 projects 段, 手动把代码放到 data/code/<项目名>/ 即可。
|
|
99
|
-
# 填完后跑: python .qoder/scripts/
|
|
137
|
+
# 填完后跑: python .qoder/scripts/orchestration/wlkj.py doctor --fix 会自动克隆+建索引。
|
|
100
138
|
git_sync:
|
|
101
139
|
schedule: "0 18 * * 5"
|
|
102
140
|
projects:
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""post-tool-use.py - PostToolUse hook: 工具执行后自动埋点 (确定性, 治漏斗断链)
|
|
4
|
+
|
|
5
|
+
吸收宿主确定性 hook 能力: 把"埋点"从"靠 AI 手动调 learn record"(老忘 → F2 漏斗数据残缺)
|
|
6
|
+
升级为"宿主在工具执行后自动埋点"。AI 想忘都忘不了。
|
|
7
|
+
|
|
8
|
+
触发场景 (只埋这几类, 不滥用):
|
|
9
|
+
- Write/Edit 落到 **/prd*.md 或 **/PRD*.md → 记 prd_written
|
|
10
|
+
- Write/Edit/MultiEdit 落到 **/task.json → 记 task_created
|
|
11
|
+
- Bash 跑了 git commit / team_sync push → 记 commit_done (兜底, 主路径在 team_sync.py)
|
|
12
|
+
|
|
13
|
+
铁律:
|
|
14
|
+
- 永远 exit 0 (埋点绝不阻断工作流)
|
|
15
|
+
- 失败 try/except 静默 (hook 崩溃绝不阻塞会话)
|
|
16
|
+
- 幂等: 同一文件短时间内重复埋点靠 feedback.jsonl append 即可 (统计层去重)
|
|
17
|
+
|
|
18
|
+
阻断契约: exit 0 放行 (本 hook 不阻断)。
|
|
19
|
+
stdin 字段名官方未公开, 兼容 tool_name/tool_input 等多种写法。
|
|
20
|
+
"""
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
import sys
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
|
|
27
|
+
if sys.platform == 'win32':
|
|
28
|
+
try:
|
|
29
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
30
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
35
|
+
HOOK_ERRORS_LOG = os.path.join(BASE, '.qoder', '.runtime', 'hook-errors.log')
|
|
36
|
+
|
|
37
|
+
# 工具名: 宿主原生工具(大写) + 兼容别名
|
|
38
|
+
WRITE_TOOLS = {'write', 'edit', 'multiedit', 'str_replace_editor', 'create_file'}
|
|
39
|
+
COMMIT_RE = re.compile(r'git\s+commit|team_sync.*push|wlkj\.py\s+sync\s+push', re.IGNORECASE)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _read_tool_info():
|
|
43
|
+
"""从 stdin 读 {tool_name, tool_input}。兼容多种字段名。取不到返回 ({}, None)。"""
|
|
44
|
+
try:
|
|
45
|
+
if sys.stdin.isatty():
|
|
46
|
+
return {}, None
|
|
47
|
+
raw = sys.stdin.read().strip()
|
|
48
|
+
except Exception:
|
|
49
|
+
return {}, None
|
|
50
|
+
if not raw:
|
|
51
|
+
return {}, None
|
|
52
|
+
try:
|
|
53
|
+
data = json.loads(raw)
|
|
54
|
+
except (ValueError, TypeError):
|
|
55
|
+
return {}, None
|
|
56
|
+
if not isinstance(data, dict):
|
|
57
|
+
return {}, None
|
|
58
|
+
# tool_name 兼容
|
|
59
|
+
tool_name = None
|
|
60
|
+
for k in ('tool_name', 'name', 'tool'):
|
|
61
|
+
v = data.get(k)
|
|
62
|
+
if isinstance(v, str) and v.strip():
|
|
63
|
+
tool_name = v.strip()
|
|
64
|
+
break
|
|
65
|
+
# tool_input 兼容
|
|
66
|
+
tool_input = data.get('tool_input') or data.get('input') or data.get('args') or data.get('arguments')
|
|
67
|
+
if not isinstance(tool_input, dict):
|
|
68
|
+
tool_input = {}
|
|
69
|
+
return tool_input, tool_name
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _file_paths_from_input(tool_input, tool_name):
|
|
73
|
+
"""从 tool_input 提取涉及的文件路径列表。兼容 Write/Edit/Bash 的不同参数名。"""
|
|
74
|
+
paths = []
|
|
75
|
+
for k in ('file_path', 'path', 'filename', 'filePath'):
|
|
76
|
+
v = tool_input.get(k)
|
|
77
|
+
if isinstance(v, str) and v:
|
|
78
|
+
paths.append(v)
|
|
79
|
+
# MultiEdit 可能多文件
|
|
80
|
+
for k in ('paths', 'files'):
|
|
81
|
+
v = tool_input.get(k)
|
|
82
|
+
if isinstance(v, list):
|
|
83
|
+
paths.extend(x for x in v if isinstance(x, str))
|
|
84
|
+
return paths
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _cmd_from_input(tool_input):
|
|
88
|
+
"""Bash 工具的命令文本。"""
|
|
89
|
+
for k in ('command', 'cmd'):
|
|
90
|
+
v = tool_input.get(k)
|
|
91
|
+
if isinstance(v, str):
|
|
92
|
+
return v
|
|
93
|
+
return ''
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _dev():
|
|
97
|
+
"""读当前开发者。失败返回 None。"""
|
|
98
|
+
df = os.path.join(BASE, '.qoder', '.developer')
|
|
99
|
+
try:
|
|
100
|
+
if not os.path.isfile(df):
|
|
101
|
+
return None
|
|
102
|
+
for line in open(df, encoding='utf-8'):
|
|
103
|
+
if 'name=' in line:
|
|
104
|
+
return line.split('name=', 1)[1].strip().split()[0]
|
|
105
|
+
except Exception:
|
|
106
|
+
pass
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _append_event(event, data):
|
|
111
|
+
"""best-effort append 一条事件到当前 dev 的 feedback.jsonl。避开 learn import 断链, 直接写文件。"""
|
|
112
|
+
try:
|
|
113
|
+
dev = _dev()
|
|
114
|
+
if not dev:
|
|
115
|
+
return
|
|
116
|
+
fb = os.path.join(BASE, 'workspace', 'members', dev, 'journal', 'feedback.jsonl')
|
|
117
|
+
os.makedirs(os.path.dirname(fb), exist_ok=True)
|
|
118
|
+
entry = {
|
|
119
|
+
'ts': datetime.now().isoformat(timespec='seconds'),
|
|
120
|
+
'dev': dev,
|
|
121
|
+
'event': event,
|
|
122
|
+
'data': data,
|
|
123
|
+
'_source': 'post-tool-use-hook', # 标记来源, 区别于 AI 主动埋点
|
|
124
|
+
}
|
|
125
|
+
with open(fb, 'a', encoding='utf-8') as f:
|
|
126
|
+
f.write(json.dumps(entry, ensure_ascii=False) + '\n')
|
|
127
|
+
except Exception:
|
|
128
|
+
pass # 铁律: 埋点失败绝不阻塞
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main():
|
|
132
|
+
try:
|
|
133
|
+
tool_input, tool_name = _read_tool_info()
|
|
134
|
+
tn = (tool_name or '').lower()
|
|
135
|
+
|
|
136
|
+
# 1. Write/Edit 落到 PRD / task.json → 埋点
|
|
137
|
+
if tn in WRITE_TOOLS or tn == 'bash':
|
|
138
|
+
paths = _file_paths_from_input(tool_input, tool_name)
|
|
139
|
+
for p in paths:
|
|
140
|
+
plow = p.lower().replace('\\', '/')
|
|
141
|
+
if 'prd' in os.path.basename(plow).lower() and plow.endswith('.md'):
|
|
142
|
+
_append_event('prd_written', {'prd': os.path.basename(p), 'tool': tool_name})
|
|
143
|
+
if plow.endswith('task.json'):
|
|
144
|
+
_append_event('task_touched', {'task_json': os.path.basename(os.path.dirname(p)), 'tool': tool_name})
|
|
145
|
+
# v3.1 软提示: prototype HTML 写完但缺来源锚定 → stderr 提醒(不阻断)
|
|
146
|
+
if 'prototype' in os.path.basename(plow) and plow.endswith('.html'):
|
|
147
|
+
try:
|
|
148
|
+
if os.path.isfile(p):
|
|
149
|
+
with open(p, encoding='utf-8', errors='ignore') as _f:
|
|
150
|
+
_ht = _f.read()[:8192]
|
|
151
|
+
import re as _re
|
|
152
|
+
if not _re.search(r'@prototype-source:|x-source-pages', _ht):
|
|
153
|
+
sys.stderr.write(
|
|
154
|
+
'[prototype 提醒] %s 缺来源锚定注释。\n'
|
|
155
|
+
' → 在 <head> 加一行: <!-- @prototype-source: fywl-ui/.../index.vue -->\n'
|
|
156
|
+
' → 没做这一步 = 没读真实代码就手抄模板, eval A2 会扣 5 分。\n' % os.path.basename(p))
|
|
157
|
+
except Exception:
|
|
158
|
+
pass # 软提示, 失败静默
|
|
159
|
+
|
|
160
|
+
# 2. Bash 跑了 commit/push → 兜底埋点 commit_done
|
|
161
|
+
if tn == 'bash':
|
|
162
|
+
cmd = _cmd_from_input(tool_input)
|
|
163
|
+
if cmd and COMMIT_RE.search(cmd):
|
|
164
|
+
_append_event('commit_done', {'source': 'post-tool-use-hook', 'cmd': cmd[:120]})
|
|
165
|
+
|
|
166
|
+
sys.exit(0) # 永远放行
|
|
167
|
+
except SystemExit:
|
|
168
|
+
raise
|
|
169
|
+
except Exception as e:
|
|
170
|
+
try:
|
|
171
|
+
os.makedirs(os.path.dirname(HOOK_ERRORS_LOG), exist_ok=True)
|
|
172
|
+
with open(HOOK_ERRORS_LOG, 'a', encoding='utf-8') as f:
|
|
173
|
+
f.write('[{}] post-tool-use: {}\n'.format(
|
|
174
|
+
datetime.now().strftime('%Y-%m-%d %H:%M:%S'), str(e)[:200]))
|
|
175
|
+
except Exception:
|
|
176
|
+
pass
|
|
177
|
+
sys.exit(0)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == '__main__':
|
|
181
|
+
main()
|
|
@@ -62,17 +62,8 @@ def _recent_hook_errors(hours=24, limit=3):
|
|
|
62
62
|
except Exception:
|
|
63
63
|
return []
|
|
64
64
|
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
'## CRITICAL RULE',
|
|
68
|
-
'When user asks for /wl-prd-full, ALWAYS ask platform FIRST:',
|
|
69
|
-
'"这个需求是针对哪个平台?',
|
|
70
|
-
' 1. Web 管理端 (fywl-ui) - Ant Design Vue + VxeGrid 风格',
|
|
71
|
-
' 2. APP 移动端 (Carmg-H5) - Vant 风格',
|
|
72
|
-
' 3. 两端都要',
|
|
73
|
-
' 请选择 (1/2/3):"',
|
|
74
|
-
'DO NOT skip this question. DO NOT auto-detect. ALWAYS ASK.',
|
|
75
|
-
])
|
|
65
|
+
# v3.0 M4: 删 CRITICAL_RULE (会话开始主动推销工作流铁律 → 非侵入式改造)
|
|
66
|
+
# 平台询问逻辑保留在 wl-pipeline.md 规则1 (命令启动后才生效, 不在会话开头强推)
|
|
76
67
|
|
|
77
68
|
|
|
78
69
|
def load_json_safe(path):
|
|
@@ -138,14 +129,14 @@ def get_index_info():
|
|
|
138
129
|
recent = errors[-10:] # 取最后 10 行检查
|
|
139
130
|
if len(recent) >= 3:
|
|
140
131
|
lines.append('[WARN] hook 最近频繁失败 (%d 条错误), 详情见 .qoder/.runtime/hook-errors.log' % len(recent))
|
|
141
|
-
lines.append(' 跑 python .qoder/scripts/init_doctor.py --fix 排查')
|
|
132
|
+
lines.append(' 跑 python .qoder/scripts/setup/init_doctor.py --fix 排查')
|
|
142
133
|
except OSError:
|
|
143
134
|
pass
|
|
144
135
|
|
|
145
136
|
lines.append('## Knowledge Graph')
|
|
146
137
|
lines.append('MCP工具 (QoderWork自动调用): search_code / get_impact / coverage_matrix / get_workflow 等11个')
|
|
147
|
-
lines.append(' python .qoder/scripts/search_index.py <keyword> [--platform web|app]')
|
|
148
|
-
lines.append(' python .qoder/scripts/context_pack.py <keyword> # 一次打包全部上下文')
|
|
138
|
+
lines.append(' python .qoder/scripts/io/search_index.py <keyword> [--platform web|app]')
|
|
139
|
+
lines.append(' python .qoder/scripts/io/context_pack.py <keyword> # 一次打包全部上下文')
|
|
149
140
|
|
|
150
141
|
# DuckDB 知识图谱状态检查
|
|
151
142
|
# Read role from .developer file
|
|
@@ -192,22 +183,23 @@ def get_index_info():
|
|
|
192
183
|
# 管理员/开发: 能构建
|
|
193
184
|
if code_newer or db_age_hours > 168: # 7天 = 168小时
|
|
194
185
|
lines.append(' [WARN] 知识图谱过期! 代码有新变更.')
|
|
195
|
-
lines.append(' → 管理员请运行: python .qoder/scripts/kg_build.py')
|
|
196
|
-
lines.append(' → 完成后: python .qoder/scripts/team_sync.py push (共享给团队)')
|
|
186
|
+
lines.append(' → 管理员请运行: python .qoder/scripts/kg/kg_build.py')
|
|
187
|
+
lines.append(' → 完成后: python .qoder/scripts/io/team_sync.py push (共享给团队)')
|
|
197
188
|
else:
|
|
198
189
|
lines.append(' ✓ 知识图谱最新')
|
|
199
190
|
else:
|
|
200
191
|
# 非管理员: 只拉取
|
|
201
192
|
if code_newer or db_age_hours > 168:
|
|
202
193
|
lines.append(' [WARN] 知识图谱可能过期.')
|
|
203
|
-
lines.append(' → 运行: python .qoder/scripts/team_sync.py pull (获取管理员最新构建)')
|
|
194
|
+
lines.append(' → 运行: python .qoder/scripts/io/team_sync.py pull (获取管理员最新构建)')
|
|
204
195
|
else:
|
|
205
196
|
lines.append(' ✓ 知识图谱最新')
|
|
206
197
|
|
|
207
198
|
# 检查是否有未标注的模块 (提示AI做语义标注)
|
|
208
199
|
try:
|
|
209
200
|
import subprocess as _sp
|
|
210
|
-
|
|
201
|
+
# v3.x: kg_semantic.py 已迁移到 domain/kg/graph/ (旧扁平路径不存在)
|
|
202
|
+
r = _sp.run([sys.executable, os.path.join(BASE, '.qoder', 'scripts', 'domain', 'kg', 'graph', 'kg_semantic.py'), '--check'],
|
|
211
203
|
capture_output=True, text=True, encoding='utf-8', timeout=5)
|
|
212
204
|
if r.stdout and 'semantic_labeling_needed' in r.stdout:
|
|
213
205
|
import json as _json
|
|
@@ -220,9 +212,9 @@ def get_index_info():
|
|
|
220
212
|
else:
|
|
221
213
|
lines.append(' [MISSING] kg.duckdb 不存在!')
|
|
222
214
|
if role in ('admin', 'dev'):
|
|
223
|
-
lines.append(' → 运行: python .qoder/scripts/kg_build.py')
|
|
215
|
+
lines.append(' → 运行: python .qoder/scripts/kg/kg_build.py')
|
|
224
216
|
else:
|
|
225
|
-
lines.append(' → 运行: python .qoder/scripts/team_sync.py pull')
|
|
217
|
+
lines.append(' → 运行: python .qoder/scripts/io/team_sync.py pull')
|
|
226
218
|
|
|
227
219
|
return NL.join(lines)
|
|
228
220
|
|
|
@@ -235,8 +227,8 @@ def get_style_info():
|
|
|
235
227
|
|
|
236
228
|
lines = []
|
|
237
229
|
lines.append('## UI Style Index')
|
|
238
|
-
lines.append('Style search: python .qoder/scripts/search_index.py --style <table|form|modal>')
|
|
239
|
-
lines.append('Field search: python .qoder/scripts/search_index.py --field <name>')
|
|
230
|
+
lines.append('Style search: python .qoder/scripts/io/search_index.py --style <table|form|modal>')
|
|
231
|
+
lines.append('Field search: python .qoder/scripts/io/search_index.py --field <name>')
|
|
240
232
|
|
|
241
233
|
for proj, d in meta.get('projects', {}).items():
|
|
242
234
|
vue = d.get('vue_files', 0)
|
|
@@ -254,6 +246,81 @@ def get_style_info():
|
|
|
254
246
|
return NL.join(lines)
|
|
255
247
|
|
|
256
248
|
|
|
249
|
+
def get_usability_summary():
|
|
250
|
+
"""读工作流可用性指标, 把"不达标"项注入会话上下文。
|
|
251
|
+
|
|
252
|
+
吸收宿主 SessionStart hook 能力: 不用等用户跑 /wl-status, 每次开会话主动
|
|
253
|
+
暴露"工作流用没用起来"的红灯项。只展示红/黄 (绿的不打扰), 无数据则不显示。
|
|
254
|
+
(与 usability_score.py 同源逻辑, 但 hook 内联精简版, 避免 import 验证脚本拖慢启动)
|
|
255
|
+
"""
|
|
256
|
+
lines = []
|
|
257
|
+
try:
|
|
258
|
+
# U2 一次过审率 (从 eval-history 取首条)
|
|
259
|
+
eval_path = os.path.join(BASE, 'data', 'learning', 'eval-history.jsonl')
|
|
260
|
+
records = []
|
|
261
|
+
if os.path.isfile(eval_path):
|
|
262
|
+
with open(eval_path, encoding='utf-8', errors='replace') as f:
|
|
263
|
+
for ln in f:
|
|
264
|
+
ln = ln.strip()
|
|
265
|
+
if ln:
|
|
266
|
+
try:
|
|
267
|
+
records.append(json.loads(ln))
|
|
268
|
+
except Exception:
|
|
269
|
+
continue
|
|
270
|
+
# 取最近 7 天
|
|
271
|
+
from datetime import datetime, timedelta
|
|
272
|
+
cutoff = datetime.now() - timedelta(days=7)
|
|
273
|
+
recent = []
|
|
274
|
+
for r in records:
|
|
275
|
+
t = r.get('time', '')
|
|
276
|
+
try:
|
|
277
|
+
if datetime.fromisoformat(str(t).split('.')[0]) >= cutoff:
|
|
278
|
+
recent.append(r)
|
|
279
|
+
except Exception:
|
|
280
|
+
continue
|
|
281
|
+
|
|
282
|
+
issues = [] # 只收红/黄
|
|
283
|
+
if recent:
|
|
284
|
+
passed = sum(1 for r in recent if r.get('passed'))
|
|
285
|
+
rate = 100.0 * passed / len(recent)
|
|
286
|
+
if rate < 60: # 黄线 30, 红线 60
|
|
287
|
+
tag = '🔴' if rate < 30 else '🟡'
|
|
288
|
+
issues.append('%s 一次过审率 %.0f%% (<60%% 目标) — PRD 质量需提升' % (tag, rate))
|
|
289
|
+
|
|
290
|
+
# U4 规则违反率 (从 feedback)
|
|
291
|
+
fb_dir = os.path.join(BASE, 'workspace', 'members')
|
|
292
|
+
violations = 0
|
|
293
|
+
prds_gen = 0
|
|
294
|
+
if os.path.isdir(fb_dir):
|
|
295
|
+
for dev in os.listdir(fb_dir):
|
|
296
|
+
fb = os.path.join(fb_dir, dev, 'journal', 'feedback.jsonl')
|
|
297
|
+
if not os.path.isfile(fb):
|
|
298
|
+
continue
|
|
299
|
+
try:
|
|
300
|
+
with open(fb, encoding='utf-8', errors='replace') as f:
|
|
301
|
+
for ln in f:
|
|
302
|
+
if '"rule_violation"' in ln:
|
|
303
|
+
violations += 1
|
|
304
|
+
elif '"prd_accepted"' in ln:
|
|
305
|
+
prds_gen += 1
|
|
306
|
+
except Exception:
|
|
307
|
+
continue
|
|
308
|
+
if prds_gen and violations:
|
|
309
|
+
vrate = 100.0 * violations / prds_gen
|
|
310
|
+
if vrate > 10:
|
|
311
|
+
tag = '🔴' if vrate > 25 else '🟡'
|
|
312
|
+
issues.append('%s 规则违反率 %.0f%% (>10%% 目标) — 关键规则没拦住' % (tag, vrate))
|
|
313
|
+
|
|
314
|
+
if issues:
|
|
315
|
+
lines.append('## Workflow Usability (近7天)')
|
|
316
|
+
lines.append('工作流"用不用得起来"红灯 (详情: wlkj.py usability):')
|
|
317
|
+
for it in issues:
|
|
318
|
+
lines.append(' ' + it)
|
|
319
|
+
except Exception:
|
|
320
|
+
pass # hook 失败绝不阻塞会话
|
|
321
|
+
return NL.join(lines) if lines else ''
|
|
322
|
+
|
|
323
|
+
|
|
257
324
|
def main():
|
|
258
325
|
parts = []
|
|
259
326
|
parts.append('<qoder-context>')
|
|
@@ -282,8 +349,12 @@ def main():
|
|
|
282
349
|
for t in tasks:
|
|
283
350
|
parts.append(' [' + t['status'] + '] ' + t['name'] + ' - ' + t['title'])
|
|
284
351
|
|
|
352
|
+
usability = get_usability_summary()
|
|
353
|
+
if usability:
|
|
354
|
+
parts.append('')
|
|
355
|
+
parts.append(usability)
|
|
356
|
+
|
|
285
357
|
parts.append('')
|
|
286
|
-
parts.append(CRITICAL_RULE)
|
|
287
358
|
|
|
288
359
|
idx_info = get_index_info()
|
|
289
360
|
if idx_info:
|