@hupan56/wlkj 3.0.0 → 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 -1373
- package/package.json +1 -1
- package/templates/qoder/commands/optional/wl-insight.md +6 -4
- 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 +5 -5
- 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 +3 -3
- 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 +10 -10
- package/templates/qoder/config.yaml +35 -13
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +82 -1
- 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 +70 -8
- package/templates/qoder/scripts/README.md +78 -115
- 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/{report → deployment/setup}/__init__.py +11 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
- package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
- package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
- package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
- package/templates/qoder/scripts/domain/__init__.py +0 -0
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
- package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
- package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
- package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
- 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/{common → domain/kg/extract}/test_extract.py +115 -115
- 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 → domain/kg/graph}/kg_link_db.py +7 -2
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
- package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
- package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
- package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
- 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 → domain/kg/storage}/kg_duckdb.py +38 -14
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
- package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
- package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
- package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
- package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
- package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
- 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/{report → domain/requirement}/archive_prd.py +8 -2
- package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
- package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
- package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
- package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
- package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
- package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
- package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
- package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
- package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
- package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
- package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
- 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 +2 -2
- 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/{common → foundation/protocol}/mcp_base.py +1 -1
- 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/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
- 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/{report → validation/metrics}/eval_prd.py +17 -10
- 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/{test → validation/test}/autotest.py +64 -4
- package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
- package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
- package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
- package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
- package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
- package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +30 -0
- package/templates/qoder/skills/design-import/SKILL.md +22 -13
- package/templates/qoder/skills/design-review/SKILL.md +19 -10
- package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
- package/templates/qoder/skills/prd-review/SKILL.md +6 -4
- package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
- package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
- package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
- package/templates/qoder/skills/test-generator/SKILL.md +7 -5
- package/templates/qoder/skills/wl-code/SKILL.md +23 -11
- package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
- package/templates/qoder/skills/wl-design/SKILL.md +5 -3
- package/templates/qoder/skills/wl-init/SKILL.md +6 -6
- package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
- package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
- package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
- package/templates/qoder/skills/wl-report/SKILL.md +25 -23
- package/templates/qoder/skills/wl-search/SKILL.md +149 -78
- package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
- package/templates/qoder/skills/wl-status/SKILL.md +48 -16
- package/templates/qoder/skills/wl-task/SKILL.md +92 -10
- package/templates/qoder/skills/wl-test/SKILL.md +75 -29
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -236
- 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/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
- package/templates/.qoder/learning/eval-history.jsonl +0 -28
- package/templates/data/index/wiki-index.json +0 -8
- package/templates/qoder/nul +0 -4
- package/templates/qoder/scripts/kg/extract.py +0 -398
- package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
- package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
- package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
- package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
- package/templates/qoder/scripts/task/__init__.py +0 -11
- package/templates/qoder/scripts/test/__init__.py +0 -11
- package/templates/qoder/scripts/wlkj.py +0 -116
- /package/templates/qoder/scripts/{common → deployment}/__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/{test → validation/test}/secure-ls.js +0 -0
|
@@ -1,239 +1,239 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
QODER Pipeline - 开发者管理工具
|
|
5
|
-
|
|
6
|
-
提供开发者身份的初始化和管理功能:
|
|
7
|
-
- init_developer: 初始化开发者身份, 创建工作空间和日志
|
|
8
|
-
- ensure_developer: 确保开发者已初始化 (未初始化则退出)
|
|
9
|
-
- show_developer_info: 显示开发者信息
|
|
10
|
-
|
|
11
|
-
开发者身份存储在 .qoder/.developer 文件中 (gitignored)
|
|
12
|
-
工作空间在 workspace/members/<developer>/ 目录下
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import sys
|
|
18
|
-
from datetime import datetime
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
|
|
21
|
-
# 将 scripts 目录加入路径以便导入 common 模块
|
|
22
|
-
import os
|
|
23
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
24
|
-
|
|
25
|
-
from
|
|
26
|
-
DIR_WORKFLOW,
|
|
27
|
-
FILE_DEVELOPER,
|
|
28
|
-
MEMBERS_DIR,
|
|
29
|
-
get_repo_root,
|
|
30
|
-
get_developer,
|
|
31
|
-
check_developer,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# =============================================================================
|
|
36
|
-
# 开发者初始化
|
|
37
|
-
# =============================================================================
|
|
38
|
-
|
|
39
|
-
def write_developer_file(name: str, role: str | None = None,
|
|
40
|
-
repo_root: Path | None = None) -> bool:
|
|
41
|
-
"""写入 .qoder/.developer (规范格式: name=<name>)。"""
|
|
42
|
-
if repo_root is None:
|
|
43
|
-
repo_root = get_repo_root()
|
|
44
|
-
dev_file = repo_root / DIR_WORKFLOW / FILE_DEVELOPER
|
|
45
|
-
lines = [f"name={name}"]
|
|
46
|
-
if role:
|
|
47
|
-
lines.append(f"role={role}")
|
|
48
|
-
lines.append(f"initialized_at={datetime.now().isoformat()}")
|
|
49
|
-
try:
|
|
50
|
-
dev_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
51
|
-
return True
|
|
52
|
-
except (OSError, IOError) as e:
|
|
53
|
-
print(f"Error: Failed to create .developer file: {e}", file=sys.stderr)
|
|
54
|
-
return False
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _detect_git_author() -> str | None:
|
|
58
|
-
"""探测系统 git config 的 user.name <user.email>。git 未装或未配返回 None。"""
|
|
59
|
-
import subprocess
|
|
60
|
-
try:
|
|
61
|
-
rn = subprocess.run(["git", "config", "user.name"],
|
|
62
|
-
capture_output=True, text=True, timeout=5)
|
|
63
|
-
re_ = subprocess.run(["git", "config", "user.email"],
|
|
64
|
-
capture_output=True, text=True, timeout=5)
|
|
65
|
-
if rn.returncode == 0 and re_.returncode == 0:
|
|
66
|
-
gname = rn.stdout.strip()
|
|
67
|
-
gemail = re_.stdout.strip()
|
|
68
|
-
if gname and gemail:
|
|
69
|
-
return f"{gname} <{gemail}>"
|
|
70
|
-
except (FileNotFoundError, OSError, subprocess.SubprocessError):
|
|
71
|
-
pass # git 未装
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def _ensure_git_config(name: str, repo_root: Path) -> None:
|
|
76
|
-
"""确认 git user.name/email 已配置。未配则提示 (绝不自动造假账号)。
|
|
77
|
-
|
|
78
|
-
旧逻辑会塞 name@local 假账号, 与零信任 push 门禁冲突, 已废弃。
|
|
79
|
-
真正的 git 配置由 setup.py configure_git_identity() 在 init 前完成。
|
|
80
|
-
这里只做兜底检测: 如果走到这还没配, 提示用户去配。
|
|
81
|
-
"""
|
|
82
|
-
import subprocess
|
|
83
|
-
# 先看 git 是否可用
|
|
84
|
-
try:
|
|
85
|
-
subprocess.run(["git", "--version"], capture_output=True, timeout=5)
|
|
86
|
-
except (FileNotFoundError, OSError):
|
|
87
|
-
print(" [INFO] git 未安装, 跳过 git 配置 (核心功能不依赖 git)")
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
author = _detect_git_author()
|
|
91
|
-
if author:
|
|
92
|
-
# 检测到疑似假账号也要提示 (虽然配了, 但 push 会被门禁拒)
|
|
93
|
-
if "@local" in author:
|
|
94
|
-
print(f" [WARN] git 作者疑似假账号: {author} (push 门禁会拒绝)")
|
|
95
|
-
print(f" 请重跑 'wlkj init' 配置真实团队 git 账号")
|
|
96
|
-
else:
|
|
97
|
-
print(f" [OK] git 作者: {author}")
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
# git 未配: 不造假, 只提示
|
|
101
|
-
print(f" [WARN] git 未配置 user.name/email")
|
|
102
|
-
print(f" push/commit 会失败。请配置团队 git 账号:")
|
|
103
|
-
print(f" git config --global user.name '你的名字'")
|
|
104
|
-
print(f" git config --global user.email '你的团队邮箱'")
|
|
105
|
-
print(f" 或重跑: wlkj init <你的名字>")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def init_developer(name: str, role: str | None = None,
|
|
109
|
-
repo_root: Path | None = None) -> bool:
|
|
110
|
-
"""初始化开发者身份。
|
|
111
|
-
|
|
112
|
-
创建以下内容:
|
|
113
|
-
- .qoder/.developer 文件 (name= / role= / initialized_at=)
|
|
114
|
-
- workspace/members/<name>/{journal,drafts,inbox}/ 目录
|
|
115
|
-
- 初始日志文件 journal/journal-1.md
|
|
116
|
-
- member.json + .signing_key (零信任身份注册, 用于 push 门禁)
|
|
117
|
-
- git user.name/email 检测与配置
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
成功返回 True, 失败返回 False。
|
|
121
|
-
"""
|
|
122
|
-
if not name:
|
|
123
|
-
print("Error: developer name is required", file=sys.stderr)
|
|
124
|
-
return False
|
|
125
|
-
|
|
126
|
-
# 校验名称合法性 (允许字母数字、中文和部分符号)
|
|
127
|
-
if not all(c.isalnum() or c in "-_" for c in name):
|
|
128
|
-
print(f"Error: invalid developer name '{name}' (only alphanumeric, -, _ allowed)", file=sys.stderr)
|
|
129
|
-
return False
|
|
130
|
-
|
|
131
|
-
if repo_root is None:
|
|
132
|
-
repo_root = get_repo_root()
|
|
133
|
-
|
|
134
|
-
# 零信任: 切换身份时清除旧开发者的 current-task 指针
|
|
135
|
-
# (防止 B 在 A 用过的机器上看到 A 的活跃任务, 误操作)
|
|
136
|
-
try:
|
|
137
|
-
from .paths import get_developer, set_current_task
|
|
138
|
-
old_dev = get_developer(repo_root)
|
|
139
|
-
if old_dev and old_dev != name:
|
|
140
|
-
set_current_task(None, repo_root, developer=old_dev)
|
|
141
|
-
print(f"Note: 切换开发者 {old_dev} -> {name}, 已清除 {old_dev} 的活跃任务指针")
|
|
142
|
-
except Exception:
|
|
143
|
-
pass # 清理失败不阻塞初始化
|
|
144
|
-
|
|
145
|
-
# 1. 创建 .developer 文件
|
|
146
|
-
if not write_developer_file(name, role, repo_root):
|
|
147
|
-
return False
|
|
148
|
-
|
|
149
|
-
# 2. 创建个人工作空间 workspace/members/<name>/
|
|
150
|
-
# v3.0 M3: 个人目录 = member.json + drafts + outputs + journal + .private
|
|
151
|
-
personal = repo_root / "workspace" / "members" / name
|
|
152
|
-
try:
|
|
153
|
-
for sub in ("journal", "drafts", "outputs", ".private", ".private/secrets"):
|
|
154
|
-
(personal / sub).mkdir(parents=True, exist_ok=True)
|
|
155
|
-
except (OSError, IOError) as e:
|
|
156
|
-
print(f"Error: Failed to create workspace directory: {e}", file=sys.stderr)
|
|
157
|
-
return False
|
|
158
|
-
|
|
159
|
-
# 3. 创建初始日志文件
|
|
160
|
-
journal_file = personal / "journal" / "journal-1.md"
|
|
161
|
-
if not journal_file.exists():
|
|
162
|
-
today = datetime.now().strftime("%Y-%m-%d")
|
|
163
|
-
try:
|
|
164
|
-
journal_file.write_text(
|
|
165
|
-
f"# Journal - {name}\n\n> Started: {today}\n\n---\n\n",
|
|
166
|
-
encoding="utf-8"
|
|
167
|
-
)
|
|
168
|
-
except (OSError, IOError) as e:
|
|
169
|
-
print(f"Error: Failed to create journal file: {e}", file=sys.stderr)
|
|
170
|
-
return False
|
|
171
|
-
|
|
172
|
-
# 4. 零信任身份注册 (写 member.json + 签名密钥 + git_author)
|
|
173
|
-
# 这是 push 门禁的前提: 没 member.json → syncgate 拒绝 push
|
|
174
|
-
git_author = _detect_git_author()
|
|
175
|
-
try:
|
|
176
|
-
from
|
|
177
|
-
# 修正: 旧 member.json 若是假账号(@local), 且现在 git 配了真账号 → 用真账号覆盖
|
|
178
|
-
# register_member 已有"已注册则只更新 git_author, 密钥不动"的逻辑, 这里只加提示
|
|
179
|
-
existing_member = get_member(name, repo_root)
|
|
180
|
-
if existing_member:
|
|
181
|
-
old_author = existing_member.get("git_author", "")
|
|
182
|
-
if (old_author and "@local" in old_author
|
|
183
|
-
and git_author and "@local" not in git_author):
|
|
184
|
-
print(f" [修正] member.json git_author 从假账号更新为真账号: {git_author}")
|
|
185
|
-
try:
|
|
186
|
-
register_member(name, role or "pm", git_author, repo_root)
|
|
187
|
-
print(f" [OK] 身份注册: member.json + 签名密钥已生成")
|
|
188
|
-
except IdentityError as e:
|
|
189
|
-
print(f" [WARN] 身份注册失败 (不阻塞): {e}")
|
|
190
|
-
except ImportError:
|
|
191
|
-
pass # identity 模块不可用, 跳过 (向后兼容)
|
|
192
|
-
|
|
193
|
-
# 5. 确保 git 配置 (只检测+提示, 不造假; 真账号由 setup.py 在 init 前配好)
|
|
194
|
-
_ensure_git_config(name, repo_root)
|
|
195
|
-
|
|
196
|
-
print(f"Developer initialized: {name}" + (f" ({role})" if role else ""))
|
|
197
|
-
print(f" Workspace: workspace/members/{name}/")
|
|
198
|
-
print(f" Journal: workspace/members/{name}/journal/journal-1.md")
|
|
199
|
-
return True
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
# =============================================================================
|
|
203
|
-
# 开发者校验
|
|
204
|
-
# =============================================================================
|
|
205
|
-
|
|
206
|
-
def ensure_developer(repo_root: Path | None = None) -> str:
|
|
207
|
-
"""确保开发者已初始化, 未初始化则打印提示并退出。"""
|
|
208
|
-
developer = get_developer(repo_root)
|
|
209
|
-
if not developer:
|
|
210
|
-
print("Error: Developer not initialized.", file=sys.stderr)
|
|
211
|
-
print("Run:
|
|
212
|
-
sys.exit(1)
|
|
213
|
-
return developer
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def show_developer_info(repo_root: Path | None = None) -> None:
|
|
217
|
-
"""显示当前开发者信息。"""
|
|
218
|
-
from
|
|
219
|
-
|
|
220
|
-
info = get_developer_info(repo_root)
|
|
221
|
-
if not info:
|
|
222
|
-
print("Developer: not initialized")
|
|
223
|
-
print("Run:
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
print(f"Developer: {info.get('name', 'unknown')}")
|
|
227
|
-
if info.get('role'):
|
|
228
|
-
print(f"Role: {info['role']}")
|
|
229
|
-
print(f"Initialized: {info.get('initialized_at', 'unknown')}")
|
|
230
|
-
|
|
231
|
-
workspace = get_workspace_dir(repo_root)
|
|
232
|
-
if workspace:
|
|
233
|
-
print(f"Workspace: {workspace}")
|
|
234
|
-
|
|
235
|
-
journal = get_active_journal_file(repo_root)
|
|
236
|
-
if journal:
|
|
237
|
-
from
|
|
238
|
-
lines = count_lines(journal)
|
|
239
|
-
print(f"Active Journal: {journal.name} ({lines} lines)")
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
QODER Pipeline - 开发者管理工具
|
|
5
|
+
|
|
6
|
+
提供开发者身份的初始化和管理功能:
|
|
7
|
+
- init_developer: 初始化开发者身份, 创建工作空间和日志
|
|
8
|
+
- ensure_developer: 确保开发者已初始化 (未初始化则退出)
|
|
9
|
+
- show_developer_info: 显示开发者信息
|
|
10
|
+
|
|
11
|
+
开发者身份存储在 .qoder/.developer 文件中 (gitignored)
|
|
12
|
+
工作空间在 workspace/members/<developer>/ 目录下
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# 将 scripts 目录加入路径以便导入 common 模块
|
|
22
|
+
import os
|
|
23
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
24
|
+
|
|
25
|
+
from foundation.core.paths import (
|
|
26
|
+
DIR_WORKFLOW,
|
|
27
|
+
FILE_DEVELOPER,
|
|
28
|
+
MEMBERS_DIR,
|
|
29
|
+
get_repo_root,
|
|
30
|
+
get_developer,
|
|
31
|
+
check_developer,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# 开发者初始化
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
def write_developer_file(name: str, role: str | None = None,
|
|
40
|
+
repo_root: Path | None = None) -> bool:
|
|
41
|
+
"""写入 .qoder/.developer (规范格式: name=<name>)。"""
|
|
42
|
+
if repo_root is None:
|
|
43
|
+
repo_root = get_repo_root()
|
|
44
|
+
dev_file = repo_root / DIR_WORKFLOW / FILE_DEVELOPER
|
|
45
|
+
lines = [f"name={name}"]
|
|
46
|
+
if role:
|
|
47
|
+
lines.append(f"role={role}")
|
|
48
|
+
lines.append(f"initialized_at={datetime.now().isoformat()}")
|
|
49
|
+
try:
|
|
50
|
+
dev_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
51
|
+
return True
|
|
52
|
+
except (OSError, IOError) as e:
|
|
53
|
+
print(f"Error: Failed to create .developer file: {e}", file=sys.stderr)
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _detect_git_author() -> str | None:
|
|
58
|
+
"""探测系统 git config 的 user.name <user.email>。git 未装或未配返回 None。"""
|
|
59
|
+
import subprocess
|
|
60
|
+
try:
|
|
61
|
+
rn = subprocess.run(["git", "config", "user.name"],
|
|
62
|
+
capture_output=True, text=True, timeout=5)
|
|
63
|
+
re_ = subprocess.run(["git", "config", "user.email"],
|
|
64
|
+
capture_output=True, text=True, timeout=5)
|
|
65
|
+
if rn.returncode == 0 and re_.returncode == 0:
|
|
66
|
+
gname = rn.stdout.strip()
|
|
67
|
+
gemail = re_.stdout.strip()
|
|
68
|
+
if gname and gemail:
|
|
69
|
+
return f"{gname} <{gemail}>"
|
|
70
|
+
except (FileNotFoundError, OSError, subprocess.SubprocessError):
|
|
71
|
+
pass # git 未装
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _ensure_git_config(name: str, repo_root: Path) -> None:
|
|
76
|
+
"""确认 git user.name/email 已配置。未配则提示 (绝不自动造假账号)。
|
|
77
|
+
|
|
78
|
+
旧逻辑会塞 name@local 假账号, 与零信任 push 门禁冲突, 已废弃。
|
|
79
|
+
真正的 git 配置由 setup.py configure_git_identity() 在 init 前完成。
|
|
80
|
+
这里只做兜底检测: 如果走到这还没配, 提示用户去配。
|
|
81
|
+
"""
|
|
82
|
+
import subprocess
|
|
83
|
+
# 先看 git 是否可用
|
|
84
|
+
try:
|
|
85
|
+
subprocess.run(["git", "--version"], capture_output=True, timeout=5)
|
|
86
|
+
except (FileNotFoundError, OSError):
|
|
87
|
+
print(" [INFO] git 未安装, 跳过 git 配置 (核心功能不依赖 git)")
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
author = _detect_git_author()
|
|
91
|
+
if author:
|
|
92
|
+
# 检测到疑似假账号也要提示 (虽然配了, 但 push 会被门禁拒)
|
|
93
|
+
if "@local" in author:
|
|
94
|
+
print(f" [WARN] git 作者疑似假账号: {author} (push 门禁会拒绝)")
|
|
95
|
+
print(f" 请重跑 'wlkj init' 配置真实团队 git 账号")
|
|
96
|
+
else:
|
|
97
|
+
print(f" [OK] git 作者: {author}")
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# git 未配: 不造假, 只提示
|
|
101
|
+
print(f" [WARN] git 未配置 user.name/email")
|
|
102
|
+
print(f" push/commit 会失败。请配置团队 git 账号:")
|
|
103
|
+
print(f" git config --global user.name '你的名字'")
|
|
104
|
+
print(f" git config --global user.email '你的团队邮箱'")
|
|
105
|
+
print(f" 或重跑: wlkj init <你的名字>")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def init_developer(name: str, role: str | None = None,
|
|
109
|
+
repo_root: Path | None = None) -> bool:
|
|
110
|
+
"""初始化开发者身份。
|
|
111
|
+
|
|
112
|
+
创建以下内容:
|
|
113
|
+
- .qoder/.developer 文件 (name= / role= / initialized_at=)
|
|
114
|
+
- workspace/members/<name>/{journal,drafts,inbox}/ 目录
|
|
115
|
+
- 初始日志文件 journal/journal-1.md
|
|
116
|
+
- member.json + .signing_key (零信任身份注册, 用于 push 门禁)
|
|
117
|
+
- git user.name/email 检测与配置
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
成功返回 True, 失败返回 False。
|
|
121
|
+
"""
|
|
122
|
+
if not name:
|
|
123
|
+
print("Error: developer name is required", file=sys.stderr)
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
# 校验名称合法性 (允许字母数字、中文和部分符号)
|
|
127
|
+
if not all(c.isalnum() or c in "-_" for c in name):
|
|
128
|
+
print(f"Error: invalid developer name '{name}' (only alphanumeric, -, _ allowed)", file=sys.stderr)
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
if repo_root is None:
|
|
132
|
+
repo_root = get_repo_root()
|
|
133
|
+
|
|
134
|
+
# 零信任: 切换身份时清除旧开发者的 current-task 指针
|
|
135
|
+
# (防止 B 在 A 用过的机器上看到 A 的活跃任务, 误操作)
|
|
136
|
+
try:
|
|
137
|
+
from foundation.core.paths import get_developer, set_current_task
|
|
138
|
+
old_dev = get_developer(repo_root)
|
|
139
|
+
if old_dev and old_dev != name:
|
|
140
|
+
set_current_task(None, repo_root, developer=old_dev)
|
|
141
|
+
print(f"Note: 切换开发者 {old_dev} -> {name}, 已清除 {old_dev} 的活跃任务指针")
|
|
142
|
+
except Exception:
|
|
143
|
+
pass # 清理失败不阻塞初始化
|
|
144
|
+
|
|
145
|
+
# 1. 创建 .developer 文件
|
|
146
|
+
if not write_developer_file(name, role, repo_root):
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
# 2. 创建个人工作空间 workspace/members/<name>/
|
|
150
|
+
# v3.0 M3: 个人目录 = member.json + drafts + outputs + journal + .private
|
|
151
|
+
personal = repo_root / "workspace" / "members" / name
|
|
152
|
+
try:
|
|
153
|
+
for sub in ("journal", "drafts", "outputs", ".private", ".private/secrets"):
|
|
154
|
+
(personal / sub).mkdir(parents=True, exist_ok=True)
|
|
155
|
+
except (OSError, IOError) as e:
|
|
156
|
+
print(f"Error: Failed to create workspace directory: {e}", file=sys.stderr)
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
# 3. 创建初始日志文件
|
|
160
|
+
journal_file = personal / "journal" / "journal-1.md"
|
|
161
|
+
if not journal_file.exists():
|
|
162
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
163
|
+
try:
|
|
164
|
+
journal_file.write_text(
|
|
165
|
+
f"# Journal - {name}\n\n> Started: {today}\n\n---\n\n",
|
|
166
|
+
encoding="utf-8"
|
|
167
|
+
)
|
|
168
|
+
except (OSError, IOError) as e:
|
|
169
|
+
print(f"Error: Failed to create journal file: {e}", file=sys.stderr)
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# 4. 零信任身份注册 (写 member.json + 签名密钥 + git_author)
|
|
173
|
+
# 这是 push 门禁的前提: 没 member.json → syncgate 拒绝 push
|
|
174
|
+
git_author = _detect_git_author()
|
|
175
|
+
try:
|
|
176
|
+
from foundation.identity.identity import register_member, IdentityError, get_member
|
|
177
|
+
# 修正: 旧 member.json 若是假账号(@local), 且现在 git 配了真账号 → 用真账号覆盖
|
|
178
|
+
# register_member 已有"已注册则只更新 git_author, 密钥不动"的逻辑, 这里只加提示
|
|
179
|
+
existing_member = get_member(name, repo_root)
|
|
180
|
+
if existing_member:
|
|
181
|
+
old_author = existing_member.get("git_author", "")
|
|
182
|
+
if (old_author and "@local" in old_author
|
|
183
|
+
and git_author and "@local" not in git_author):
|
|
184
|
+
print(f" [修正] member.json git_author 从假账号更新为真账号: {git_author}")
|
|
185
|
+
try:
|
|
186
|
+
register_member(name, role or "pm", git_author, repo_root)
|
|
187
|
+
print(f" [OK] 身份注册: member.json + 签名密钥已生成")
|
|
188
|
+
except IdentityError as e:
|
|
189
|
+
print(f" [WARN] 身份注册失败 (不阻塞): {e}")
|
|
190
|
+
except ImportError:
|
|
191
|
+
pass # identity 模块不可用, 跳过 (向后兼容)
|
|
192
|
+
|
|
193
|
+
# 5. 确保 git 配置 (只检测+提示, 不造假; 真账号由 setup.py 在 init 前配好)
|
|
194
|
+
_ensure_git_config(name, repo_root)
|
|
195
|
+
|
|
196
|
+
print(f"Developer initialized: {name}" + (f" ({role})" if role else ""))
|
|
197
|
+
print(f" Workspace: workspace/members/{name}/")
|
|
198
|
+
print(f" Journal: workspace/members/{name}/journal/journal-1.md")
|
|
199
|
+
return True
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# =============================================================================
|
|
203
|
+
# 开发者校验
|
|
204
|
+
# =============================================================================
|
|
205
|
+
|
|
206
|
+
def ensure_developer(repo_root: Path | None = None) -> str:
|
|
207
|
+
"""确保开发者已初始化, 未初始化则打印提示并退出。"""
|
|
208
|
+
developer = get_developer(repo_root)
|
|
209
|
+
if not developer:
|
|
210
|
+
print("Error: Developer not initialized.", file=sys.stderr)
|
|
211
|
+
print("Run: npx @hupan56/wlkj init <your-name>", file=sys.stderr)
|
|
212
|
+
sys.exit(1)
|
|
213
|
+
return developer
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def show_developer_info(repo_root: Path | None = None) -> None:
|
|
217
|
+
"""显示当前开发者信息。"""
|
|
218
|
+
from foundation.core.paths import get_developer_info, get_workspace_dir, get_active_journal_file
|
|
219
|
+
|
|
220
|
+
info = get_developer_info(repo_root)
|
|
221
|
+
if not info:
|
|
222
|
+
print("Developer: not initialized")
|
|
223
|
+
print("Run: npx @hupan56/wlkj init <your-name>")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
print(f"Developer: {info.get('name', 'unknown')}")
|
|
227
|
+
if info.get('role'):
|
|
228
|
+
print(f"Role: {info['role']}")
|
|
229
|
+
print(f"Initialized: {info.get('initialized_at', 'unknown')}")
|
|
230
|
+
|
|
231
|
+
workspace = get_workspace_dir(repo_root)
|
|
232
|
+
if workspace:
|
|
233
|
+
print(f"Workspace: {workspace}")
|
|
234
|
+
|
|
235
|
+
journal = get_active_journal_file(repo_root)
|
|
236
|
+
if journal:
|
|
237
|
+
from foundation.core.paths import count_lines
|
|
238
|
+
lines = count_lines(journal)
|
|
239
|
+
print(f"Active Journal: {journal.name} ({lines} lines)")
|
|
@@ -22,7 +22,7 @@ guard.py - 角色能力边界守卫 (解耦的执行点)
|
|
|
22
22
|
当前角色名 (identity.get_role 的便捷封装)
|
|
23
23
|
|
|
24
24
|
Usage:
|
|
25
|
-
from
|
|
25
|
+
from foundation.identity.guard import check_produce
|
|
26
26
|
r = check_produce('unit-test')
|
|
27
27
|
if not r.ok:
|
|
28
28
|
print(r.hint) # "你的角色 pm 不能产出 unit-test, 需 dev/test 角色"
|
|
@@ -34,7 +34,7 @@ import os
|
|
|
34
34
|
import sys
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
|
-
from .result import Result, CODE
|
|
37
|
+
from foundation.data.result import Result, CODE
|
|
38
38
|
from .identity import get_role, can_produce, can_consume, get_role_config
|
|
39
39
|
except ImportError:
|
|
40
40
|
_COMMON_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -41,8 +41,8 @@ from typing import Optional, Dict, Any, Union
|
|
|
41
41
|
|
|
42
42
|
# 相对导入兄弟模块
|
|
43
43
|
try:
|
|
44
|
-
from .atomicio import atomic_write_json, safe_read_json
|
|
45
|
-
from .paths import get_repo_root, MEMBERS_DIR
|
|
44
|
+
from foundation.io.atomicio import atomic_write_json, safe_read_json
|
|
45
|
+
from foundation.core.paths import get_repo_root, MEMBERS_DIR
|
|
46
46
|
except ImportError:
|
|
47
47
|
_COMMON_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
48
48
|
if _COMMON_DIR not in sys.path:
|
|
@@ -52,7 +52,7 @@ except ImportError:
|
|
|
52
52
|
_SCRIPTS_DIR = os.path.dirname(_COMMON_DIR)
|
|
53
53
|
if _SCRIPTS_DIR not in sys.path:
|
|
54
54
|
sys.path.insert(0, _SCRIPTS_DIR)
|
|
55
|
-
from
|
|
55
|
+
from foundation.core.paths import get_repo_root, MEMBERS_DIR
|
|
56
56
|
|
|
57
57
|
__all__ = [
|
|
58
58
|
"IdentityError",
|
|
@@ -105,7 +105,7 @@ def _member_json(name: str, repo_root: Optional[Union[str, Path]] = None) -> Pat
|
|
|
105
105
|
def _key_file(name: str, repo_root: Optional[Union[str, Path]] = None) -> Path:
|
|
106
106
|
"""签名密钥路径 (v3.0: 收进 .private/signing_key, 向后兼容老 .signing_key)。"""
|
|
107
107
|
try:
|
|
108
|
-
from .paths import get_private_file
|
|
108
|
+
from foundation.core.paths import get_private_file
|
|
109
109
|
return get_private_file("signing_key", name)
|
|
110
110
|
except Exception:
|
|
111
111
|
return _member_dir(name, repo_root) / ".signing_key"
|
|
@@ -204,6 +204,10 @@ def register_member(
|
|
|
204
204
|
or "%s <unknown@local>" % name
|
|
205
205
|
),
|
|
206
206
|
"joined_at": joined_at,
|
|
207
|
+
# 禅道账号映射 (v3.1.3): 本地 developer 名(如 hupan56) ↔ 禅道 account(如 hupan)
|
|
208
|
+
# 不一致时, zentao_sync 靠此过滤归属 (见 contracts/isolation.md「禅道同步」)。
|
|
209
|
+
# 首次注册留空 (None), 成员后续填; 已注册的保留原值不覆盖。
|
|
210
|
+
"zentao_account": (existing.get("zentao_account") if existing else None),
|
|
207
211
|
}
|
|
208
212
|
atomic_write_json(mj, record)
|
|
209
213
|
return record
|
|
@@ -272,11 +276,11 @@ def require_member(name: Optional[str] = None, repo_root: Optional[Union[str, Pa
|
|
|
272
276
|
"""
|
|
273
277
|
if name is None:
|
|
274
278
|
try:
|
|
275
|
-
from .
|
|
279
|
+
from foundation.core.paths import get_developer
|
|
276
280
|
except ImportError:
|
|
277
281
|
_this = os.path.dirname(os.path.abspath(__file__))
|
|
278
282
|
sys.path.insert(0, _this)
|
|
279
|
-
from paths import get_developer # type: ignore
|
|
283
|
+
from foundation.core.paths import get_developer # type: ignore
|
|
280
284
|
name = get_developer(repo_root)
|
|
281
285
|
if not name:
|
|
282
286
|
sys.stderr.write("[authz] 拒绝: 未设置开发者身份。先跑 /wl-init 注册。\n")
|
|
@@ -292,10 +296,10 @@ def require_member(name: Optional[str] = None, repo_root: Optional[Union[str, Pa
|
|
|
292
296
|
def ensure_current_member(repo_root: Optional[Union[str, Path]] = None) -> Optional[str]:
|
|
293
297
|
"""软检查: 返回当前已验证的成员名, 或 None (不 exit)。给非破坏性命令用。"""
|
|
294
298
|
try:
|
|
295
|
-
from paths import get_developer
|
|
299
|
+
from foundation.core.paths import get_developer
|
|
296
300
|
except ImportError:
|
|
297
301
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
298
|
-
from paths import get_developer # type: ignore
|
|
302
|
+
from foundation.core.paths import get_developer # type: ignore
|
|
299
303
|
name = get_developer(repo_root)
|
|
300
304
|
if name and verify_member(name, repo_root):
|
|
301
305
|
return name
|
|
@@ -341,10 +345,10 @@ def get_role(name: Optional[str] = None, repo_root: Optional[Union[str, Path]] =
|
|
|
341
345
|
if not name:
|
|
342
346
|
# 从 .developer 读
|
|
343
347
|
try:
|
|
344
|
-
from paths import get_developer, get_developer_info
|
|
348
|
+
from foundation.core.paths import get_developer, get_developer_info
|
|
345
349
|
except ImportError:
|
|
346
350
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
347
|
-
from paths import get_developer, get_developer_info # type: ignore
|
|
351
|
+
from foundation.core.paths import get_developer, get_developer_info # type: ignore
|
|
348
352
|
info = get_developer_info(repo_root) if "get_developer_info" in dir() else None
|
|
349
353
|
if isinstance(info, dict) and info.get("role"):
|
|
350
354
|
return info["role"]
|
|
@@ -11,7 +11,7 @@ kg_mcp_server.py(:249-276) 逐字重复。改一处忘改另一处 → CLI 和 M
|
|
|
11
11
|
让不同角色只看到与其职责相关的段落, 减少噪音。
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
|
-
from
|
|
14
|
+
from foundation.identity.roles import filter_by_role, ROLE_SECTIONS
|
|
15
15
|
text = filter_by_role(context_pack_output, role='pm')
|
|
16
16
|
"""
|
|
17
17
|
|
|
File without changes
|