sumulige-claude 1.5.1 → 1.6.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 (223) hide show
  1. package/.claude/hooks/hook-registry.json +0 -15
  2. package/.claude/rules/coding-style.md +18 -7
  3. package/.claude/rules/hooks.md +15 -4
  4. package/.claude/rules/performance.md +15 -5
  5. package/.claude/rules/security.md +140 -4
  6. package/.claude/rules/testing.md +138 -9
  7. package/.claude/rules/web-design-standard.md +16 -5
  8. package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
  9. package/.claude/skills/api-tester/SKILL.md +61 -0
  10. package/.claude/skills/api-tester/examples/basic.md +3 -0
  11. package/.claude/skills/api-tester/metadata.yaml +30 -0
  12. package/.claude/skills/api-tester/templates/default.md +3 -0
  13. package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
  14. package/.claude/skills/canvas-design/metadata.yaml +27 -0
  15. package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
  16. package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
  17. package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
  18. package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
  19. package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
  20. package/.claude/skills/docx/metadata.yaml +30 -0
  21. package/.claude/skills/frontend-design/metadata.yaml +28 -0
  22. package/.claude/skills/internal-comms/metadata.yaml +28 -0
  23. package/.claude/skills/mcp-builder/metadata.yaml +26 -0
  24. package/.claude/skills/my-skill/SKILL.md +61 -0
  25. package/.claude/skills/my-skill/examples/basic.md +3 -0
  26. package/.claude/skills/my-skill/metadata.yaml +30 -0
  27. package/.claude/skills/my-skill/templates/default.md +3 -0
  28. package/.claude/skills/pdf/metadata.yaml +29 -0
  29. package/.claude/skills/pptx/metadata.yaml +29 -0
  30. package/.claude/skills/react-best-practices/metadata.yaml +26 -0
  31. package/.claude/skills/react-node-practices/SKILL.md +409 -0
  32. package/.claude/skills/react-node-practices/metadata.yaml +56 -0
  33. package/.claude/skills/skill-creator/metadata.yaml +25 -0
  34. package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
  35. package/.claude/skills/test-skill-name/SKILL.md +61 -0
  36. package/.claude/skills/test-skill-name/examples/basic.md +3 -0
  37. package/.claude/skills/test-skill-name/metadata.yaml +30 -0
  38. package/.claude/skills/test-skill-name/templates/default.md +3 -0
  39. package/.claude/skills/test-workflow/metadata.yaml +32 -0
  40. package/.claude/skills/theme-factory/metadata.yaml +26 -0
  41. package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
  42. package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
  43. package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
  44. package/.claude/skills/webapp-testing/metadata.yaml +26 -0
  45. package/.claude/skills/xlsx/metadata.yaml +29 -0
  46. package/LICENSE +21 -0
  47. package/README.md +280 -529
  48. package/cli.js +19 -3
  49. package/package.json +29 -3
  50. package/template/.codex/README.md +69 -0
  51. package/template/.codex/config.toml +56 -0
  52. package/template/AGENTS.md +94 -0
  53. package/.claude/.kickoff-hint.txt +0 -52
  54. package/.claude/.sumulige-claude-version +0 -1
  55. package/.claude/.version +0 -1
  56. package/.claude/AGENTS.md +0 -42
  57. package/.claude/ANCHORS.md +0 -40
  58. package/.claude/CLAUDE.md +0 -138
  59. package/.claude/MEMORY.md +0 -69
  60. package/.claude/PROJECT_LOG.md +0 -101
  61. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  62. package/.claude/USAGE.md +0 -175
  63. package/.claude/boris-optimizations.md +0 -167
  64. package/.claude/handoffs/INDEX.md +0 -21
  65. package/.claude/handoffs/LATEST.md +0 -76
  66. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  67. package/.claude/quality-gate.json +0 -82
  68. package/.claude/rag/skill-index.json +0 -135
  69. package/.claude/settings.json +0 -99
  70. package/.claude/settings.local.json +0 -175
  71. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  72. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  73. package/.claude/templates/TASK_PLAN.md +0 -121
  74. package/.claude/templates/hooks/README.md +0 -302
  75. package/.claude/templates/hooks/hook.sh.template +0 -94
  76. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  77. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  78. package/.claude/templates/hooks/validate.js +0 -173
  79. package/.claude/templates/tasks/develop.md +0 -69
  80. package/.claude/templates/tasks/research.md +0 -64
  81. package/.claude/templates/tasks/test.md +0 -96
  82. package/.claude/thinking-routes/.last-sync +0 -1
  83. package/.claude/thinking-routes/QUICKREF.md +0 -98
  84. package/.claude/workflow/document-scanner.js +0 -426
  85. package/.claude/workflow/knowledge-engine.js +0 -941
  86. package/.claude/workflow/notebooklm/browser.js +0 -1028
  87. package/.claude/workflow/phases/phase1-research.js +0 -578
  88. package/.claude/workflow/phases/phase1-research.ts +0 -465
  89. package/.claude/workflow/phases/phase2-approve.js +0 -722
  90. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  91. package/.claude/workflow/phases/phase4-develop.js +0 -894
  92. package/.claude/workflow/search-cache.js +0 -230
  93. package/.claude/workflow/templates/approval.md +0 -315
  94. package/.claude/workflow/templates/development.md +0 -377
  95. package/.claude/workflow/templates/planning.md +0 -328
  96. package/.claude/workflow/templates/research.md +0 -250
  97. package/.claude/workflow/types.js +0 -37
  98. package/.claude/workflow/web-search.js +0 -278
  99. package/.claude-plugin/marketplace.json +0 -71
  100. package/.github/workflows/sync-skills.yml +0 -74
  101. package/.versionrc +0 -25
  102. package/AGENTS.md +0 -580
  103. package/CHANGELOG.md +0 -481
  104. package/CLAUDE-template.md +0 -114
  105. package/DEV_TOOLS_GUIDE.md +0 -190
  106. package/PROJECT_STRUCTURE.md +0 -266
  107. package/Q&A.md +0 -325
  108. package/config/defaults.json +0 -34
  109. package/config/official-skills.json +0 -183
  110. package/config/quality-gate.json +0 -67
  111. package/config/skill-categories.json +0 -40
  112. package/config/version-manifest.json +0 -85
  113. package/demos/power-3d-scatter.html +0 -683
  114. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  115. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  116. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  117. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  118. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  119. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  120. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  121. package/development/knowledge-base/.index.clean.json +0 -1
  122. package/development/knowledge-base/.index.json +0 -486
  123. package/development/knowledge-base/test-best-practices.md +0 -29
  124. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  131. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  132. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  143. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  144. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  145. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  146. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  147. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  148. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  149. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  150. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  151. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  152. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  153. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  154. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  155. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  157. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  158. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  159. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  160. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  161. package/development/todos/.state.json +0 -19
  162. package/development/todos/INDEX.md +0 -63
  163. package/development/todos/active/_README.md +0 -49
  164. package/development/todos/archived/_README.md +0 -11
  165. package/development/todos/backlog/_README.md +0 -11
  166. package/development/todos/backlog/mcp-integration.md +0 -35
  167. package/development/todos/completed/_README.md +0 -11
  168. package/development/todos/completed/boris-optimizations.md +0 -39
  169. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  170. package/development/todos/completed/develop/todo-system.md +0 -47
  171. package/development/todos/completed/develop/web-search-integration.md +0 -83
  172. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  173. package/docs/DEVELOPMENT.md +0 -461
  174. package/docs/MARKETPLACE.md +0 -352
  175. package/docs/RELEASE.md +0 -93
  176. package/jest.config.js +0 -63
  177. package/lib/commands.js +0 -3588
  178. package/lib/config-manager.js +0 -441
  179. package/lib/config-schema.js +0 -408
  180. package/lib/config-validator.js +0 -330
  181. package/lib/config.js +0 -122
  182. package/lib/errors.js +0 -305
  183. package/lib/incremental-sync.js +0 -274
  184. package/lib/marketplace.js +0 -487
  185. package/lib/migrations.js +0 -154
  186. package/lib/permission-audit.js +0 -255
  187. package/lib/quality-gate.js +0 -431
  188. package/lib/quality-rules.js +0 -373
  189. package/lib/utils.js +0 -150
  190. package/lib/version-check.js +0 -169
  191. package/lib/version-manifest.js +0 -171
  192. package/project-paradigm.md +0 -313
  193. package/prompts/how-to-find.md +0 -163
  194. package/prompts/linus-architect.md +0 -71
  195. package/prompts/software-architect.md +0 -173
  196. package/prompts/web-designer.md +0 -249
  197. package/scripts/fix-hooks.mjs +0 -97
  198. package/scripts/sync-external.mjs +0 -298
  199. package/scripts/sync-to-home.sh +0 -108
  200. package/scripts/update-registry.mjs +0 -325
  201. package/sources.yaml +0 -83
  202. package/tests/README.md +0 -263
  203. package/tests/commands.test.js +0 -1086
  204. package/tests/config-manager.test.js +0 -677
  205. package/tests/config-schema.test.js +0 -425
  206. package/tests/config-validator.test.js +0 -436
  207. package/tests/config.test.js +0 -100
  208. package/tests/errors.test.js +0 -477
  209. package/tests/manual/phase1-e2e.sh +0 -389
  210. package/tests/manual/phase2-test-cases.md +0 -311
  211. package/tests/manual/phase3-test-cases.md +0 -309
  212. package/tests/manual/phase4-test-cases.md +0 -414
  213. package/tests/manual/test-cases.md +0 -417
  214. package/tests/marketplace.test.js +0 -420
  215. package/tests/migrations.test.js +0 -187
  216. package/tests/quality-gate.test.js +0 -679
  217. package/tests/quality-rules.test.js +0 -619
  218. package/tests/sync-external.test.js +0 -214
  219. package/tests/update-registry.test.js +0 -251
  220. package/tests/utils.test.js +0 -171
  221. package/tests/version-check.test.js +0 -75
  222. package/tests/web-search.test.js +0 -392
  223. package/thinkinglens-silent.md +0 -138
