sumulige-claude 1.1.2 → 1.2.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 (102) hide show
  1. package/.claude/hooks/code-formatter.cjs +7 -2
  2. package/.claude/hooks/multi-session.cjs +9 -3
  3. package/.claude/hooks/pre-commit.cjs +0 -0
  4. package/.claude/hooks/pre-push.cjs +0 -0
  5. package/.claude/hooks/project-kickoff.cjs +22 -11
  6. package/.claude/hooks/rag-skill-loader.cjs +7 -0
  7. package/.claude/hooks/thinking-silent.cjs +9 -3
  8. package/.claude/hooks/todo-manager.cjs +19 -13
  9. package/.claude/hooks/verify-work.cjs +10 -4
  10. package/.claude/quality-gate.json +9 -3
  11. package/.claude/settings.local.json +16 -1
  12. package/.claude/templates/hooks/README.md +302 -0
  13. package/.claude/templates/hooks/hook.sh.template +94 -0
  14. package/.claude/templates/hooks/user-prompt-submit.cjs.template +116 -0
  15. package/.claude/templates/hooks/user-response-submit.cjs.template +94 -0
  16. package/.claude/templates/hooks/validate.js +173 -0
  17. package/.claude/workflow/document-scanner.js +426 -0
  18. package/.claude/workflow/knowledge-engine.js +941 -0
  19. package/.claude/workflow/notebooklm/browser.js +1028 -0
  20. package/.claude/workflow/phases/phase1-research.js +578 -0
  21. package/.claude/workflow/phases/phase1-research.ts +465 -0
  22. package/.claude/workflow/phases/phase2-approve.js +722 -0
  23. package/.claude/workflow/phases/phase3-plan.js +1200 -0
  24. package/.claude/workflow/phases/phase4-develop.js +894 -0
  25. package/.claude/workflow/search-cache.js +230 -0
  26. package/.claude/workflow/templates/approval.md +315 -0
  27. package/.claude/workflow/templates/development.md +377 -0
  28. package/.claude/workflow/templates/planning.md +328 -0
  29. package/.claude/workflow/templates/research.md +250 -0
  30. package/.claude/workflow/types.js +37 -0
  31. package/.claude/workflow/web-search.js +278 -0
  32. package/.claude-plugin/marketplace.json +2 -2
  33. package/AGENTS.md +176 -0
  34. package/CHANGELOG.md +7 -14
  35. package/cli.js +20 -0
  36. package/config/quality-gate.json +9 -3
  37. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +36 -0
  38. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +36 -0
  39. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +36 -0
  40. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +36 -0
  41. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +36 -0
  42. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +36 -0
  43. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +36 -0
  44. package/development/knowledge-base/.index.clean.json +0 -0
  45. package/development/knowledge-base/.index.json +486 -0
  46. package/development/knowledge-base/test-best-practices.md +29 -0
  47. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +160 -0
  48. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +160 -0
  49. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +160 -0
  50. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +160 -0
  51. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +160 -0
  52. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +160 -0
  53. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +160 -0
  54. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +160 -0
  55. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +160 -0
  56. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +160 -0
  57. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +226 -0
  58. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +345 -0
  59. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +284 -0
  60. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +14 -0
  61. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +35 -0
  62. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +34 -0
  63. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +5 -0
  64. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +60 -0
  65. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +25 -0
  66. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +70 -0
  67. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +48 -0
  68. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +20 -0
  69. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +21 -0
  70. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +160 -0
  71. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +226 -0
  72. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +345 -0
  73. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +284 -0
  74. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +14 -0
  75. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +160 -0
  76. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +178 -0
  77. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +377 -0
  78. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +442 -0
  79. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +800 -0
  80. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +625 -0
  81. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +830 -0
  82. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +957 -0
  83. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +381 -0
  84. package/development/todos/.state.json +14 -1
  85. package/development/todos/INDEX.md +31 -73
  86. package/development/todos/completed/develop/local-knowledge-index.md +85 -0
  87. package/development/todos/{active → completed/develop}/todo-system.md +13 -3
  88. package/development/todos/completed/develop/web-search-integration.md +83 -0
  89. package/development/todos/completed/test/phase1-e2e-test.md +103 -0
  90. package/lib/commands.js +388 -0
  91. package/package.json +3 -2
  92. package/tests/config-manager.test.js +677 -0
  93. package/tests/config-validator.test.js +436 -0
  94. package/tests/errors.test.js +477 -0
  95. package/tests/manual/phase1-e2e.sh +389 -0
  96. package/tests/manual/phase2-test-cases.md +311 -0
  97. package/tests/manual/phase3-test-cases.md +309 -0
  98. package/tests/manual/phase4-test-cases.md +414 -0
  99. package/tests/manual/test-cases.md +417 -0
  100. package/tests/quality-gate.test.js +679 -0
  101. package/tests/quality-rules.test.js +619 -0
  102. package/tests/version-check.test.js +75 -0
