@hupan56/wlkj 2.0.0

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.
Files changed (87) hide show
  1. package/bin/cli.js +213 -0
  2. package/package.json +11 -0
  3. package/templates/cli.js +198 -0
  4. package/templates/qoder/commands/wl-code.md +43 -0
  5. package/templates/qoder/commands/wl-commit.md +30 -0
  6. package/templates/qoder/commands/wl-init.md +80 -0
  7. package/templates/qoder/commands/wl-insight.md +51 -0
  8. package/templates/qoder/commands/wl-prd.md +199 -0
  9. package/templates/qoder/commands/wl-report.md +166 -0
  10. package/templates/qoder/commands/wl-search.md +52 -0
  11. package/templates/qoder/commands/wl-spec.md +18 -0
  12. package/templates/qoder/commands/wl-status.md +51 -0
  13. package/templates/qoder/commands/wl-task.md +71 -0
  14. package/templates/qoder/commands/wl-test.md +42 -0
  15. package/templates/qoder/config.toml +5 -0
  16. package/templates/qoder/config.yaml +141 -0
  17. package/templates/qoder/hooks/inject-workflow-state.py +117 -0
  18. package/templates/qoder/hooks/session-start.py +204 -0
  19. package/templates/qoder/rules/wl-pipeline.md +105 -0
  20. package/templates/qoder/scripts/add_session.py +245 -0
  21. package/templates/qoder/scripts/benchmark.py +209 -0
  22. package/templates/qoder/scripts/build_style_index.py +268 -0
  23. package/templates/qoder/scripts/code_index.py +41 -0
  24. package/templates/qoder/scripts/collect_prds.py +31 -0
  25. package/templates/qoder/scripts/common/__init__.py +0 -0
  26. package/templates/qoder/scripts/common/active_task.py +230 -0
  27. package/templates/qoder/scripts/common/atomicio.py +172 -0
  28. package/templates/qoder/scripts/common/developer.py +161 -0
  29. package/templates/qoder/scripts/common/eval_api.py +144 -0
  30. package/templates/qoder/scripts/common/feishu.py +278 -0
  31. package/templates/qoder/scripts/common/filelock.py +211 -0
  32. package/templates/qoder/scripts/common/identity.py +285 -0
  33. package/templates/qoder/scripts/common/mentions.py +134 -0
  34. package/templates/qoder/scripts/common/paths.py +311 -0
  35. package/templates/qoder/scripts/common/reqid.py +218 -0
  36. package/templates/qoder/scripts/common/search_engine.py +205 -0
  37. package/templates/qoder/scripts/common/task_utils.py +342 -0
  38. package/templates/qoder/scripts/common/terms.py +234 -0
  39. package/templates/qoder/scripts/common/utf8.py +38 -0
  40. package/templates/qoder/scripts/context_pack.py +196 -0
  41. package/templates/qoder/scripts/eval_prd.py +225 -0
  42. package/templates/qoder/scripts/export.py +487 -0
  43. package/templates/qoder/scripts/git_sync.py +1087 -0
  44. package/templates/qoder/scripts/handoff.py +22 -0
  45. package/templates/qoder/scripts/init_developer.py +76 -0
  46. package/templates/qoder/scripts/init_doctor.py +527 -0
  47. package/templates/qoder/scripts/install_qoderwork.py +339 -0
  48. package/templates/qoder/scripts/learn.py +67 -0
  49. package/templates/qoder/scripts/notify.py +5 -0
  50. package/templates/qoder/scripts/parse_prds.py +33 -0
  51. package/templates/qoder/scripts/report.py +281 -0
  52. package/templates/qoder/scripts/role.py +39 -0
  53. package/templates/qoder/scripts/run_weekly_update.bat +17 -0
  54. package/templates/qoder/scripts/run_weekly_update.sh +20 -0
  55. package/templates/qoder/scripts/search_index.py +352 -0
  56. package/templates/qoder/scripts/setup.py +453 -0
  57. package/templates/qoder/scripts/setup_weekly_cron.bat +22 -0
  58. package/templates/qoder/scripts/setup_weekly_cron.sh +19 -0
  59. package/templates/qoder/scripts/status.py +389 -0
  60. package/templates/qoder/scripts/syncgate.py +330 -0
  61. package/templates/qoder/scripts/task.py +954 -0
  62. package/templates/qoder/scripts/team.py +29 -0
  63. package/templates/qoder/scripts/team_sync.py +419 -0
  64. package/templates/qoder/scripts/workspace_init.py +102 -0
  65. package/templates/qoder/settings.json +53 -0
  66. package/templates/qoder/skills/design-review/SKILL.md +25 -0
  67. package/templates/qoder/skills/prd-generator/SKILL.md +180 -0
  68. package/templates/qoder/skills/prd-review/SKILL.md +36 -0
  69. package/templates/qoder/skills/prototype-generator/SKILL.md +141 -0
  70. package/templates/qoder/skills/spec-coder/SKILL.md +69 -0
  71. package/templates/qoder/skills/spec-generator/SKILL.md +67 -0
  72. package/templates/qoder/skills/test-generator/SKILL.md +72 -0
  73. package/templates/qoder/skills/wl-commit/SKILL.md +76 -0
  74. package/templates/qoder/skills/wl-init/SKILL.md +67 -0
  75. package/templates/qoder/skills/wl-insight/SKILL.md +81 -0
  76. package/templates/qoder/skills/wl-report/SKILL.md +87 -0
  77. package/templates/qoder/skills/wl-search/SKILL.md +75 -0
  78. package/templates/qoder/skills/wl-status/SKILL.md +61 -0
  79. package/templates/qoder/skills/wl-task/SKILL.md +58 -0
  80. package/templates/qoder/templates/prd-full-template.md +103 -0
  81. package/templates/qoder/templates/prd-quick-template.md +69 -0
  82. package/templates/qoder/templates/prototype-app.html +344 -0
  83. package/templates/qoder/templates/prototype-web.html +310 -0
  84. package/templates/root/AGENTS.md +182 -0
  85. package/templates/root/README-pipeline.md +56 -0
  86. package/templates/root/ROLES.md +85 -0
  87. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -0
