@hupan56/wlkj 2.0.0 → 2.0.1

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,11 +1,11 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "2.0.0",
4
- "description": "AI 产品研发工作流 - PRD/原型/搜索/任务/报告 一键就绪",
3
+ "version": "2.0.1",
4
+ "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": { "wlkj": "./bin/cli.js" },
6
6
  "files": ["bin/", "templates/"],
7
7
  "keywords": ["workflow", "ai", "prd", "pipeline", "qoder", "product", "team"],
8
8
  "license": "MIT",
9
9
  "publishConfig": { "access": "public" },
10
10
  "engines": { "node": ">=16" }
11
- }
11
+ }
@@ -21,6 +21,7 @@ Usage:
21
21
  import os
22
22
  import sys
23
23
  import json
24
+ import time
24
25
 
25
26
  # UTF-8 stdio (防御性: stdout 被捕获时不崩溃)
26
27
  try:
@@ -71,6 +72,53 @@ def plat_filter(files, target):
71
72
  return [f for f in files if target.lower() in f.lower()]
72
73
 
73
74
 
75
+ def _cache_key(query, platform, page_type):
76
+ """生成缓存 key: query|platform|type|索引mtime 指纹。
77
+
78
+ 索引文件没变就命中缓存, 变了自动失效。
79
+ """
80
+ import hashlib
81
+ # 取 3 个主要索引的 mtime 作为指纹
82
+ mtime_str = ''
83
+ for idx_file in ['keyword-index.json', 'api-index.json', 'prd-index.json']:
84
+ p = os.path.join(INDEX_DIR, idx_file)
85
+ if os.path.isfile(p):
86
+ mtime_str += '%d_' % os.path.getmtime(p)
87
+ raw = '{}|{}|{}|{}'.format(query, platform or '', page_type or '', mtime_str)
88
+ return hashlib.md5(raw.encode('utf-8')).hexdigest()[:16]
89
+
90
+
91
+ def _try_cache(key):
92
+ """读缓存命中则返回内容, 未命中返回 None。"""
93
+ cache_path = os.path.join(BASE, '.qoder', '.runtime', 'ctx-cache-%s.md' % key)
94
+ if not os.path.isfile(cache_path):
95
+ return None
96
+ try:
97
+ # 缓存 24 小时有效 (防 mtime 精度问题)
98
+ age = time.time() - os.path.getmtime(cache_path)
99
+ if age > 86400:
100
+ return None
101
+ with open(cache_path, encoding='utf-8') as f:
102
+ return f.read()
103
+ except OSError:
104
+ return None
105
+
106
+
107
+ def _save_cache(key, content):
108
+ """写缓存 (best-effort, 失败不阻塞)。"""
109
+ try:
110
+ cache_dir = os.path.join(BASE, '.qoder', '.runtime')
111
+ os.makedirs(cache_dir, exist_ok=True)
112
+ cache_path = os.path.join(cache_dir, 'ctx-cache-%s.md' % key)
113
+ # 原子写
114
+ tmp = cache_path + '.tmp'
115
+ with open(tmp, 'w', encoding='utf-8') as f:
116
+ f.write(content)
117
+ os.replace(tmp, cache_path)
118
+ except OSError:
119
+ pass
120
+
121
+
74
122
  def main():
75
123
  args = sys.argv[1:]
76
124
  platform = None
@@ -88,6 +136,30 @@ def main():
88
136
  return 1
89
137
  query = args[0]
90
138
 
139
+ # 结果缓存: 同 query+platform+索引未变 → 直接返回 (470ms → <10ms)
140
+ cache_key = _cache_key(query, platform, page_type)
141
+ cached = _try_cache(cache_key)
142
+ if cached is not None:
143
+ print(cached, end='')
144
+ return 0
145
+
146
+ # 缓存未命中, 正常计算, 末尾写入缓存
147
+ import io
148
+ old_stdout = sys.stdout
149
+ sys.stdout = buf = io.StringIO()
150
+ try:
151
+ _compute_and_print(query, platform, page_type)
152
+ finally:
153
+ sys.stdout = old_stdout
154
+ output = buf.getvalue()
155
+ print(output, end='')
156
+ _save_cache(cache_key, output)
157
+ return 0
158
+
159
+
160
+ def _compute_and_print(query, platform, page_type):
161
+ """实际的 context_pack 计算 (原 main 逻辑)。"""
162
+
91
163
  targets = []
92
164
  if platform and platform.lower() not in ('both', '两端'):
93
165
  targets = [PLATFORM_MAP.get(platform.lower(), platform)]
@@ -189,8 +261,6 @@ def main():
189
261
  for w in wikis:
190
262
  print('- ' + w)
191
263
 
192
- return 0
193
-
194
264
 
195
265
  if __name__ == '__main__':
196
266
  sys.exit(main())