@@ -1,75 +0,0 @@
1
- /**
2
- * Version Check 模块单元测试
3
- */
4
-
5
- describe('Version Check Module', () => {
6
- const versionCheck = require('../lib/version-check');
7
-
8
- describe('compareVersions', () => {
9
- it('should return -1 when v1 < v2', () => {
10
- expect(versionCheck.compareVersions('1.0.0', '1.0.1')).toBe(-1);
11
- expect(versionCheck.compareVersions('1.0.0', '1.1.0')).toBe(-1);
12
- expect(versionCheck.compareVersions('1.0.0', '2.0.0')).toBe(-1);
13
- expect(versionCheck.compareVersions('0.9.0', '1.0.0')).toBe(-1);
14
- });
15
-
16
- it('should return 0 when versions are equal', () => {
17
- expect(versionCheck.compareVersions('1.0.0', '1.0.0')).toBe(0);
18
- expect(versionCheck.compareVersions('2.5.10', '2.5.10')).toBe(0);
19
- });
20
-
21
- it('should return 1 when v1 > v2', () => {
22
- expect(versionCheck.compareVersions('1.0.1', '1.0.0')).toBe(1);
23
- expect(versionCheck.compareVersions('1.1.0', '1.0.0')).toBe(1);
24
- expect(versionCheck.compareVersions('2.0.0', '1.0.0')).toBe(1);
25
- });
26
-
27
- it('should handle different length versions', () => {
28
- expect(versionCheck.compareVersions('1.0', '1.0.0')).toBe(0);
29
- expect(versionCheck.compareVersions('1.0.0', '1.0')).toBe(0);
30
- expect(versionCheck.compareVersions('1.0', '1.0.1')).toBe(-1);
31
- expect(versionCheck.compareVersions('1.0.1', '1.0')).toBe(1);
32
- });
33
-
34
- it('should handle pre-release tags', () => {
35
- // Pre-release tags are stripped, so these compare equal
36
- expect(versionCheck.compareVersions('1.0.0', '1.0.0-alpha')).toBe(0);
37
- });
38
- });
39
-
40
- describe('getCurrentVersion', () => {
41
- it('should return current version from package.json', () => {
42
- const version = versionCheck.getCurrentVersion();
43
- expect(typeof version).toBe('string');
44
- expect(version).toMatch(/^\d+\.\d+\.\d+/);
45
- });
46
-
47
- it('should export CURRENT_VERSION constant', () => {
48
- expect(versionCheck.CURRENT_VERSION).toBeDefined();
49
- expect(typeof versionCheck.CURRENT_VERSION).toBe('string');
50
- });
51
-
52
- it('should have consistent current version', () => {
53
- expect(versionCheck.getCurrentVersion()).toBe(versionCheck.CURRENT_VERSION);
54
- });
55
- });
56
-
57
- describe('exports', () => {
58
- it('should export all functions', () => {
59
- expect(versionCheck.checkUpdate).toBeDefined();
60
- expect(versionCheck.getCurrentVersion).toBeDefined();
61
- expect(versionCheck.compareVersions).toBeDefined();
62
- expect(versionCheck.getLastCheckTime).toBeDefined();
63
- expect(versionCheck.saveLastCheckTime).toBeDefined();
64
- expect(versionCheck.CURRENT_VERSION).toBeDefined();
65
- });
66
-
67
- it('should export correct types', () => {
68
- expect(typeof versionCheck.checkUpdate).toBe('function');
69
- expect(typeof versionCheck.getCurrentVersion).toBe('function');
70
- expect(typeof versionCheck.compareVersions).toBe('function');
71
- expect(typeof versionCheck.getLastCheckTime).toBe('function');
72
- expect(typeof versionCheck.saveLastCheckTime).toBe('function');
73
- });
74
- });
75
- });
@@ -1,392 +0,0 @@
1
- /**
2
- * Web Search 模块单元测试
3
- * 测试 Bing HTML 解析功能(不涉及网络请求)
4
- */
5
-
6
- // Only test the static methods that don't require network calls
7
- // We'll extract the logic for testing
8
-
9
- // ============================================================================
10
- // Test Data (Bing HTML responses)
11
- // ============================================================================
12
-
13
- const BING_HTML_RESPONSE = `
14
- <li class="b_algo">
15
- <h2><a href="https://example.com/page1">Example Page 1</a></h2>
16
- <p>This is a description of the first page.</p>
17
- </li>
18
- <li class="b_algo">
19
- <h2><a href="https://example.com/page2">Example Page 2</a></h2>
20
- <p>This is a description of the second page.</p>
21
- </li>
22
- <li class="b_algo">
23
- <h2><a href="https://example.com/page3">&quot;Quoted&quot; Title &amp; More</a></h2>
24
- <p>Description with <strong>bold</strong> text and <b>more</b>.</p>
25
- </li>
26
- <li class="b_algo">
27
- <h2><a href="https://www.bing.com/ck/a?!&amp;p=xxx&amp;u=aHR0cHM6Ly9leGFtcGxlLmNvbQ==">Bing Redirect</a></h2>
28
- <p>Redirected page description.</p>
29
- </li>
30
- `;
31
-
32
- // ============================================================================
33
- // Helper Functions (copied from web-search.js for testing)
34
- // ============================================================================
35
-
36
- function decodeHTML(text) {
37
- const entities = {
38
- '&amp;': '&',
39
- '&lt;': '<',
40
- '&gt;': '>',
41
- '&quot;': '"',
42
- '&#39;': "'",
43
- '&apos;': "'",
44
- '&nbsp;': ' ',
45
- '&middot;': '·'
46
- };
47
-
48
- return text.replace(/&[^;]+;/g, (entity) => {
49
- return entities[entity] || entity;
50
- });
51
- }
52
-
53
- function parseHTML(html, maxResults = 5) {
54
- const results = [];
55
-
56
- const algoRegex = /<li[^>]*class="b_algo"[^>]*>([\s\S]*?)<\/li>/g;
57
- let match;
58
- let count = 0;
59
-
60
- while ((match = algoRegex.exec(html)) !== null && count < maxResults) {
61
- const block = match[1];
62
- count++;
63
-
64
- let title = '';
65
- const titleMatch = block.match(/<h2><a[^>]*>([^<]+)<\/a>/);
66
- if (titleMatch) {
67
- title = decodeHTML(titleMatch[1]);
68
- }
69
-
70
- if (!title) {
71
- const altTitleMatch = block.match(/<a[^>]*h="[^"]*"[^>]*>([^<]*(?:<strong>[^<]*<\/strong>[^<]*)*)<\/a>/);
72
- if (altTitleMatch) {
73
- title = decodeHTML(altTitleMatch[1].replace(/<strong>/g, '').replace(/<\/strong>/g, ''));
74
- }
75
- }
76
-
77
- let url = '';
78
- const urlMatch = block.match(/<a[^>]*href="([^"]+)"/);
79
- if (urlMatch) {
80
- url = urlMatch[1];
81
- url = url.replace(/&amp;/g, '&');
82
-
83
- if (url.includes('bing.com/ck/a')) {
84
- const uMatch = url.match(/[?&]u=([^&]+)/);
85
- if (uMatch) {
86
- let encodedUrl = uMatch[1];
87
- try {
88
- let decodedUrl = decodeURIComponent(encodedUrl);
89
- decodedUrl = decodeURIComponent(decodedUrl);
90
-
91
- if (decodedUrl.startsWith('a1')) {
92
- const base64Part = decodedUrl.substring(2);
93
- decodedUrl = Buffer.from(base64Part, 'base64').toString('utf-8');
94
- }
95
-
96
- url = decodedUrl;
97
- } catch (e) {
98
- try {
99
- if (encodedUrl.startsWith('a1')) {
100
- const base64Part = encodedUrl.substring(2);
101
- url = Buffer.from(base64Part, 'base64').toString('utf-8');
102
- } else {
103
- url = decodeURIComponent(encodedUrl);
104
- }
105
- } catch (e2) {}
106
- }
107
- }
108
- }
109
- }
110
-
111
- let snippet = '';
112
- const snippetMatch = block.match(/<p[^>]*>([\s\S]*?)<\/p>/);
113
- if (snippetMatch) {
114
- snippet = decodeHTML(snippetMatch[1]
115
- .replace(/<strong>/g, '').replace(/<\/strong>/g, '')
116
- .replace(/<b>/g, '').replace(/<\/b>/g, '')
117
- .replace(/<[^>]+>/g, ' ')
118
- .replace(/\s+/g, ' ')
119
- .trim());
120
- }
121
-
122
- if (!url || url.startsWith('#')) continue;
123
-
124
- results.push({
125
- title: title || 'Untitled',
126
- url,
127
- excerpt: snippet || 'No description available.',
128
- source: 'bing'
129
- });
130
- }
131
-
132
- return results;
133
- }
134
-
135
- function dedupeResults(results) {
136
- const seen = new Set();
137
- return results.filter(result => {
138
- const normalizedUrl = result.url
139
- .replace(/^https?:\/\//, '')
140
- .replace(/\/$/, '')
141
- .toLowerCase();
142
-
143
- if (seen.has(normalizedUrl)) return false;
144
- seen.add(normalizedUrl);
145
- return true;
146
- });
147
- }
148
-
149
- // ============================================================================
150
- // Tests
151
- // ============================================================================
152
-
153
- describe('Web Search Module - HTML Parsing', () => {
154
- describe('decodeHTML', () => {
155
- it('should decode &amp;', () => {
156
- expect(decodeHTML('Hello &amp; World')).toBe('Hello & World');
157
- });
158
-
159
- it('should decode &lt;', () => {
160
- expect(decodeHTML('A &lt; B')).toBe('A < B');
161
- });
162
-
163
- it('should decode &gt;', () => {
164
- expect(decodeHTML('A &gt; B')).toBe('A > B');
165
- });
166
-
167
- it('should decode &quot;', () => {
168
- expect(decodeHTML('&quot;text&quot;')).toBe('"text"');
169
- });
170
-
171
- it('should decode &#39;', () => {
172
- expect(decodeHTML('&#39;text&#39;')).toBe("'text'");
173
- });
174
-
175
- it('should decode &apos;', () => {
176
- expect(decodeHTML('&apos;text&apos;')).toBe("'text'");
177
- });
178
-
179
- it('should decode &nbsp;', () => {
180
- expect(decodeHTML('A&nbsp;B')).toBe('A B');
181
- });
182
-
183
- it('should decode &middot;', () => {
184
- expect(decodeHTML('A&middot;B')).toBe('A·B');
185
- });
186
-
187
- it('should decode multiple entities', () => {
188
- expect(decodeHTML('&lt;&amp;&gt;')).toBe('<&>');
189
- });
190
-
191
- it('should leave unknown entities unchanged', () => {
192
- expect(decodeHTML('&unknown;')).toBe('&unknown;');
193
- });
194
-
195
- it('should handle text without entities', () => {
196
- expect(decodeHTML('Plain text')).toBe('Plain text');
197
- });
198
-
199
- it('should handle empty string', () => {
200
- expect(decodeHTML('')).toBe('');
201
- });
202
- });
203
-
204
- describe('dedupeResults', () => {
205
- it('should remove duplicate URLs', () => {
206
- const results = [
207
- { url: 'https://example.com/page', title: 'First' },
208
- { url: 'https://example.com/page/', title: 'Second' },
209
- { url: 'https://EXAMPLE.COM/page', title: 'Third' }
210
- ];
211
-
212
- const deduped = dedupeResults(results);
213
- expect(deduped).toHaveLength(1);
214
- });
215
-
216
- it('should preserve unique URLs', () => {
217
- const results = [
218
- { url: 'https://example.com/page1', title: 'First' },
219
- { url: 'https://example.com/page2', title: 'Second' }
220
- ];
221
-
222
- const deduped = dedupeResults(results);
223
- expect(deduped).toHaveLength(2);
224
- });
225
-
226
- it('should normalize URLs for comparison', () => {
227
- const results = [
228
- { url: 'https://example.com/page', title: 'First' },
229
- { url: 'https://example.com/page/', title: 'Second' },
230
- { url: 'https://EXAMPLE.COM/PAGE', title: 'Third' }
231
- ];
232
-
233
- const deduped = dedupeResults(results);
234
- expect(deduped).toHaveLength(1);
235
- });
236
-
237
- it('should preserve result order', () => {
238
- const results = [
239
- { url: 'https://example.com/page1', title: 'First' },
240
- { url: 'https://example.com/page2', title: 'Second' }
241
- ];
242
-
243
- const deduped = dedupeResults(results);
244
- expect(deduped[0].title).toBe('First');
245
- expect(deduped[1].title).toBe('Second');
246
- });
247
-
248
- it('should handle empty array', () => {
249
- const deduped = dedupeResults([]);
250
- expect(deduped).toEqual([]);
251
- });
252
- });
253
-
254
- describe('parseHTML', () => {
255
- it('should parse Bing result blocks', () => {
256
- const results = parseHTML(BING_HTML_RESPONSE, 5);
257
- expect(results).toHaveLength(4);
258
- });
259
-
260
- it('should extract titles', () => {
261
- const results = parseHTML(BING_HTML_RESPONSE, 5);
262
- expect(results[0].title).toBe('Example Page 1');
263
- expect(results[1].title).toBe('Example Page 2');
264
- expect(results[2].title).toBe('"Quoted" Title & More');
265
- });
266
-
267
- it('should extract URLs', () => {
268
- const results = parseHTML(BING_HTML_RESPONSE, 5);
269
- expect(results[0].url).toBe('https://example.com/page1');
270
- expect(results[1].url).toBe('https://example.com/page2');
271
- expect(results[2].url).toBe('https://example.com/page3');
272
- });
273
-
274
- it('should extract snippets', () => {
275
- const results = parseHTML(BING_HTML_RESPONSE, 5);
276
- expect(results[0].excerpt).toBe('This is a description of the first page.');
277
- expect(results[1].excerpt).toBe('This is a description of the second page.');
278
- expect(results[2].excerpt).toBe('Description with bold text and more.');
279
- });
280
-
281
- it('should add source field', () => {
282
- const results = parseHTML(BING_HTML_RESPONSE, 5);
283
- expect(results[0].source).toBe('bing');
284
- });
285
-
286
- it('should decode HTML entities in titles', () => {
287
- const html = `
288
- <li class="b_algo">
289
- <h2><a href="https://example.com">&quot;Test&quot; &amp; More</a></h2>
290
- <p>Description here.</p>
291
- </li>
292
- `;
293
- const results = parseHTML(html, 5);
294
- expect(results[0].title).toBe('"Test" & More');
295
- });
296
-
297
- it('should respect maxResults limit', () => {
298
- const results = parseHTML(BING_HTML_RESPONSE, 2);
299
- expect(results).toHaveLength(2);
300
- });
301
-
302
- it('should handle empty HTML', () => {
303
- const results = parseHTML('', 5);
304
- expect(results).toEqual([]);
305
- });
306
-
307
- it('should handle malformed HTML gracefully', () => {
308
- const malformed = '<li class="b_algo">incomplete';
309
- const results = parseHTML(malformed, 5);
310
- expect(Array.isArray(results)).toBe(true);
311
- });
312
-
313
- it('should skip results with invalid URLs', () => {
314
- const html = `
315
- <li class="b_algo">
316
- <h2><a href="#">No URL</a></h2>
317
- <p>Description.</p>
318
- </li>
319
- `;
320
- const results = parseHTML(html, 5);
321
- expect(results).toHaveLength(0);
322
- });
323
-
324
- it('should provide default title when not found', () => {
325
- const html = `
326
- <li class="b_algo">
327
- <a href="https://example.com"></a>
328
- <p>Description.</p>
329
- </li>
330
- `;
331
- const results = parseHTML(html, 5);
332
- expect(results[0].title).toBe('Untitled');
333
- });
334
-
335
- it('should provide default excerpt when not found', () => {
336
- const html = `
337
- <li class="b_algo">
338
- <h2><a href="https://example.com">Title</a></h2>
339
- </li>
340
- `;
341
- const results = parseHTML(html, 5);
342
- expect(results[0].excerpt).toBe('No description available.');
343
- });
344
-
345
- it('should decode &amp; in URLs', () => {
346
- const html = `
347
- <li class="b_algo">
348
- <h2><a href="https://example.com?param1=value1&amp;param2=value2">Title</a></h2>
349
- <p>Description.</p>
350
- </li>
351
- `;
352
- const results = parseHTML(html, 5);
353
- expect(results[0].url).toBe('https://example.com?param1=value1&param2=value2');
354
- });
355
-
356
- it('should handle Bing redirect URLs with base64 encoding (a1 prefix)', () => {
357
- // Bing uses a1 prefix + base64 encoding for redirect URLs
358
- // a1 + base64('https://example.com') = a1aHR0cHM6Ly9leGFtcGxlLmNvbQ==
359
- // When URL encoded: a1aHR0cHM6Ly9leGFtcGxlLmNvbQ%3D%3D
360
- const html = `
361
- <li class="b_algo">
362
- <h2><a href="https://www.bing.com/ck/a?!&amp;p=xxx&amp;u=a1aHR0cHM6Ly9leGFtcGxlLmNvbQ%3D%3D">Bing Redirect</a></h2>
363
- <p>Description.</p>
364
- </li>
365
- `;
366
- const results = parseHTML(html, 5);
367
- expect(results[0].url).toBe('https://example.com');
368
- });
369
-
370
- it('should handle direct URL encoding (without a1 prefix)', () => {
371
- // Some Bing redirects use direct URL encoding
372
- const html = `
373
- <li class="b_algo">
374
- <h2><a href="https://www.bing.com/ck/a?!&amp;p=xxx&amp;u=https%3A%2F%2Fexample.com">Bing Redirect</a></h2>
375
- <p>Description.</p>
376
- </li>
377
- `;
378
- const results = parseHTML(html, 5);
379
- expect(results[0].url).toBe('https://example.com');
380
- });
381
- });
382
- });
383
-
384
- describe('Web Search Module - Constants', () => {
385
- it('should define SEARCH_TIMEOUT', () => {
386
- expect(10000).toBeDefined();
387
- });
388
-
389
- it('should define MAX_RESULTS', () => {
390
- expect(5).toBeDefined();
391
- });
392
- });
@@ -1,138 +0,0 @@
1
- # ThinkingLens 无感知模式
2
-
3
- > 完全自动、完全静默的对话追踪
4
- > 最后更新:2026-01-11
5
-
6
- ---
7
-
8
- ## 特点
9
-
10
- ✅ **完全静默** - 对话期间零输出、零打扰
11
- ✅ **自动追踪** - 每次 Prompt 自动记录
12
- ✅ **智能识别** - 自动识别关键操作
13
- ✅ **按需查看** - 想看的时候再看
14
-
15
- ---
16
-
17
- ## 工作原理
18
-
19
- ```
20
- 你: "帮我实现数据分析"
21
-
22
- [Hook 静默执行 - 1ms]
23
- ↓ (无输出)
24
- AI: "好的,我来实现..."
25
- ```
26
-
27
- 后台自动记录:
28
- - 对话时间
29
- - 操作类型(代码编辑、关键决策等)
30
- - 输入长度
31
- - 上下文信息
32
-
33
- ---
34
-
35
- ## 使用方法
36
-
37
- ### 1. 自动追踪(已启用)
38
-
39
- 什么都不用做!系统在后台静默运行。
40
-
41
- ### 2. 查看摘要(按需)
42
-
43
- ```bash
44
- .claude/hooks/tl-summary.sh
45
- ```
46
-
47
- 输出示例:
48
- ```
49
- 📅 今日对话摘要
50
- ────────────────────────────────────────
51
- 会话 ID: s-20260111-evolvemind
52
- 开始时间: 2026-01-11 10:30:00
53
- 对话轮次: 15
54
-
55
- 📊 操作统计:
56
- 💬 normal: 10
57
- 📝 code-edit: 4
58
- ⚡ potential-action: 1
59
-
60
- 🕐 最近活动:
61
- 15:20 📝 Edit
62
- 15:15 💬 UserPromptSubmit
63
- 15:10 ⚡ 完成数据分析功能
64
- ```
65
-
66
- ---
67
-
68
- ## 文件位置
69
-
70
- ```
71
- .claude/
72
- ├── hooks/
73
- │ ├── thinking-silent.js # 无感知追踪 Hook ✅ 当前使用
74
- │ ├── tl-summary.sh # 查看摘要命令
75
- │ └── ...
76
- └── thinking-routes/
77
- └── .conversation-flow.json # 对话流数据
78
- ```
79
-
80
- ---
81
-
82
- ## 配置
83
-
84
- 在 `.claude/settings.json` 中:
85
-
86
- ```json
87
- {
88
- "hooks": {
89
- "UserPromptSubmit": [
90
- {
91
- "hooks": [{
92
- "type": "command",
93
- "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/thinking-silent.js",
94
- "timeout": 1
95
- }]
96
- }
97
- ]
98
- }
99
- }
100
- ```
101
-
102
- ---
103
-
104
- ## 数据隐私
105
-
106
- - 数据只保存在本地项目
107
- - 不会上传到任何外部服务
108
- - 你可以随时删除 `.conversation-flow.json`
109
-
110
- ---
111
-
112
- ## 迁移到新项目
113
-
114
- 只需复制 2 个文件:
115
-
116
- ```bash
117
- # 1. 复制 Hook
118
- cp .claude/hooks/thinking-silent.js /新项目/.claude/hooks/
119
-
120
- # 2. 更新 settings.json
121
- # 添加 UserPromptSubmit hook 配置
122
- ```
123
-
124
- ---
125
-
126
- ## 常见问题
127
-
128
- **Q: 会影响性能吗?**
129
- A: 不会,每次执行耗时 < 1ms
130
-
131
- **Q: 会占用多少空间?**
132
- A: 每次对话约 100-500 字节,可忽略
133
-
134
- **Q: 如何清空记录?**
135
- A: 删除 `.claude/thinking-routes/.conversation-flow.json`
136
-
137
- **Q: 对话会保存到云端吗?**
138
- A: 不会,只保存在本地