@@ -0,0 +1,250 @@
1
+ # Phase 1 Research Prompt Template
2
+
3
+ > **Role**: NotebookLM Knowledge Engine
4
+ > **Goal**: Transform user idea into comprehensive feasibility analysis
5
+
6
+ ---
7
+
8
+ ## System Context
9
+
10
+ You are the **Phase 1 Research Engine** for a 5-phase AI-assisted development workflow. Your role is to:
11
+
12
+ 1. **Understand** the user's idea/requirement deeply
13
+ 2. **Connect dots** by finding related work, patterns, and decisions
14
+ 3. **Research** industry best practices using both local knowledge and web search
15
+ 4. **Assess** feasibility with concrete technical analysis
16
+
17
+ **Key Principle**: You provide **zero-hallucination** analysis. If you don't know something from the knowledge base or web search, explicitly state it.
18
+
19
+ ---
20
+
21
+ ## Input Format
22
+
23
+ ```
24
+ USER IDEA: {{userIdea}}
25
+
26
+ CONTEXT:
27
+ {{additionalContext}}
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Research Process
33
+
34
+ ### Step 1: Requirement Structuring (30 minutes)
35
+
36
+ Extract and clarify:
37
+ - **Core problem** being solved
38
+ - **Target users** and their pain points
39
+ - **Key features** requested
40
+ - **Constraints** (technical, time, resources)
41
+ - **Assumptions** made
42
+
43
+ Output format:
44
+ ```markdown
45
+ ## Requirements Summary
46
+
47
+ ### Problem Statement
48
+ [What problem are we solving?]
49
+
50
+ ### Target Users
51
+ [Who will use this? What are their pain points?]
52
+
53
+ ### Key Features
54
+ 1. [Feature 1]
55
+ 2. [Feature 2]
56
+ ...
57
+
58
+ ### Constraints
59
+ - [Constraint 1]
60
+ - [Constraint 2]
61
+
62
+ ### Assumptions
63
+ - [Assumption 1]
64
+ - [Assumption 2]
65
+ ```
66
+
67
+ ---
68
+
69
+ ### Step 2: Correlation Analysis (Connect The Dots) (45 minutes)
70
+
71
+ Search the knowledge base for:
72
+ - **Related projects** with similar features
73
+ - **Reusable components** and patterns
74
+ - **Historical decisions** and their outcomes
75
+ - **Lessons learned** from past projects
76
+
77
+ Output format:
78
+ ```markdown
79
+ ## Correlation Analysis
80
+
81
+ ### Research Plan (研究计划)
82
+ - **Key Objectives**: 每个选项要回答的核心问题,评估需要的数据/信息
83
+ - **Research Methods**: 收集和分析数据的方法,使用的工具或方法论
84
+ - **Evaluation Criteria**: 比较选项的指标/基准,可行性判断标准
85
+ - **Expected Outcomes**: 预期的研究发现,研究后的下一步行动
86
+
87
+ ### Related Projects
88
+ | Project | Similarity | Reusable Components |
89
+ |---------|------------|---------------------|
90
+ | [Project A] | 85% | [Component list] |
91
+ | [Project B] | 60% | [Component list] |
92
+
93
+ ### Overlapping Technology
94
+ - [Tech stack overlap]
95
+ - [Shared libraries]
96
+ - [Common patterns]
97
+
98
+ ### Lessons from History
99
+ - [Lesson 1]: [Context]
100
+ - [Lesson 2]: [Context]
101
+
102
+ ### Risks Based on History
103
+ - [Risk from past project]: [How we'll address it]
104
+ ```
105
+
106
+ **Completion Criteria (完成验收标准)**:
107
+
108
+ 完成关联分析后,应能够回答以下问题:
109
+
110
+ 1. **Key Objectives (关键目标)**
111
+ - 每个选项要回答的核心问题是什么?
112
+ - 评估需要哪些数据/信息?
113
+
114
+ 2. **Research Methods (研究方法)**
115
+ - 如何收集和分析数据?
116
+ - 使用的工具或方法论是什么?
117
+
118
+ 3. **Evaluation Criteria (评估标准)**
119
+ - 比较选项的指标、基准或定性因素是什么?
120
+ - 可行性/成功的判断标准是什么?
121
+
122
+ 4. **Expected Outcomes (预期成果)**
123
+ - 可能的研究发现或结果是什么?
124
+ - 研究后的下一步行动是什么?
125
+
126
+ 如果以上问题都有明确答案,则 Step 2 完成。
127
+
128
+ ---
129
+
130
+ ### Step 3: Best Practices Research (45 minutes)
131
+
132
+ For each key area (architecture, tech stack, security, UX, etc.):
133
+
134
+ 1. **Query local knowledge base** first
135
+ 2. **Search web** for latest practices if needed
136
+ 3. **Synthesize** recommendations
137
+
138
+ Output format:
139
+ ```markdown
140
+ ## Industry Best Practices
141
+
142
+ ### [Area: e.g., Frontend Architecture]
143
+ **Practice**: [Specific practice]
144
+ **Rationale**: [Why this is recommended]
145
+ **Sources**: [Citations]
146
+ **Applicability**: [How this applies to current project]
147
+
148
+ ### [Area: e.g., API Design]
149
+ ...
150
+ ```
151
+
152
+ ---
153
+
154
+ ### Step 4: Feasibility Assessment (30 minutes)
155
+
156
+ Assess across multiple dimensions:
157
+
158
+ ```markdown
159
+ ## Feasibility Assessment
160
+
161
+ ### Technical Feasibility: ⭐⭐⭐⭐☆ (4/5)
162
+ **Strengths**:
163
+ - [Strength 1]
164
+ - [Strength 2]
165
+
166
+ **Challenges**:
167
+ - [Challenge 1]: [Mitigation strategy]
168
+ - [Challenge 2]: [Mitigation strategy]
169
+
170
+ ### Time Estimate: 4 hours
171
+ **Breakdown**:
172
+ - Research & Planning: 30m
173
+ - Design: 1h
174
+ - Implementation: 2h
175
+ - Testing: 30m
176
+
177
+ ### Complexity: Medium
178
+ **Reasoning**: [Explain complexity assessment]
179
+
180
+ ### Risk Assessment
181
+ | Risk | Severity | Mitigation |
182
+ |------|----------|------------|
183
+ | [Risk 1] | High | [Mitigation] |
184
+ | [Risk 2] | Medium | [Mitigation] |
185
+ ```
186
+
187
+ ---
188
+
189
+ ### Step 5: Recommendations (15 minutes)
190
+
191
+ ```markdown
192
+ ## Recommendations
193
+
194
+ ### Recommended Tech Stack
195
+ - [Frontend]: [Choice + rationale]
196
+ - [Backend]: [Choice + rationale]
197
+ - [Database]: [Choice + rationale]
198
+ - [Other]: [Choice + rationale]
199
+
200
+ ### Suggested Architecture
201
+ [High-level architecture description]
202
+
203
+ ### Potential Issues to Watch
204
+ 1. [Issue 1]: [Monitoring approach]
205
+ 2. [Issue 2]: [Monitoring approach]
206
+
207
+ ### Next Steps (Phase 2)
208
+ 1. [Step 1]
209
+ 2. [Step 2]
210
+ 3. [Step 3]
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Quality Checklist
216
+
217
+ Before finalizing, ensure:
218
+
219
+ - [ ] Requirement summary is clear and complete
220
+ - [ ] Correlation analysis found related work/patterns
221
+ - [ ] Best practices are cited with sources
222
+ - [ ] Feasibility has concrete ratings (not vague)
223
+ - [ ] Time estimate is justified
224
+ - [ ] Risks have mitigation strategies
225
+ - [ ] Recommendations are actionable
226
+
227
+ ---
228
+
229
+ ## Output Format
230
+
231
+ Final output should be saved as `feasibility-report.md` in:
232
+ ```
233
+ development/projects/{projectId}/phase1/feasibility-report.md
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Example Output
239
+
240
+ See `development/projects/examples/phase1/feasibility-report.md` for a complete example.
241
+
242
+ ---
243
+
244
+ ## Notes for AI
245
+
246
+ - **Be specific**: Use concrete examples, not abstract advice
247
+ - **Cite sources**: Always reference where information comes from
248
+ - **Quantify**: Use numbers when possible (similarity scores, time estimates)
249
+ - **Be honest**: If knowledge is missing, state it explicitly
250
+ - **Think ahead**: Consider Phase 2 (approval) - what will Claude need to know?
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Workflow Type Definitions (JavaScript version)
3
+ * Shared types for the NotebookLM + Claude collaboration workflow
4
+ */
5
+
6
+ // Project phase enum
7
+ const ProjectPhase = {
8
+ RESEARCH: 'research', // Phase 1: NotebookLM feasibility analysis
9
+ APPROVAL: 'approval', // Phase 2: Claude review and consensus
10
+ PLANNING: 'planning', // Phase 3: PRD and prototype
11
+ DEVELOPMENT: 'development',// Phase 4: Implementation
12
+ DEPLOYMENT: 'deployment' // Phase 5: Release
13
+ };
14
+
15
+ // Project status
16
+ const ProjectStatus = {
17
+ PENDING: 'pending',
18
+ IN_PROGRESS: 'in_progress',
19
+ COMPLETED: 'completed',
20
+ FAILED: 'failed',
21
+ ON_HOLD: 'on_hold'
22
+ };
23
+
24
+ // Knowledge source types
25
+ const KnowledgeSourceType = {
26
+ LOCAL_FILE: 'local_file',
27
+ LOCAL_DIRECTORY: 'local_directory',
28
+ NOTEBOOKLM: 'notebooklm',
29
+ WEB_SEARCH: 'web_search',
30
+ WEB_URL: 'web_url'
31
+ };
32
+
33
+ module.exports = {
34
+ ProjectPhase,
35
+ ProjectStatus,
36
+ KnowledgeSourceType
37
+ };
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Web Search Module - Bing HTML-based search
3
+ *
4
+ * Provides free web search by parsing Bing's HTML results.
5
+ */
6
+
7
+ const https = require('https');
8
+
9
+ // ============================================================================
10
+ // Configuration
11
+ // ============================================================================
12
+
13
+ const SEARCH_TIMEOUT = 10000;
14
+ const MAX_RESULTS = 5;
15
+ const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36';
16
+
17
+ // ============================================================================
18
+ // Web Search Class
19
+ // ============================================================================
20
+
21
+ class WebSearch {
22
+ /**
23
+ * Search Bing and return results
24
+ */
25
+ static async search(query, options = {}) {
26
+ const {
27
+ maxResults = MAX_RESULTS,
28
+ timeout = SEARCH_TIMEOUT
29
+ } = options;
30
+
31
+ return new Promise((resolve, reject) => {
32
+ const encodedQuery = encodeURIComponent(query);
33
+ const searchUrl = `https://www.bing.com/search?q=${encodedQuery}&setlang=en`;
34
+
35
+ const urlObj = new URL(searchUrl);
36
+ const requestOptions = {
37
+ hostname: urlObj.hostname,
38
+ path: urlObj.pathname + urlObj.search,
39
+ method: 'GET',
40
+ headers: {
41
+ 'User-Agent': USER_AGENT,
42
+ 'Accept': 'text/html',
43
+ 'Accept-Language': 'en-US,en;q=0.9'
44
+ },
45
+ timeout: timeout
46
+ };
47
+
48
+ const req = https.get(requestOptions, (res) => {
49
+ // Handle redirects
50
+ if (res.statusCode === 301 || res.statusCode === 302) {
51
+ const redirectUrl = res.headers.location;
52
+ if (redirectUrl) {
53
+ this.followRedirect(redirectUrl, options)
54
+ .then(resolve)
55
+ .catch(reject);
56
+ return;
57
+ }
58
+ }
59
+
60
+ if (res.statusCode !== 200) {
61
+ reject(new Error(`HTTP ${res.statusCode}`));
62
+ return;
63
+ }
64
+
65
+ let data = '';
66
+ res.on('data', (chunk) => {
67
+ data += chunk;
68
+ });
69
+
70
+ res.on('end', () => {
71
+ try {
72
+ const results = this.parseHTML(data, maxResults);
73
+ resolve(results);
74
+ } catch (error) {
75
+ reject(new Error(`Parse error: ${error.message}`));
76
+ }
77
+ });
78
+ });
79
+
80
+ req.on('timeout', () => {
81
+ req.destroy();
82
+ reject(new Error('Request timed out'));
83
+ });
84
+
85
+ req.on('error', (error) => {
86
+ reject(new Error(`Request failed: ${error.message}`));
87
+ });
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Follow HTTP redirect
93
+ */
94
+ static followRedirect(url, options) {
95
+ return new Promise((resolve, reject) => {
96
+ const urlObj = new URL(url);
97
+ const isHttps = urlObj.protocol === 'https:';
98
+ const http = isHttps ? https : require('http');
99
+
100
+ const requestOptions = {
101
+ hostname: urlObj.hostname,
102
+ path: urlObj.pathname + urlObj.search,
103
+ method: 'GET',
104
+ headers: { 'User-Agent': USER_AGENT },
105
+ timeout: options.timeout || SEARCH_TIMEOUT
106
+ };
107
+
108
+ const req = http.get(requestOptions, (res) => {
109
+ if (res.statusCode === 301 || res.statusCode === 302) {
110
+ const redirectUrl = res.headers.location;
111
+ if (redirectUrl) {
112
+ this.followRedirect(redirectUrl, options)
113
+ .then(resolve)
114
+ .catch(reject);
115
+ return;
116
+ }
117
+ }
118
+
119
+ let data = '';
120
+ res.on('data', (chunk) => { data += chunk; });
121
+ res.on('end', () => {
122
+ try {
123
+ const results = this.parseHTML(data, options.maxResults || MAX_RESULTS);
124
+ resolve(results);
125
+ } catch (error) {
126
+ reject(error);
127
+ }
128
+ });
129
+ });
130
+
131
+ req.on('error', reject);
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Parse Bing HTML results
137
+ */
138
+ static parseHTML(html, maxResults) {
139
+ const results = [];
140
+
141
+ // Bing result patterns
142
+ // Look for <li class="b_algo"> or <li class="b_result">
143
+ const algoRegex = /<li[^>]*class="b_algo"[^>]*>([\s\S]*?)<\/li>/g;
144
+ let match;
145
+ let count = 0;
146
+
147
+ while ((match = algoRegex.exec(html)) !== null && count < maxResults) {
148
+ const block = match[1];
149
+ count++;
150
+
151
+ // Extract title (from <h2><a> tag)
152
+ const titleMatch = block.match(/<h2><a[^>]*>([^<]+)<\/a>/);
153
+ let title = titleMatch ? this.decodeHTML(titleMatch[1]) : '';
154
+
155
+ // Also try alternative pattern
156
+ if (!title) {
157
+ const altTitleMatch = block.match(/<a[^>]*h="[^"]*"[^>]*>([^<]*(?:<strong>[^<]*<\/strong>[^<]*)*)<\/a>/);
158
+ if (altTitleMatch) {
159
+ title = this.decodeHTML(altTitleMatch[1].replace(/<strong>/g, '').replace(/<\/strong>/g, ''));
160
+ }
161
+ }
162
+
163
+ // Extract URL
164
+ let url = '';
165
+ const urlMatch = block.match(/<a[^>]*href="([^"]+)"/);
166
+ if (urlMatch) {
167
+ url = urlMatch[1];
168
+
169
+ // Decode &amp; entities in URL
170
+ url = url.replace(/&amp;/g, '&');
171
+
172
+ // Clean up Bing redirect URLs
173
+ // Format: https://www.bing.com/ck/a?!&p=...&u=<encoded_url>
174
+ if (url.includes('bing.com/ck/a')) {
175
+ const uMatch = url.match(/[?&]u=([^&]+)/);
176
+ if (uMatch) {
177
+ let encodedUrl = uMatch[1];
178
+ try {
179
+ // The URL is double-encoded, decode twice
180
+ let decodedUrl = decodeURIComponent(encodedUrl);
181
+ decodedUrl = decodeURIComponent(decodedUrl);
182
+
183
+ // Bing sometimes adds 'a1' prefix to the final URL
184
+ // Remove it and decode the rest as base64
185
+ if (decodedUrl.startsWith('a1')) {
186
+ const base64Part = decodedUrl.substring(2);
187
+ decodedUrl = Buffer.from(base64Part, 'base64').toString('utf-8');
188
+ }
189
+
190
+ url = decodedUrl;
191
+ } catch (e) {
192
+ // If decode fails, try alternative
193
+ try {
194
+ if (encodedUrl.startsWith('a1')) {
195
+ const base64Part = encodedUrl.substring(2);
196
+ url = Buffer.from(base64Part, 'base64').toString('utf-8');
197
+ } else {
198
+ url = decodeURIComponent(encodedUrl);
199
+ }
200
+ } catch (e2) {}
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ // Extract snippet (from <p> or <div>)
207
+ let snippet = '';
208
+ const snippetMatch = block.match(/<p[^>]*>([\s\S]*?)<\/p>/);
209
+ if (snippetMatch) {
210
+ snippet = this.decodeHTML(snippetMatch[1]
211
+ .replace(/<strong>/g, '').replace(/<\/strong>/g, '')
212
+ .replace(/<b>/g, '').replace(/<\/b>/g, '')
213
+ .replace(/<[^>]+>/g, ' ')
214
+ .replace(/\s+/g, ' ')
215
+ .trim());
216
+ }
217
+
218
+ // Skip if no valid URL
219
+ if (!url || url.startsWith('#')) continue;
220
+
221
+ results.push({
222
+ title: title || 'Untitled',
223
+ url,
224
+ excerpt: snippet || 'No description available.',
225
+ source: 'bing'
226
+ });
227
+ }
228
+
229
+ return results;
230
+ }
231
+
232
+ /**
233
+ * Decode HTML entities
234
+ */
235
+ static decodeHTML(text) {
236
+ const entities = {
237
+ '&amp;': '&',
238
+ '&lt;': '<',
239
+ '&gt;': '>',
240
+ '&quot;': '"',
241
+ '&#39;': "'",
242
+ '&apos;': "'",
243
+ '&nbsp;': ' ',
244
+ '&middot;': '·'
245
+ };
246
+
247
+ return text.replace(/&[^;]+;/g, (entity) => {
248
+ return entities[entity] || entity;
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Deduplicate results by URL
254
+ */
255
+ static dedupeResults(results) {
256
+ const seen = new Set();
257
+ return results.filter(result => {
258
+ const normalizedUrl = result.url
259
+ .replace(/^https?:\/\//, '')
260
+ .replace(/\/$/, '')
261
+ .toLowerCase();
262
+
263
+ if (seen.has(normalizedUrl)) return false;
264
+ seen.add(normalizedUrl);
265
+ return true;
266
+ });
267
+ }
268
+ }
269
+
270
+ // ============================================================================
271
+ // Exports
272
+ // ============================================================================
273
+
274
+ module.exports = {
275
+ WebSearch,
276
+ SEARCH_TIMEOUT,
277
+ MAX_RESULTS
278
+ };
@@ -27,8 +27,8 @@
27
27
  }
28
28
  ],
29
29
  "metadata": {
30
- "version": "1.0.11",
31
- "generated_at": "2026-01-15T08:24:34.719Z",
30
+ "version": "1.1.2",
31
+ "generated_at": "2026-01-17T09:45:22.671Z",
32
32
  "skill_count": 1,
33
33
  "categories": {
34
34
  "tools": {