@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.
package/package.json CHANGED
@@ -1,28 +1,28 @@
1
- {
2
- "name": "@hupan56/wlkj",
3
- "version": "2.2.4",
4
- "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
- "bin": {
6
- "wlkj": "bin/cli.js"
7
- },
8
- "files": [
9
- "bin/",
10
- "templates/"
11
- ],
12
- "keywords": [
13
- "workflow",
14
- "ai",
15
- "prd",
16
- "pipeline",
17
- "qoder",
18
- "product",
19
- "team"
20
- ],
21
- "license": "MIT",
22
- "publishConfig": {
23
- "access": "public"
24
- },
25
- "engines": {
26
- "node": ">=16"
27
- }
28
- }
1
+ {
2
+ "name": "@hupan56/wlkj",
3
+ "version": "2.2.6",
4
+ "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
+ "bin": {
6
+ "wlkj": "bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "templates/"
11
+ ],
12
+ "keywords": [
13
+ "workflow",
14
+ "ai",
15
+ "prd",
16
+ "pipeline",
17
+ "qoder",
18
+ "product",
19
+ "team"
20
+ ],
21
+ "license": "MIT",
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "engines": {
26
+ "node": ">=16"
27
+ }
28
+ }
@@ -1,117 +1,117 @@
1
- # inject-workflow-state.py - Inject workflow state on every message
2
- import os, json, sys
3
-
4
- if sys.platform == 'win32':
5
- try:
6
- sys.stdout.reconfigure(encoding='utf-8')
7
- except Exception:
8
- pass
9
-
10
- NL = chr(10)
11
- BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12
-
13
- # mtime 缓存 (审计 H5: 每条用户消息都跑这个 hook, 缓存避免重复读盘)
14
- # 格式: {filepath: (mtime, parsed_value)}
15
- _CACHE = {}
16
-
17
-
18
- def _read_developer():
19
- """读当前开发者名 (容错: UTF-8/GBK)."""
20
- df = os.path.join(BASE, '.qoder', '.developer')
21
- if not os.path.isfile(df):
22
- return None
23
- for enc in ('utf-8', 'gbk', 'utf-8-sig'):
24
- try:
25
- with open(df, encoding=enc) as f:
26
- for line in f:
27
- line = line.strip()
28
- if line.startswith('name='):
29
- return line.split('=', 1)[1].strip()
30
- if line.startswith('name:') or ':' in line:
31
- # 容错 name: x 格式
32
- for sep in ('=', ':'):
33
- if sep in line:
34
- k, v = line.split(sep, 1)
35
- if k.strip() == 'name':
36
- return v.strip()
37
- except (OSError, UnicodeDecodeError):
38
- continue
39
- return None
40
-
41
-
42
- def _cached_read(path, parser):
43
- """带 mtime 缓存的文件读。parser: callable(content_str) -> value."""
44
- try:
45
- mtime = os.path.getmtime(path)
46
- except OSError:
47
- return None
48
- cached = _CACHE.get(path)
49
- if cached and cached[0] == mtime:
50
- return cached[1]
51
- # 重读
52
- for enc in ('utf-8', 'gbk', 'utf-8-sig'):
53
- try:
54
- with open(path, encoding=enc) as f:
55
- content = f.read()
56
- value = parser(content)
57
- _CACHE[path] = (mtime, value)
58
- return value
59
- except (OSError, UnicodeDecodeError):
60
- continue
61
- return None
62
-
63
-
64
- def get_status():
65
- # 优先读按开发者隔离的 current-task 文件
66
- dev = _read_developer()
67
- ct_candidates = []
68
- if dev:
69
- safe = ''.join(c for c in dev if c.isalnum() or c in '_-') or 'anon'
70
- ct_candidates.append(os.path.join(BASE, '.qoder', '.runtime', 'current-task.' + safe))
71
- ct_candidates.append(os.path.join(BASE, '.qoder', '.current-task')) # 旧格式回退
72
-
73
- rel = None
74
- for ct in ct_candidates:
75
- rel = _cached_read(ct, lambda c: c.strip() or None)
76
- if rel:
77
- break
78
- if not rel:
79
- return None, None
80
-
81
- tj = os.path.join(BASE, rel, 'task.json')
82
- if os.path.isfile(tj):
83
- d = _cached_read(tj, lambda c: json.loads(c) if c.strip() else None)
84
- if d:
85
- return rel, d.get('status', 'unknown')
86
- return rel, None
87
-
88
-
89
- # Statuses written by task.py: planning -> in_progress -> completed
90
- GUIDE = {
91
- None: 'No active task. Use /wl-prd to start, or /wl-task create.',
92
- 'planning': 'Task in planning. Use /wl-prd to write the PRD, then /wl-task start.',
93
- 'in_progress': 'Dev in progress. /wl-spec to generate spec, /wl-code to implement.',
94
- 'completed': 'Task completed. /wl-task archive to archive, or start the next one.',
95
- }
96
-
97
-
98
- def main():
99
- tp, st = get_status()
100
- g = GUIDE.get(st, 'Status "{}" - check task.json, expected planning/in_progress/completed.'.format(st))
101
- parts = []
102
- parts.append('<qoder-workflow>')
103
- if tp:
104
- parts.append('Task: ' + tp)
105
- parts.append('Status: ' + str(st))
106
- parts.append('Do: ' + g)
107
- parts.append('</qoder-workflow>')
108
- print(NL.join(parts))
109
-
110
-
111
- if __name__ == '__main__':
112
- try:
113
- main()
114
- except Exception as e:
115
- print('<qoder-workflow>')
116
- print('hook error: ' + str(e)[:200])
117
- print('</qoder-workflow>')
1
+ # inject-workflow-state.py - Inject workflow state on every message
2
+ import os, json, sys
3
+
4
+ if sys.platform == 'win32':
5
+ try:
6
+ sys.stdout.reconfigure(encoding='utf-8')
7
+ except Exception:
8
+ pass
9
+
10
+ NL = chr(10)
11
+ BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12
+
13
+ # mtime 缓存 (审计 H5: 每条用户消息都跑这个 hook, 缓存避免重复读盘)
14
+ # 格式: {filepath: (mtime, parsed_value)}
15
+ _CACHE = {}
16
+
17
+
18
+ def _read_developer():
19
+ """读当前开发者名 (容错: UTF-8/GBK)."""
20
+ df = os.path.join(BASE, '.qoder', '.developer')
21
+ if not os.path.isfile(df):
22
+ return None
23
+ for enc in ('utf-8', 'gbk', 'utf-8-sig'):
24
+ try:
25
+ with open(df, encoding=enc) as f:
26
+ for line in f:
27
+ line = line.strip()
28
+ if line.startswith('name='):
29
+ return line.split('=', 1)[1].strip()
30
+ if line.startswith('name:') or ':' in line:
31
+ # 容错 name: x 格式
32
+ for sep in ('=', ':'):
33
+ if sep in line:
34
+ k, v = line.split(sep, 1)
35
+ if k.strip() == 'name':
36
+ return v.strip()
37
+ except (OSError, UnicodeDecodeError):
38
+ continue
39
+ return None
40
+
41
+
42
+ def _cached_read(path, parser):
43
+ """带 mtime 缓存的文件读。parser: callable(content_str) -> value."""
44
+ try:
45
+ mtime = os.path.getmtime(path)
46
+ except OSError:
47
+ return None
48
+ cached = _CACHE.get(path)
49
+ if cached and cached[0] == mtime:
50
+ return cached[1]
51
+ # 重读
52
+ for enc in ('utf-8', 'gbk', 'utf-8-sig'):
53
+ try:
54
+ with open(path, encoding=enc) as f:
55
+ content = f.read()
56
+ value = parser(content)
57
+ _CACHE[path] = (mtime, value)
58
+ return value
59
+ except (OSError, UnicodeDecodeError):
60
+ continue
61
+ return None
62
+
63
+
64
+ def get_status():
65
+ # 优先读按开发者隔离的 current-task 文件
66
+ dev = _read_developer()
67
+ ct_candidates = []
68
+ if dev:
69
+ safe = ''.join(c for c in dev if c.isalnum() or c in '_-') or 'anon'
70
+ ct_candidates.append(os.path.join(BASE, '.qoder', '.runtime', 'current-task.' + safe))
71
+ ct_candidates.append(os.path.join(BASE, '.qoder', '.current-task')) # 旧格式回退
72
+
73
+ rel = None
74
+ for ct in ct_candidates:
75
+ rel = _cached_read(ct, lambda c: c.strip() or None)
76
+ if rel:
77
+ break
78
+ if not rel:
79
+ return None, None
80
+
81
+ tj = os.path.join(BASE, rel, 'task.json')
82
+ if os.path.isfile(tj):
83
+ d = _cached_read(tj, lambda c: json.loads(c) if c.strip() else None)
84
+ if d:
85
+ return rel, d.get('status', 'unknown')
86
+ return rel, None
87
+
88
+
89
+ # Statuses written by task.py: planning -> in_progress -> completed
90
+ GUIDE = {
91
+ None: 'No active task. Use /wl-prd to start, or /wl-task create.',
92
+ 'planning': 'Task in planning. Use /wl-prd to write the PRD, then /wl-task start.',
93
+ 'in_progress': 'Dev in progress. /wl-spec to generate spec, /wl-code to implement.',
94
+ 'completed': 'Task completed. /wl-task archive to archive, or start the next one.',
95
+ }
96
+
97
+
98
+ def main():
99
+ tp, st = get_status()
100
+ g = GUIDE.get(st, 'Status "{}" - check task.json, expected planning/in_progress/completed.'.format(st))
101
+ parts = []
102
+ parts.append('<qoder-workflow>')
103
+ if tp:
104
+ parts.append('Task: ' + tp)
105
+ parts.append('Status: ' + str(st))
106
+ parts.append('Do: ' + g)
107
+ parts.append('</qoder-workflow>')
108
+ print(NL.join(parts))
109
+
110
+
111
+ if __name__ == '__main__':
112
+ try:
113
+ main()
114
+ except Exception as e:
115
+ print('<qoder-workflow>')
116
+ print('hook error: ' + str(e)[:200])
117
+ print('</qoder-workflow>')