@@ -0,0 +1,352 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Search knowledge index with platform filtering and PRD search.
5
+
6
+ Usage:
7
+ python search_index.py 保险 --platform web
8
+ python search_index.py 保险 --platform app
9
+ python search_index.py --prd 保险
10
+ python search_index.py --style table [--platform web]
11
+ python search_index.py --field nickName
12
+ python search_index.py --api salary
13
+ python search_index.py --components
14
+ python search_index.py --modules
15
+ python search_index.py --list
16
+ python search_index.py --vben
17
+ """
18
+ import os, json, sys
19
+
20
+ # UTF-8 stdio (防御性: stdout 被捕获时不崩溃)
21
+ try:
22
+ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
23
+ except (AttributeError, TypeError, OSError, IOError):
24
+ try:
25
+ sys.stdout.reconfigure(encoding='utf-8')
26
+ except Exception:
27
+ pass
28
+
29
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
30
+ from common.terms import CN_MAP, BUSINESS_PATH_MAP, CN_TO_EN, get_platform_map
31
+ PLATFORM_MAP = get_platform_map()
32
+
33
+ BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
34
+ INDEX_DIR = os.path.join(BASE, 'data', 'index')
35
+
36
+ # Substring matching against keys shorter than this produces too much noise
37
+ MIN_FUZZY_LEN = 4
38
+
39
+
40
+ def load_index(filename, hint=''):
41
+ path = os.path.join(INDEX_DIR, filename)
42
+ if not os.path.isfile(path):
43
+ print('{} not found.{}'.format(filename, ' ' + hint if hint else ''))
44
+ return None
45
+ try:
46
+ with open(path, encoding='utf-8') as f:
47
+ return json.load(f)
48
+ except (json.JSONDecodeError, OSError) as e:
49
+ print('ERROR: failed to read {}: {}'.format(filename, e))
50
+ return None
51
+
52
+
53
+ def filter_by_platform(files, platform=None):
54
+ if not platform:
55
+ return files
56
+ target = PLATFORM_MAP.get(platform.lower(), platform)
57
+ return [f for f in files if target.lower() in f.lower()]
58
+
59
+
60
+ def _match_keyword(query_word, kw):
61
+ """Match a query word against an index keyword without short-key noise."""
62
+ w, k = query_word.lower(), kw.lower()
63
+ if w == k:
64
+ return True
65
+ if len(k) >= MIN_FUZZY_LEN and k in w:
66
+ return True
67
+ if len(w) >= MIN_FUZZY_LEN and w in k:
68
+ return True
69
+ return False
70
+
71
+
72
+ def search_keywords(query, platform=None):
73
+ ki = load_index('keyword-index.json', hint='Run: python git_sync.py')
74
+ if ki is None:
75
+ return
76
+
77
+ # Expand Chinese query via the shared term maps (same maps used at build time)
78
+ words = []
79
+ if query in CN_MAP:
80
+ words.extend(CN_MAP[query].split())
81
+ if query in BUSINESS_PATH_MAP:
82
+ words.extend(p.strip('/-').lower() for p in BUSINESS_PATH_MAP[query])
83
+ if words:
84
+ words = sorted(set(w.lower() for w in words))
85
+ print('Chinese: {} -> English: {}'.format(query, ' '.join(words)))
86
+ else:
87
+ words = [query.lower()]
88
+ if platform:
89
+ print('Platform: {} -> {}'.format(platform, PLATFORM_MAP.get(platform.lower(), platform)))
90
+ print()
91
+
92
+ # 性能优化 A2: 用倒排索引缩小候选集 (从 O(all_keys) 降到 O(命中数))
93
+ try:
94
+ from common.search_engine import get_inverted_index, search_keywords_fast
95
+ from common.paths import get_repo_root
96
+ inverted = get_inverted_index(str(get_repo_root()))
97
+ kw_matches = search_keywords_fast(words, inverted, ki)
98
+ except Exception:
99
+ # 回退: 全扫描
100
+ kw_matches = {}
101
+ for word in words:
102
+ for kw, files in ki.items():
103
+ if _match_keyword(word, kw):
104
+ kw_matches[kw] = files
105
+
106
+ # 按平台过滤 + 分组
107
+ matches = {}
108
+ for kw, files in kw_matches.items():
109
+ for f in filter_by_platform(files, platform):
110
+ p = f.split('/')[0] if '/' in f else f.split('\\')[0]
111
+ matches.setdefault(p, {}).setdefault(kw, [])
112
+ if f not in matches[p][kw]:
113
+ matches[p][kw].append(f)
114
+
115
+ if not matches:
116
+ print('No matches for: {}'.format(query))
117
+ return
118
+
119
+ total = 0
120
+ for proj, kws in sorted(matches.items()):
121
+ count = sum(len(fs) for fs in kws.values())
122
+ total += count
123
+ print('[{}] {} files'.format(proj, count))
124
+ for kw, fs in sorted(kws.items(), key=lambda x: len(x[1]), reverse=True)[:5]:
125
+ for f in fs[:3]:
126
+ parts = f.replace('\\', '/').split('/')
127
+ short = '/'.join(parts[:2]) + '/.../' + '/'.join(parts[-2:]) if len(parts) > 5 else '/'.join(parts)
128
+ print(' ' + short)
129
+ print()
130
+ print('Total: {} matches'.format(total))
131
+
132
+
133
+ def search_prds(query):
134
+ prd_index = load_index('prd-index.json', hint='Run: python git_sync.py --prd-only')
135
+ if prd_index is None:
136
+ return
137
+ q = query.lower()
138
+
139
+ matches = []
140
+ for prd_file, prd in prd_index.items():
141
+ score = 0
142
+ title = prd.get('title', '').lower()
143
+ keywords = [kw.lower() for kw in prd.get('keywords', [])]
144
+ cn_terms = [t.lower() for t in prd.get('cn_terms', [])]
145
+
146
+ if q in title:
147
+ score += 10
148
+ if q in keywords or q in cn_terms:
149
+ score += 5
150
+ if q in CN_TO_EN and CN_TO_EN[q] in keywords:
151
+ score += 3
152
+ for kw in keywords:
153
+ if q in kw or kw in q:
154
+ score += 2
155
+
156
+ if score > 0:
157
+ matches.append((score, prd_file, prd))
158
+
159
+ if not matches:
160
+ print('No PRDs matching: {}'.format(query))
161
+ return
162
+
163
+ matches.sort(key=lambda x: x[0], reverse=True)
164
+
165
+ print('PRDs matching: {} ({} found)'.format(query, len(matches)))
166
+ print()
167
+ for score, prd_file, prd in matches[:10]:
168
+ title = prd.get('title', prd_file)[:60]
169
+ features = len(prd.get('features', []))
170
+ related = sum(len(fs) for fs in prd.get('related_code', {}).values())
171
+ print(' {} - {}'.format(prd_file, title))
172
+ print(' Features: {}, Related code: {} files'.format(features, related))
173
+ if prd.get('related_code'):
174
+ for proj, files in list(prd['related_code'].items())[:2]:
175
+ print(' -> {}: {} files'.format(proj, len(files)))
176
+ print()
177
+
178
+
179
+ def search_style(style_type, platform=None):
180
+ """Find pages of a given UI type (table/form/detail/modal/dashboard)."""
181
+ si = load_index('style-index.json', hint='Run: python build_style_index.py')
182
+ if si is None:
183
+ return
184
+ q = style_type.lower()
185
+ projects = si.get('projects', {})
186
+ if platform:
187
+ target = PLATFORM_MAP.get(platform.lower(), platform)
188
+ projects = {p: v for p, v in projects.items() if p.lower() == target.lower()}
189
+
190
+ found_any = False
191
+ for proj, pdata in sorted(projects.items()):
192
+ page_types = pdata.get('page_types', {})
193
+ examples = pdata.get('page_examples', {})
194
+ hit_types = [pt for pt in page_types if q in pt.lower()]
195
+ if not hit_types:
196
+ continue
197
+ found_any = True
198
+ for pt in hit_types:
199
+ print('[{}] {}: {} pages'.format(proj, pt, page_types[pt]))
200
+ for f in examples.get(pt, [])[:8]:
201
+ print(' ' + f)
202
+ if not examples.get(pt):
203
+ print(' (no examples stored - run: python build_style_index.py)')
204
+ print()
205
+
206
+ if not found_any:
207
+ known = sorted({pt for v in si.get('projects', {}).values()
208
+ for pt in v.get('page_types', {})})
209
+ print('No style matches for: {}'.format(style_type))
210
+ print('Known page types: {}'.format(', '.join(known)))
211
+
212
+
213
+ def search_field(field_name):
214
+ """Search field usage in style-index.json field_map."""
215
+ si = load_index('style-index.json', hint='Run: python build_style_index.py')
216
+ if si is None:
217
+ return
218
+ fm = si.get('field_map', {})
219
+ matches = {f: i for f, i in fm.items() if field_name.lower() in f.lower()}
220
+ if not matches:
221
+ print('No fields matching: {}'.format(field_name))
222
+ return
223
+ print('Fields matching: {} ({} found)'.format(field_name, len(matches)))
224
+ for field, info in sorted(matches.items())[:10]:
225
+ titles = ', '.join(info.get('titles', []))
226
+ print(' {} -> {} (used {}x)'.format(field, titles, info.get('count', '?')))
227
+ for f in info.get('sample_files', [])[:3]:
228
+ print(' ' + f)
229
+
230
+
231
+ def search_api(query):
232
+ """Search API endpoints in api-index.json."""
233
+ ai = load_index('api-index.json', hint='Run: python git_sync.py --index-only')
234
+ if ai is None:
235
+ return
236
+ q = query.lower()
237
+ matches = {ep: f for ep, f in ai.items() if q in ep.lower() or q in f.lower()}
238
+ if not matches:
239
+ print('No APIs matching: {}'.format(query))
240
+ return
241
+ print('APIs matching: {} ({} found)'.format(query, len(matches)))
242
+ for ep, f in sorted(matches.items())[:20]:
243
+ print(' {} -> {}'.format(ep, f))
244
+ if len(matches) > 20:
245
+ print(' ... and {} more'.format(len(matches) - 20))
246
+
247
+
248
+ def show_modules():
249
+ """Show project overview from module-map.json."""
250
+ mm = load_index('module-map.json', hint='Run: python git_sync.py --index-only')
251
+ if mm is None:
252
+ return
253
+ print('=== Project Modules ===')
254
+ print()
255
+ for proj, info in sorted(mm.items()):
256
+ files = info.get('files', 0)
257
+ files = len(files) if isinstance(files, list) else files
258
+ print('[{}] files: {}, classes: {}, apis: {}'.format(
259
+ proj, files, info.get('classes', 0), info.get('apis', 0)))
260
+
261
+
262
+ def show_top_keywords():
263
+ """Show top keywords by file count."""
264
+ ki = load_index('keyword-index.json', hint='Run: python git_sync.py')
265
+ if ki is None:
266
+ return
267
+ top = sorted(ki.items(), key=lambda x: len(x[1]), reverse=True)[:50]
268
+ print('=== Top 50 Keywords (by file count) ===')
269
+ print()
270
+ for kw, files in top:
271
+ print(' {} {}'.format(kw.ljust(30), len(files)))
272
+
273
+
274
+ def show_components():
275
+ meta = load_index('style-meta.json', hint='Run: python build_style_index.py')
276
+ if meta is None:
277
+ return
278
+ print('=== UI Components Usage ===')
279
+ print()
280
+ print('Ant Design Vue (import counts):')
281
+ for comp, count in meta.get('common_components', {}).items():
282
+ print(' {} {}'.format(comp.ljust(20), str(count).ljust(5)))
283
+ print()
284
+ print('Form Components (usage in schemas):')
285
+ for comp, count in sorted(meta.get('form_components', {}).items(), key=lambda x: x[1], reverse=True)[:20]:
286
+ print(' {} {}'.format(comp.ljust(20), str(count).ljust(5)))
287
+
288
+
289
+ def show_vben():
290
+ data = load_index('vben-style-reference.json')
291
+ if data is None:
292
+ return
293
+ print('=== Vben Admin Design Tokens ===')
294
+ print()
295
+ for section, values in data.items():
296
+ print('[{}]'.format(section))
297
+ if isinstance(values, dict):
298
+ for key, val in values.items():
299
+ print(' {}: {}'.format(key, val))
300
+ print()
301
+
302
+
303
+ def print_usage():
304
+ print('Usage:')
305
+ print(' search_index.py <keyword> [--platform web|app] - Search code')
306
+ print(' search_index.py --prd <keyword> - Search PRDs')
307
+ print(' search_index.py --style <type> [--platform p] - UI patterns (table/form/detail/modal/dashboard)')
308
+ print(' search_index.py --field <name> - Field usage')
309
+ print(' search_index.py --api <keyword> - API endpoints')
310
+ print(' search_index.py --components - UI components')
311
+ print(' search_index.py --modules - Project overview')
312
+ print(' search_index.py --list - Top 50 keywords')
313
+ print(' search_index.py --vben - Vben design tokens')
314
+ print()
315
+ print('Platform: web/pc/管理端 -> fywl-ui, app/mobile/h5/移动端 -> Carmg-H5')
316
+
317
+
318
+ if __name__ == '__main__':
319
+ platform = None
320
+ args = sys.argv[1:]
321
+ if '--platform' in args:
322
+ idx = args.index('--platform')
323
+ if idx + 1 < len(args):
324
+ platform = args[idx + 1]
325
+ args = args[:idx] + args[idx+2:]
326
+ else:
327
+ args = args[:idx]
328
+
329
+ if len(args) < 1:
330
+ print_usage()
331
+ elif args[0] == '--prd' and len(args) >= 2:
332
+ search_prds(args[1])
333
+ elif args[0] == '--style' and len(args) >= 2:
334
+ search_style(args[1], platform)
335
+ elif args[0] == '--field' and len(args) >= 2:
336
+ search_field(args[1])
337
+ elif args[0] == '--api' and len(args) >= 2:
338
+ search_api(args[1])
339
+ elif args[0] == '--components':
340
+ show_components()
341
+ elif args[0] == '--modules':
342
+ show_modules()
343
+ elif args[0] == '--list':
344
+ show_top_keywords()
345
+ elif args[0] == '--vben':
346
+ show_vben()
347
+ elif args[0].startswith('--'):
348
+ print('Unknown option: {}'.format(args[0]))
349
+ print()
350
+ print_usage()
351
+ else:
352
+ search_keywords(args[0], platform)