@hupan56/wlkj 2.2.4 → 2.2.6

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.
@@ -1,161 +1,231 @@
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>/ 目录下 (与 workspace_init.py 一致)
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 common.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 init_developer(name: str, role: str | None = None,
58
- repo_root: Path | None = None) -> bool:
59
- """初始化开发者身份。
60
-
61
- 创建以下内容:
62
- - .qoder/.developer 文件 (name= / role= / initialized_at=)
63
- - workspace/members/<name>/{journal,drafts,inbox}/ 目录
64
- - 初始日志文件 journal/journal-1.md
65
-
66
- Returns:
67
- 成功返回 True, 失败返回 False。
68
- """
69
- if not name:
70
- print("Error: developer name is required", file=sys.stderr)
71
- return False
72
-
73
- # 校验名称合法性 (允许字母数字、中文和部分符号)
74
- if not all(c.isalnum() or c in "-_" for c in name):
75
- print(f"Error: invalid developer name '{name}' (only alphanumeric, -, _ allowed)", file=sys.stderr)
76
- return False
77
-
78
- if repo_root is None:
79
- repo_root = get_repo_root()
80
-
81
- # 零信任: 切换身份时清除旧开发者的 current-task 指针
82
- # (防止 B 在 A 用过的机器上看到 A 的活跃任务, 误操作)
83
- try:
84
- from .paths import get_developer, set_current_task
85
- old_dev = get_developer(repo_root)
86
- if old_dev and old_dev != name:
87
- set_current_task(None, repo_root, developer=old_dev)
88
- print(f"Note: 切换开发者 {old_dev} -> {name}, 已清除 {old_dev} 的活跃任务指针")
89
- except Exception:
90
- pass # 清理失败不阻塞初始化
91
-
92
- # 1. 创建 .developer 文件
93
- if not write_developer_file(name, role, repo_root):
94
- return False
95
-
96
- # 2. 创建个人工作空间 workspace/members/<name>/
97
- personal = repo_root / "workspace" / "members" / name
98
- try:
99
- for sub in ("journal", "drafts", "inbox"):
100
- (personal / sub).mkdir(parents=True, exist_ok=True)
101
- except (OSError, IOError) as e:
102
- print(f"Error: Failed to create workspace directory: {e}", file=sys.stderr)
103
- return False
104
-
105
- # 3. 创建初始日志文件
106
- journal_file = personal / "journal" / "journal-1.md"
107
- if not journal_file.exists():
108
- today = datetime.now().strftime("%Y-%m-%d")
109
- try:
110
- journal_file.write_text(
111
- f"# Journal - {name}\n\n> Started: {today}\n\n---\n\n",
112
- encoding="utf-8"
113
- )
114
- except (OSError, IOError) as e:
115
- print(f"Error: Failed to create journal file: {e}", file=sys.stderr)
116
- return False
117
-
118
- print(f"Developer initialized: {name}" + (f" ({role})" if role else ""))
119
- print(f" Workspace: workspace/members/{name}/")
120
- print(f" Journal: workspace/members/{name}/journal/journal-1.md")
121
- return True
122
-
123
-
124
- # =============================================================================
125
- # 开发者校验
126
- # =============================================================================
127
-
128
- def ensure_developer(repo_root: Path | None = None) -> str:
129
- """确保开发者已初始化, 未初始化则打印提示并退出。"""
130
- developer = get_developer(repo_root)
131
- if not developer:
132
- print("Error: Developer not initialized.", file=sys.stderr)
133
- print("Run: python .qoder/scripts/workspace_init.py <your-name>", file=sys.stderr)
134
- sys.exit(1)
135
- return developer
136
-
137
-
138
- def show_developer_info(repo_root: Path | None = None) -> None:
139
- """显示当前开发者信息。"""
140
- from common.paths import get_developer_info, get_workspace_dir, get_active_journal_file
141
-
142
- info = get_developer_info(repo_root)
143
- if not info:
144
- print("Developer: not initialized")
145
- print("Run: python .qoder/scripts/workspace_init.py <your-name>")
146
- return
147
-
148
- print(f"Developer: {info.get('name', 'unknown')}")
149
- if info.get('role'):
150
- print(f"Role: {info['role']}")
151
- print(f"Initialized: {info.get('initialized_at', 'unknown')}")
152
-
153
- workspace = get_workspace_dir(repo_root)
154
- if workspace:
155
- print(f"Workspace: {workspace}")
156
-
157
- journal = get_active_journal_file(repo_root)
158
- if journal:
159
- from common.paths import count_lines
160
- lines = count_lines(journal)
161
- 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>/ 目录下 (与 workspace_init.py 一致)
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 common.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 已配置。未配则用 --local 设为 init 的名字。
77
+
78
+ 检测顺序: 全局 > local。都没有就提示用户。
79
+ """
80
+ import subprocess
81
+ # 先看 git 是否可用
82
+ try:
83
+ subprocess.run(["git", "--version"], capture_output=True, timeout=5)
84
+ except (FileNotFoundError, OSError):
85
+ print(" [INFO] git 未安装, 跳过 git 配置 (核心功能不依赖 git)")
86
+ return
87
+
88
+ author = _detect_git_author()
89
+ if author:
90
+ print(f" [OK] git 作者: {author}")
91
+ return
92
+
93
+ # git 未配 user.name/email: init 名字设 local config (不影响全局)
94
+ print(f" [设置] git 未配置 user.name/email, 用你的名字设为 local:")
95
+ fake_email = f"{name}@local"
96
+ try:
97
+ subprocess.run(["git", "config", "--local", "user.name", name],
98
+ cwd=str(repo_root), capture_output=True, timeout=5)
99
+ subprocess.run(["git", "config", "--local", "user.email", fake_email],
100
+ cwd=str(repo_root), capture_output=True, timeout=5)
101
+ print(f" user.name = {name}")
102
+ print(f" user.email = {fake_email}")
103
+ print(f" (仅本仓库生效。如有团队 git 账号, 自行改: git config --local user.email 你的邮箱)")
104
+ except (FileNotFoundError, OSError, subprocess.SubprocessError) as e:
105
+ print(f" [WARN] 设置 git config 失败 (不阻塞): {e}")
106
+ print(f" 请手动跑: git config user.name {name} && git config user.email 你的邮箱")
107
+
108
+
109
+ def init_developer(name: str, role: str | None = None,
110
+ repo_root: Path | None = None) -> bool:
111
+ """初始化开发者身份。
112
+
113
+ 创建以下内容:
114
+ - .qoder/.developer 文件 (name= / role= / initialized_at=)
115
+ - workspace/members/<name>/{journal,drafts,inbox}/ 目录
116
+ - 初始日志文件 journal/journal-1.md
117
+ - member.json + .signing_key (零信任身份注册, 用于 push 门禁)
118
+ - git user.name/email 检测与配置
119
+
120
+ Returns:
121
+ 成功返回 True, 失败返回 False。
122
+ """
123
+ if not name:
124
+ print("Error: developer name is required", file=sys.stderr)
125
+ return False
126
+
127
+ # 校验名称合法性 (允许字母数字、中文和部分符号)
128
+ if not all(c.isalnum() or c in "-_" for c in name):
129
+ print(f"Error: invalid developer name '{name}' (only alphanumeric, -, _ allowed)", file=sys.stderr)
130
+ return False
131
+
132
+ if repo_root is None:
133
+ repo_root = get_repo_root()
134
+
135
+ # 零信任: 切换身份时清除旧开发者的 current-task 指针
136
+ # (防止 B 在 A 用过的机器上看到 A 的活跃任务, 误操作)
137
+ try:
138
+ from .paths import get_developer, set_current_task
139
+ old_dev = get_developer(repo_root)
140
+ if old_dev and old_dev != name:
141
+ set_current_task(None, repo_root, developer=old_dev)
142
+ print(f"Note: 切换开发者 {old_dev} -> {name}, 已清除 {old_dev} 的活跃任务指针")
143
+ except Exception:
144
+ pass # 清理失败不阻塞初始化
145
+
146
+ # 1. 创建 .developer 文件
147
+ if not write_developer_file(name, role, repo_root):
148
+ return False
149
+
150
+ # 2. 创建个人工作空间 workspace/members/<name>/
151
+ personal = repo_root / "workspace" / "members" / name
152
+ try:
153
+ for sub in ("journal", "drafts", "inbox"):
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 common.identity import register_member, IdentityError
177
+ try:
178
+ register_member(name, role or "pm", git_author, repo_root)
179
+ print(f" [OK] 身份注册: member.json + 签名密钥已生成")
180
+ except IdentityError as e:
181
+ print(f" [WARN] 身份注册失败 (不阻塞): {e}")
182
+ except ImportError:
183
+ pass # identity 模块不可用, 跳过 (向后兼容)
184
+
185
+ # 5. 确保 git 配置 (检测 + 自动设 local)
186
+ _ensure_git_config(name, repo_root)
187
+
188
+ print(f"Developer initialized: {name}" + (f" ({role})" if role else ""))
189
+ print(f" Workspace: workspace/members/{name}/")
190
+ print(f" Journal: workspace/members/{name}/journal/journal-1.md")
191
+ return True
192
+
193
+
194
+ # =============================================================================
195
+ # 开发者校验
196
+ # =============================================================================
197
+
198
+ def ensure_developer(repo_root: Path | None = None) -> str:
199
+ """确保开发者已初始化, 未初始化则打印提示并退出。"""
200
+ developer = get_developer(repo_root)
201
+ if not developer:
202
+ print("Error: Developer not initialized.", file=sys.stderr)
203
+ print("Run: python .qoder/scripts/workspace_init.py <your-name>", file=sys.stderr)
204
+ sys.exit(1)
205
+ return developer
206
+
207
+
208
+ def show_developer_info(repo_root: Path | None = None) -> None:
209
+ """显示当前开发者信息。"""
210
+ from common.paths import get_developer_info, get_workspace_dir, get_active_journal_file
211
+
212
+ info = get_developer_info(repo_root)
213
+ if not info:
214
+ print("Developer: not initialized")
215
+ print("Run: python .qoder/scripts/workspace_init.py <your-name>")
216
+ return
217
+
218
+ print(f"Developer: {info.get('name', 'unknown')}")
219
+ if info.get('role'):
220
+ print(f"Role: {info['role']}")
221
+ print(f"Initialized: {info.get('initialized_at', 'unknown')}")
222
+
223
+ workspace = get_workspace_dir(repo_root)
224
+ if workspace:
225
+ print(f"Workspace: {workspace}")
226
+
227
+ journal = get_active_journal_file(repo_root)
228
+ if journal:
229
+ from common.paths import count_lines
230
+ lines = count_lines(journal)
231
+ print(f"Active Journal: {journal.name} ({lines} lines)")