sumulige-claude 1.5.1 โ†’ 1.5.2

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 (219) 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/cli.js +1 -1
  48. package/package.json +25 -3
  49. package/.claude/.kickoff-hint.txt +0 -52
  50. package/.claude/.sumulige-claude-version +0 -1
  51. package/.claude/.version +0 -1
  52. package/.claude/AGENTS.md +0 -42
  53. package/.claude/ANCHORS.md +0 -40
  54. package/.claude/CLAUDE.md +0 -138
  55. package/.claude/MEMORY.md +0 -69
  56. package/.claude/PROJECT_LOG.md +0 -101
  57. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  58. package/.claude/USAGE.md +0 -175
  59. package/.claude/boris-optimizations.md +0 -167
  60. package/.claude/handoffs/INDEX.md +0 -21
  61. package/.claude/handoffs/LATEST.md +0 -76
  62. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  63. package/.claude/quality-gate.json +0 -82
  64. package/.claude/rag/skill-index.json +0 -135
  65. package/.claude/settings.json +0 -99
  66. package/.claude/settings.local.json +0 -175
  67. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  68. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  69. package/.claude/templates/TASK_PLAN.md +0 -121
  70. package/.claude/templates/hooks/README.md +0 -302
  71. package/.claude/templates/hooks/hook.sh.template +0 -94
  72. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  73. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  74. package/.claude/templates/hooks/validate.js +0 -173
  75. package/.claude/templates/tasks/develop.md +0 -69
  76. package/.claude/templates/tasks/research.md +0 -64
  77. package/.claude/templates/tasks/test.md +0 -96
  78. package/.claude/thinking-routes/.last-sync +0 -1
  79. package/.claude/thinking-routes/QUICKREF.md +0 -98
  80. package/.claude/workflow/document-scanner.js +0 -426
  81. package/.claude/workflow/knowledge-engine.js +0 -941
  82. package/.claude/workflow/notebooklm/browser.js +0 -1028
  83. package/.claude/workflow/phases/phase1-research.js +0 -578
  84. package/.claude/workflow/phases/phase1-research.ts +0 -465
  85. package/.claude/workflow/phases/phase2-approve.js +0 -722
  86. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  87. package/.claude/workflow/phases/phase4-develop.js +0 -894
  88. package/.claude/workflow/search-cache.js +0 -230
  89. package/.claude/workflow/templates/approval.md +0 -315
  90. package/.claude/workflow/templates/development.md +0 -377
  91. package/.claude/workflow/templates/planning.md +0 -328
  92. package/.claude/workflow/templates/research.md +0 -250
  93. package/.claude/workflow/types.js +0 -37
  94. package/.claude/workflow/web-search.js +0 -278
  95. package/.claude-plugin/marketplace.json +0 -71
  96. package/.github/workflows/sync-skills.yml +0 -74
  97. package/.versionrc +0 -25
  98. package/AGENTS.md +0 -580
  99. package/CHANGELOG.md +0 -481
  100. package/CLAUDE-template.md +0 -114
  101. package/DEV_TOOLS_GUIDE.md +0 -190
  102. package/PROJECT_STRUCTURE.md +0 -266
  103. package/Q&A.md +0 -325
  104. package/config/defaults.json +0 -34
  105. package/config/official-skills.json +0 -183
  106. package/config/quality-gate.json +0 -67
  107. package/config/skill-categories.json +0 -40
  108. package/config/version-manifest.json +0 -85
  109. package/demos/power-3d-scatter.html +0 -683
  110. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  111. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  112. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  113. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  114. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  115. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  116. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  117. package/development/knowledge-base/.index.clean.json +0 -1
  118. package/development/knowledge-base/.index.json +0 -486
  119. package/development/knowledge-base/test-best-practices.md +0 -29
  120. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  121. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  122. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  123. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  124. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  131. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  132. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  143. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  144. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  145. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  146. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  147. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  148. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  149. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  150. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  151. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  152. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  153. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  154. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  155. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  157. package/development/todos/.state.json +0 -19
  158. package/development/todos/INDEX.md +0 -63
  159. package/development/todos/active/_README.md +0 -49
  160. package/development/todos/archived/_README.md +0 -11
  161. package/development/todos/backlog/_README.md +0 -11
  162. package/development/todos/backlog/mcp-integration.md +0 -35
  163. package/development/todos/completed/_README.md +0 -11
  164. package/development/todos/completed/boris-optimizations.md +0 -39
  165. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  166. package/development/todos/completed/develop/todo-system.md +0 -47
  167. package/development/todos/completed/develop/web-search-integration.md +0 -83
  168. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  169. package/docs/DEVELOPMENT.md +0 -461
  170. package/docs/MARKETPLACE.md +0 -352
  171. package/docs/RELEASE.md +0 -93
  172. package/jest.config.js +0 -63
  173. package/lib/commands.js +0 -3588
  174. package/lib/config-manager.js +0 -441
  175. package/lib/config-schema.js +0 -408
  176. package/lib/config-validator.js +0 -330
  177. package/lib/config.js +0 -122
  178. package/lib/errors.js +0 -305
  179. package/lib/incremental-sync.js +0 -274
  180. package/lib/marketplace.js +0 -487
  181. package/lib/migrations.js +0 -154
  182. package/lib/permission-audit.js +0 -255
  183. package/lib/quality-gate.js +0 -431
  184. package/lib/quality-rules.js +0 -373
  185. package/lib/utils.js +0 -150
  186. package/lib/version-check.js +0 -169
  187. package/lib/version-manifest.js +0 -171
  188. package/project-paradigm.md +0 -313
  189. package/prompts/how-to-find.md +0 -163
  190. package/prompts/linus-architect.md +0 -71
  191. package/prompts/software-architect.md +0 -173
  192. package/prompts/web-designer.md +0 -249
  193. package/scripts/fix-hooks.mjs +0 -97
  194. package/scripts/sync-external.mjs +0 -298
  195. package/scripts/sync-to-home.sh +0 -108
  196. package/scripts/update-registry.mjs +0 -325
  197. package/sources.yaml +0 -83
  198. package/tests/README.md +0 -263
  199. package/tests/commands.test.js +0 -1086
  200. package/tests/config-manager.test.js +0 -677
  201. package/tests/config-schema.test.js +0 -425
  202. package/tests/config-validator.test.js +0 -436
  203. package/tests/config.test.js +0 -100
  204. package/tests/errors.test.js +0 -477
  205. package/tests/manual/phase1-e2e.sh +0 -389
  206. package/tests/manual/phase2-test-cases.md +0 -311
  207. package/tests/manual/phase3-test-cases.md +0 -309
  208. package/tests/manual/phase4-test-cases.md +0 -414
  209. package/tests/manual/test-cases.md +0 -417
  210. package/tests/marketplace.test.js +0 -420
  211. package/tests/migrations.test.js +0 -187
  212. package/tests/quality-gate.test.js +0 -679
  213. package/tests/quality-rules.test.js +0 -619
  214. package/tests/sync-external.test.js +0 -214
  215. package/tests/update-registry.test.js +0 -251
  216. package/tests/utils.test.js +0 -171
  217. package/tests/version-check.test.js +0 -75
  218. package/tests/web-search.test.js +0 -392
  219. package/thinkinglens-silent.md +0 -138
@@ -1,941 +0,0 @@
1
- /**
2
- * Knowledge Engine (JavaScript version)
3
- *
4
- * Integrates NotebookLM knowledge capabilities with local knowledge base
5
- * and web search for Phase 1 research.
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- // Import DocumentScanner
12
- const { DocumentScanner } = require('./document-scanner.js');
13
-
14
- // Import WebSearch and SearchCache
15
- const { WebSearch } = require('./web-search.js');
16
- const { SearchCache } = require('./search-cache.js');
17
-
18
- // Try to import NotebookLM browser module
19
- let NotebookLMClient = null;
20
- let getClient = null;
21
- try {
22
- const notebooklm = require('./notebooklm/browser.js');
23
- NotebookLMClient = notebooklm.NotebookLMClient;
24
- getClient = notebooklm.getClient;
25
- } catch (e) {
26
- // NotebookLM not available, will use fallback
27
- }
28
-
29
- // ============================================================================
30
- // Configuration
31
- // ============================================================================
32
-
33
- const KNOWLEDGE_BASE_DIR = path.join(process.cwd(), 'development/knowledge-base');
34
- const PROJECTS_DIR = path.join(process.cwd(), 'development/projects');
35
- const KNOWLEDGE_INDEX_FILE = path.join(KNOWLEDGE_BASE_DIR, '.index.json');
36
-
37
- // ============================================================================
38
- // Knowledge Engine Class
39
- // ============================================================================
40
-
41
- class KnowledgeEngine {
42
- constructor() {
43
- this.index = { sources: [], lastUpdated: 0 };
44
- this.indexLoaded = false;
45
- this.ensureDirectories();
46
- }
47
-
48
- // --------------------------------------------------------------------------
49
- // Initialization
50
- // --------------------------------------------------------------------------
51
-
52
- ensureDirectories() {
53
- [KNOWLEDGE_BASE_DIR, PROJECTS_DIR].forEach(dir => {
54
- if (!fs.existsSync(dir)) {
55
- fs.mkdirSync(dir, { recursive: true });
56
- }
57
- });
58
- }
59
-
60
- loadIndex() {
61
- if (this.indexLoaded) return;
62
-
63
- try {
64
- if (fs.existsSync(KNOWLEDGE_INDEX_FILE)) {
65
- const content = fs.readFileSync(KNOWLEDGE_INDEX_FILE, 'utf-8');
66
- this.index = JSON.parse(content);
67
- }
68
- this.indexLoaded = true;
69
- } catch (error) {
70
- console.warn('Failed to load knowledge index, starting fresh:', error);
71
- this.index = { sources: [], lastUpdated: Date.now() };
72
- this.indexLoaded = true;
73
- }
74
- }
75
-
76
- saveIndex() {
77
- try {
78
- fs.writeFileSync(
79
- KNOWLEDGE_INDEX_FILE,
80
- JSON.stringify(this.index, null, 2),
81
- 'utf-8'
82
- );
83
- this.index.lastUpdated = Date.now();
84
- } catch (error) {
85
- console.error('Failed to save knowledge index:', error);
86
- }
87
- }
88
-
89
- // --------------------------------------------------------------------------
90
- // Knowledge Source Management
91
- // --------------------------------------------------------------------------
92
-
93
- generateId() {
94
- return `src_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
95
- }
96
-
97
- getMimeType(ext) {
98
- const mimeTypes = {
99
- '.md': 'text/markdown',
100
- '.txt': 'text/plain',
101
- '.json': 'application/json',
102
- '.pdf': 'application/pdf',
103
- '.html': 'text/html',
104
- '.js': 'text/javascript',
105
- '.ts': 'text/typescript',
106
- '.py': 'text/x-python',
107
- '.rs': 'text/x-rust'
108
- };
109
- return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
110
- }
111
-
112
- /**
113
- * Add a knowledge source to the index
114
- */
115
- addSource(source) {
116
- this.loadIndex();
117
-
118
- const id = this.generateId();
119
- const newSource = {
120
- ...source,
121
- id,
122
- addedAt: Date.now(),
123
- lastAccessed: Date.now()
124
- };
125
-
126
- this.index.sources.push(newSource);
127
- this.saveIndex();
128
-
129
- return id;
130
- }
131
-
132
- /**
133
- * Add a local file as knowledge source
134
- */
135
- addFile(filePath, tags = [], options = {}) {
136
- const resolvedPath = path.resolve(filePath);
137
- if (!fs.existsSync(resolvedPath)) {
138
- throw new Error(`File not found: ${filePath}`);
139
- }
140
-
141
- const stats = fs.statSync(resolvedPath);
142
- const ext = path.extname(resolvedPath);
143
-
144
- // Scan file for content and metadata
145
- const scanResult = DocumentScanner.scanFile(resolvedPath, {
146
- includeContent: true,
147
- maxContentSize: options.maxContentSize || 500 * 1024
148
- });
149
-
150
- // Build title from front matter or filename
151
- let title = path.basename(resolvedPath);
152
- let description = `Local file: ${resolvedPath}`;
153
-
154
- if (scanResult.frontMatter && scanResult.frontMatter.title) {
155
- title = scanResult.frontMatter.title;
156
- }
157
- if (scanResult.frontMatter && scanResult.frontMatter.description) {
158
- description = scanResult.frontMatter.description;
159
- }
160
-
161
- return this.addSource({
162
- type: 'local_file',
163
- path: resolvedPath,
164
- title,
165
- description,
166
- tags: tags.concat(scanResult.frontMatter?.tags?.split(',') || []),
167
- size: stats.size,
168
- contentType: scanResult.contentType,
169
-
170
- // New fields from scanning
171
- scannable: scanResult.scannable,
172
- wordCount: scanResult.wordCount,
173
- lineCount: scanResult.lineCount,
174
- headings: scanResult.headings,
175
- links: scanResult.links,
176
- codeBlocks: scanResult.codeBlocks,
177
- content: scanResult.content,
178
- snippet: scanResult.snippet,
179
- checksum: scanResult.checksum,
180
- lastModified: scanResult.lastModified,
181
- indexedAt: Date.now()
182
- });
183
- }
184
-
185
- /**
186
- * Add a local directory as knowledge source
187
- * Recursively scans all supported files in the directory
188
- */
189
- addDirectory(dirPath, tags = [], options = {}) {
190
- const resolvedPath = path.resolve(dirPath);
191
- if (!fs.existsSync(resolvedPath)) {
192
- throw new Error(`Directory not found: ${dirPath}`);
193
- }
194
-
195
- const {
196
- recursive = true,
197
- maxDepth = 10,
198
- includePatterns = [],
199
- excludePatterns = ['node_modules', '.git', 'dist', 'build', 'coverage', 'sessions', '.claude']
200
- } = options;
201
-
202
- // Scan the directory for files
203
- const scanResults = DocumentScanner.scanDirectory(resolvedPath, {
204
- recursive,
205
- maxDepth,
206
- includePatterns,
207
- excludePatterns
208
- });
209
-
210
- // Add each file as a separate source
211
- const addedIds = [];
212
- for (const result of scanResults) {
213
- if (!result.scannable) continue;
214
-
215
- let title = path.basename(result.path);
216
- let description = `File: ${result.path}`;
217
-
218
- // Use front matter if available
219
- if (result.frontMatter && result.frontMatter.title) {
220
- title = result.frontMatter.title;
221
- }
222
- if (result.frontMatter && result.frontMatter.description) {
223
- description = result.frontMatter.description;
224
- }
225
-
226
- const id = this.addSource({
227
- type: 'local_file',
228
- path: result.path,
229
- title,
230
- description,
231
- tags: tags.concat(result.frontMatter?.tags?.split(',') || []),
232
- size: result.size,
233
- contentType: result.contentType,
234
- scannable: result.scannable,
235
- wordCount: result.wordCount,
236
- lineCount: result.lineCount,
237
- headings: result.headings,
238
- links: result.links,
239
- codeBlocks: result.codeBlocks,
240
- content: result.content,
241
- snippet: result.snippet,
242
- checksum: result.checksum,
243
- lastModified: result.lastModified,
244
- indexedAt: Date.now()
245
- });
246
- addedIds.push(id);
247
- }
248
-
249
- // Also add the directory itself as a container
250
- this.addSource({
251
- type: 'local_directory',
252
- path: resolvedPath,
253
- title: path.basename(resolvedPath),
254
- description: `Directory with ${addedIds.length} indexed files`,
255
- tags,
256
- fileCount: addedIds.length
257
- });
258
-
259
- return addedIds;
260
- }
261
-
262
- /**
263
- * Add a NotebookLM notebook as knowledge source
264
- */
265
- addNotebook(notebookUrl, title, tags = []) {
266
- return this.addSource({
267
- type: 'notebooklm',
268
- notebookUrl,
269
- title,
270
- description: `NotebookLM: ${title}`,
271
- tags
272
- });
273
- }
274
-
275
- /**
276
- * List all knowledge sources
277
- */
278
- listSources(filter = {}) {
279
- this.loadIndex();
280
-
281
- let sources = [...this.index.sources];
282
-
283
- if (filter.type) {
284
- sources = sources.filter(s => s.type === filter.type);
285
- }
286
-
287
- if (filter.tag) {
288
- sources = sources.filter(s => s.tags.includes(filter.tag));
289
- }
290
-
291
- return sources.sort((a, b) => b.lastAccessed - a.lastAccessed);
292
- }
293
-
294
- /**
295
- * Get a specific knowledge source by ID
296
- */
297
- getSource(id) {
298
- this.loadIndex();
299
- return this.index.sources.find(s => s.id === id) || null;
300
- }
301
-
302
- /**
303
- * Remove a knowledge source
304
- */
305
- removeSource(id) {
306
- this.loadIndex();
307
- const initialLength = this.index.sources.length;
308
- this.index.sources = this.index.sources.filter(s => s.id !== id);
309
-
310
- if (this.index.sources.length < initialLength) {
311
- this.saveIndex();
312
- return true;
313
- }
314
- return false;
315
- }
316
-
317
- /**
318
- * Check if a source needs reindexing due to file changes
319
- */
320
- needsReindex(source) {
321
- if (source.type !== 'local_file') return false;
322
- if (!fs.existsSync(source.path)) return false; // File deleted
323
-
324
- try {
325
- const stats = fs.statSync(source.path);
326
- // Check if file was modified since last index
327
- if (source.lastModified && stats.mtimeMs > source.lastModified) {
328
- return true;
329
- }
330
- // Also check checksum if available
331
- if (source.checksum) {
332
- const currentContent = fs.readFileSync(source.path, 'utf-8');
333
- const currentChecksum = require('crypto')
334
- .createHash('md5')
335
- .update(currentContent)
336
- .digest('hex');
337
- return currentChecksum !== source.checksum;
338
- }
339
- } catch (error) {
340
- return false;
341
- }
342
-
343
- return false;
344
- }
345
-
346
- /**
347
- * Reindex a single file source
348
- */
349
- reindexFile(source) {
350
- try {
351
- const scanResult = DocumentScanner.scanFile(source.path, {
352
- includeContent: true
353
- });
354
-
355
- // Update the source with new data
356
- Object.assign(source, {
357
- wordCount: scanResult.wordCount,
358
- lineCount: scanResult.lineCount,
359
- headings: scanResult.headings,
360
- links: scanResult.links,
361
- codeBlocks: scanResult.codeBlocks,
362
- content: scanResult.content,
363
- snippet: scanResult.snippet,
364
- checksum: scanResult.checksum,
365
- lastModified: scanResult.lastModified,
366
- indexedAt: Date.now()
367
- });
368
-
369
- this.saveIndex();
370
- return true;
371
- } catch (error) {
372
- console.warn(`Failed to reindex ${source.path}:`, error.message);
373
- return false;
374
- }
375
- }
376
-
377
- /**
378
- * Update index for all files that have changed
379
- */
380
- updateIndex(options = {}) {
381
- const { progressCallback } = options;
382
- this.loadIndex();
383
-
384
- const fileSources = this.index.sources.filter(s => s.type === 'local_file');
385
- let updatedCount = 0;
386
-
387
- for (const source of fileSources) {
388
- if (this.needsReindex(source)) {
389
- progressCallback?.(`Reindexing: ${source.title}`);
390
- if (this.reindexFile(source)) {
391
- updatedCount++;
392
- }
393
- }
394
- }
395
-
396
- // Remove sources for files that no longer exist
397
- const initialLength = this.index.sources.length;
398
- this.index.sources = this.index.sources.filter(s => {
399
- if (s.type === 'local_file') {
400
- return fs.existsSync(s.path);
401
- }
402
- return true;
403
- });
404
-
405
- if (this.index.sources.length < initialLength) {
406
- this.saveIndex();
407
- }
408
-
409
- return { updatedCount, removedCount: initialLength - this.index.sources.length };
410
- }
411
-
412
- // --------------------------------------------------------------------------
413
- // Knowledge Query
414
- // --------------------------------------------------------------------------
415
-
416
- calculateRelevance(question, source) {
417
- const questionLower = question.toLowerCase();
418
- const questionWords = questionLower.split(/\s+/).filter(w => w.length > 2);
419
-
420
- let score = 0;
421
- const weights = {
422
- title: 0.35,
423
- tags: 0.25,
424
- content: 0.30,
425
- headings: 0.10
426
- };
427
-
428
- // 1. Title matching
429
- const titleLower = (source.title || '').toLowerCase();
430
- const titleWords = titleLower.split(/\s+/);
431
- let titleMatches = 0;
432
- questionWords.forEach(word => {
433
- if (titleWords.some(t => t.includes(word) || word.includes(t))) {
434
- titleMatches++;
435
- }
436
- });
437
- score += (titleMatches / Math.max(questionWords.length, 1)) * weights.title;
438
-
439
- // 2. Tag matching
440
- if (source.tags && source.tags.length > 0) {
441
- let tagMatches = 0;
442
- questionWords.forEach(word => {
443
- if (source.tags.some(t => t.toLowerCase().includes(word))) {
444
- tagMatches++;
445
- }
446
- });
447
- score += (tagMatches / Math.max(questionWords.length, 1)) * weights.tags;
448
- }
449
-
450
- // 3. Content matching (full-text search)
451
- if (source.content || source.snippet) {
452
- const contentLower = (source.content || source.snippet || '').toLowerCase();
453
- let contentMatches = 0;
454
- questionWords.forEach(word => {
455
- if (contentLower.includes(word)) {
456
- contentMatches++;
457
- }
458
- });
459
- score += (contentMatches / Math.max(questionWords.length, 1)) * weights.content;
460
-
461
- // Boost for exact phrase match
462
- if (contentLower.includes(questionLower)) {
463
- score += 0.2;
464
- }
465
- }
466
-
467
- // 4. Headings matching
468
- if (source.headings && source.headings.length > 0) {
469
- let headingMatches = 0;
470
- questionWords.forEach(word => {
471
- if (source.headings.some(h => h.text && h.text.toLowerCase().includes(word))) {
472
- headingMatches++;
473
- }
474
- });
475
- score += (headingMatches / Math.max(questionWords.length, 1)) * weights.headings;
476
- }
477
-
478
- return Math.min(score, 1);
479
- }
480
-
481
- /**
482
- * Query the knowledge base with NotebookLM integration
483
- */
484
- async query(question, options = {}) {
485
- this.loadIndex();
486
-
487
- const {
488
- includeWeb = false,
489
- maxSources = 5,
490
- progressCallback,
491
- useNotebookLM = true
492
- } = options;
493
-
494
- const sources = [];
495
- const webResults = [];
496
- let notebooklmAnswer = null;
497
-
498
- // Step 1: Search local knowledge base
499
- await progressCallback?.('Searching local knowledge base...', 1, 5);
500
-
501
- const localSources = this.index.sources.filter(
502
- s => s.type === 'local_file' || s.type === 'local_directory'
503
- );
504
-
505
- for (const source of localSources.slice(0, maxSources)) {
506
- const relevance = this.calculateRelevance(question, source);
507
- if (relevance > 0.3) {
508
- sources.push({
509
- title: source.title,
510
- type: source.type,
511
- relevance,
512
- excerpt: `Excerpt from ${source.title}`
513
- });
514
- source.lastAccessed = Date.now();
515
- }
516
- }
517
-
518
- // Step 2: Query NotebookLM if available and enabled
519
- await progressCallback?.('Querying NotebookLM...', 2, 5);
520
-
521
- const notebooklmSources = this.index.sources.filter(s => s.type === 'notebooklm');
522
-
523
- if (useNotebookLM && notebooklmSources.length > 0 && getClient) {
524
- try {
525
- const client = getClient();
526
-
527
- // Check if authenticated
528
- const stats = client.getStats();
529
- if (!stats.authenticated) {
530
- sources.push({
531
- title: 'NotebookLM (requires auth)',
532
- type: 'notebooklm',
533
- relevance: 0.9,
534
- excerpt: 'NotebookLM requires authentication. Run: smc notebooklm auth'
535
- });
536
- } else {
537
- // Use the first NotebookLM source
538
- const notebookUrl = notebooklmSources[0].notebookUrl || null;
539
- notebooklmAnswer = await client.ask(notebookUrl, question, (msg) => {
540
- // Forward progress
541
- });
542
-
543
- sources.push({
544
- title: 'NotebookLM',
545
- type: 'notebooklm',
546
- relevance: 1.0,
547
- excerpt: notebooklmAnswer?.substring(0, 200) + '...'
548
- });
549
- }
550
- } catch (error) {
551
- sources.push({
552
- title: 'NotebookLM (error)',
553
- type: 'notebooklm',
554
- relevance: 0.5,
555
- excerpt: `NotebookLM query failed: ${error.message}`
556
- });
557
- }
558
- } else if (notebooklmSources.length === 0) {
559
- sources.push({
560
- title: 'NotebookLM',
561
- type: 'notebooklm',
562
- relevance: 0.5,
563
- excerpt: 'No NotebookLM sources added. Add one with: smc knowledge notebook <url> <title>'
564
- });
565
- }
566
-
567
- // Step 3: Web search
568
- if (includeWeb) {
569
- await progressCallback?.('Searching web for latest information...', 3, 5);
570
-
571
- try {
572
- // Check cache first
573
- const cached = SearchCache.get(question);
574
- if (cached && cached.length > 0) {
575
- webResults.push(...cached);
576
- await progressCallback?.('Using cached web results...', 4, 5);
577
- } else {
578
- // Perform fresh search
579
- const freshResults = await WebSearch.search(question, { maxResults: 5 });
580
- if (freshResults.length > 0) {
581
- webResults.push(...freshResults);
582
- // Cache the results
583
- SearchCache.set(question, freshResults);
584
- }
585
- }
586
- } catch (error) {
587
- // Graceful degradation - search failed but continue
588
- webResults.push({
589
- title: 'Web Search (unavailable)',
590
- url: '#',
591
- excerpt: `Web search is currently unavailable: ${error.message}. Try again later.`,
592
- source: 'error'
593
- });
594
- }
595
- }
596
-
597
- // Step 4: Synthesize answer
598
- await progressCallback?.('Synthesizing answer...', 4, 5);
599
-
600
- // Use NotebookLM answer if available, otherwise fall back to synthesis
601
- let finalAnswer;
602
- if (notebooklmAnswer) {
603
- finalAnswer = notebooklmAnswer;
604
- } else {
605
- finalAnswer = this.synthesizeAnswer(question, sources, webResults);
606
- }
607
-
608
- await progressCallback?.('Query complete!', 5, 5);
609
-
610
- return {
611
- answer: finalAnswer,
612
- sources: sources.slice(0, maxSources).sort((a, b) => b.relevance - a.relevance),
613
- webResults,
614
- confidence: this.calculateConfidence(sources, webResults),
615
- notebooklmUsed: !!notebooklmAnswer
616
- };
617
- }
618
-
619
- /**
620
- * Query NotebookLM directly (bypass local knowledge)
621
- */
622
- async queryNotebookLM(question, notebookUrl = null, progressCallback) {
623
- if (!getClient) {
624
- throw new Error('NotebookLM module not available. Install patchright: npm install patchright');
625
- }
626
-
627
- const client = getClient();
628
- const stats = client.getStats();
629
-
630
- if (!stats.authenticated) {
631
- throw new Error('Not authenticated. Run: smc notebooklm auth');
632
- }
633
-
634
- await progressCallback?.('Asking NotebookLM...');
635
- const answer = await client.ask(notebookUrl, question, (msg) => {
636
- // Forward progress silently or log
637
- });
638
-
639
- return {
640
- answer,
641
- notebooklmUsed: true,
642
- confidence: 0.95
643
- };
644
- }
645
-
646
- synthesizeAnswer(question, sources, webResults) {
647
- if (sources.length === 0 && (!webResults || webResults.length === 0)) {
648
- return `No relevant information found in the knowledge base for: "${question}"`;
649
- }
650
-
651
- let answer = `Based on the knowledge base, here's what I found regarding "${question}":\n\n`;
652
-
653
- if (sources.length > 0) {
654
- answer += `**Local Knowledge:**\n`;
655
- sources.forEach(source => {
656
- answer += `- ${source.title} (relevance: ${(source.relevance * 100).toFixed(0)}%)\n`;
657
- if (source.excerpt && source.excerpt !== 'Excerpt from ' + source.title) {
658
- answer += ` ${source.excerpt.substring(0, 150)}${source.excerpt.length > 150 ? '...' : ''}\n`;
659
- }
660
- });
661
- answer += '\n';
662
- }
663
-
664
- if (webResults && webResults.length > 0) {
665
- answer += `**Web Sources:**\n`;
666
- webResults.forEach(result => {
667
- // Format URL for display (decode if needed)
668
- let displayUrl = result.url;
669
- if (displayUrl.startsWith('a1aHR0c')) {
670
- // URL appears to be still encoded
671
- try {
672
- displayUrl = Buffer.from(displayUrl, 'base64').toString('ascii');
673
- // Extract the actual URL from the encoded string
674
- const urlMatch = displayUrl.match(/https?:\/\/[^&\s]+/);
675
- if (urlMatch) displayUrl = urlMatch[0];
676
- } catch (e) {}
677
- }
678
-
679
- answer += `- [${result.title}](${displayUrl})\n`;
680
- if (result.excerpt && result.excerpt !== 'No description available.') {
681
- answer += ` ${result.excerpt.substring(0, 200)}${result.excerpt.length > 200 ? '...' : ''}\n`;
682
- }
683
- });
684
- }
685
-
686
- return answer;
687
- }
688
-
689
- calculateConfidence(sources, webResults) {
690
- const sourceCount = sources.length + (webResults?.length || 0);
691
- const avgRelevance = sources.reduce((sum, s) => sum + s.relevance, 0) / Math.max(sources.length, 1);
692
-
693
- return Math.min((sourceCount / 5) * 0.5 + avgRelevance * 0.5, 1);
694
- }
695
-
696
- // --------------------------------------------------------------------------
697
- // Statistics
698
- // --------------------------------------------------------------------------
699
-
700
- getStats() {
701
- this.loadIndex();
702
-
703
- const sourcesByType = {};
704
- this.index.sources.forEach(s => {
705
- sourcesByType[s.type] = (sourcesByType[s.type] || 0) + 1;
706
- });
707
-
708
- return {
709
- totalSources: this.index.sources.length,
710
- sourcesByType,
711
- lastUpdated: this.index.lastUpdated
712
- };
713
- }
714
- }
715
-
716
- // ============================================================================
717
- // Singleton Instance
718
- // ============================================================================
719
-
720
- let knowledgeEngineInstance = null;
721
-
722
- function getKnowledgeEngine() {
723
- if (!knowledgeEngineInstance) {
724
- knowledgeEngineInstance = new KnowledgeEngine();
725
- }
726
- return knowledgeEngineInstance;
727
- }
728
-
729
- // ============================================================================
730
- // CLI Helpers
731
- // ============================================================================
732
-
733
- async function handleKnowledgeCommand(args) {
734
- const engine = getKnowledgeEngine();
735
- const [action, ...rest] = args;
736
-
737
- switch (action) {
738
- case 'add': {
739
- const [filePath, ...restArgs] = rest;
740
- if (!filePath) {
741
- console.error('Usage: smc knowledge add <file|directory> [tags...] [--recursive] [--max-depth=N]');
742
- process.exit(1);
743
- }
744
-
745
- try {
746
- // Parse options
747
- const tags = restArgs.filter(arg => !arg.startsWith('--'));
748
- const isRecursive = restArgs.includes('--recursive');
749
- const maxDepthMatch = restArgs.find(arg => arg.startsWith('--max-depth='));
750
- const maxDepth = maxDepthMatch ? parseInt(maxDepthMatch.split('=')[1]) : 10;
751
-
752
- const resolvedPath = path.resolve(filePath);
753
- const stats = fs.existsSync(resolvedPath) ? fs.statSync(resolvedPath) : null;
754
-
755
- if (stats && stats.isDirectory()) {
756
- // Add directory
757
- console.log(`๐Ÿ“ Scanning directory: ${filePath}`);
758
- const addedIds = engine.addDirectory(filePath, tags, {
759
- recursive: isRecursive !== false, // default true
760
- maxDepth
761
- });
762
- console.log(`โœ… Added ${addedIds.length} files from directory`);
763
- } else if (stats && stats.isFile()) {
764
- // Add single file
765
- const id = engine.addFile(filePath, tags);
766
- const source = engine.getSource(id);
767
- const scanInfo = source.scannable
768
- ? ` (${source.wordCount} words, ${source.headings?.length || 0} headings)`
769
- : ' (not scannable)';
770
- console.log(`โœ… Added: ${source.title}${scanInfo}`);
771
- } else {
772
- console.error(`โŒ Error: File not found: ${filePath}`);
773
- process.exit(1);
774
- }
775
- } catch (error) {
776
- console.error(`โŒ Error: ${error.message}`);
777
- process.exit(1);
778
- }
779
- break;
780
- }
781
-
782
- case 'list': {
783
- const sources = engine.listSources();
784
- console.log(`\n๐Ÿ“š Knowledge Base (${sources.length} sources):\n`);
785
-
786
- if (sources.length === 0) {
787
- console.log(' No knowledge sources yet.');
788
- console.log(' Add sources with: smc knowledge add <file|directory> [tags...]');
789
- } else {
790
- const icons = {
791
- local_file: '๐Ÿ“„',
792
- local_directory: '๐Ÿ“',
793
- notebooklm: '๐Ÿ““',
794
- web_search: '๐Ÿ”',
795
- web_url: '๐ŸŒ'
796
- };
797
-
798
- sources.forEach(source => {
799
- const icon = icons[source.type] || '๐Ÿ“„';
800
- const tags = source.tags && source.tags.length > 0 ? ` [${source.tags.join(', ')}]` : '';
801
- console.log(` ${icon} ${source.title}${tags}`);
802
-
803
- // Show file info
804
- if (source.scannable) {
805
- const wordInfo = source.wordCount ? `${source.wordCount} words` : '';
806
- const headingInfo = source.headings && source.headings.length > 0 ? `${source.headings.length} headings` : '';
807
- const details = [wordInfo, headingInfo].filter(Boolean).join(', ');
808
- if (details) {
809
- console.log(` ๐Ÿ“Š ${details}`);
810
- }
811
- }
812
-
813
- console.log(` Type: ${source.type} | Added: ${new Date(source.addedAt).toLocaleDateString()}`);
814
- });
815
- }
816
- console.log('');
817
- break;
818
- }
819
-
820
- case 'query': {
821
- const question = rest.join(' ').replace('--web', '').trim();
822
- if (!question) {
823
- console.error('Usage: smc knowledge query "<question>" [--web]');
824
- process.exit(1);
825
- }
826
-
827
- const includeWeb = rest.includes('--web');
828
- const result = await engine.query(question, { includeWeb });
829
-
830
- console.log(`\n${result.answer}\n`);
831
- console.log(`Confidence: ${(result.confidence * 100).toFixed(0)}%`);
832
- break;
833
- }
834
-
835
- case 'stats': {
836
- const stats = engine.getStats();
837
- console.log('\n๐Ÿ“Š Knowledge Base Statistics:\n');
838
- console.log(` Total Sources: ${stats.totalSources}`);
839
- console.log(' By Type:');
840
- Object.entries(stats.sourcesByType).forEach(([type, count]) => {
841
- console.log(` - ${type}: ${count}`);
842
- });
843
- console.log(` Last Updated: ${new Date(stats.lastUpdated).toLocaleString()}\n`);
844
- break;
845
- }
846
-
847
- case 'remove': {
848
- const [id] = rest;
849
- if (!id) {
850
- console.error('Usage: smc knowledge remove <source-id>');
851
- process.exit(1);
852
- }
853
-
854
- if (engine.removeSource(id)) {
855
- console.log(`โœ… Removed knowledge source: ${id}`);
856
- } else {
857
- console.error(`โŒ Source not found: ${id}`);
858
- process.exit(1);
859
- }
860
- break;
861
- }
862
-
863
- case 'update': {
864
- console.log('๐Ÿ”„ Checking for file changes...\n');
865
- const result = engine.updateIndex({
866
- progressCallback: (msg) => console.log(` ${msg}`)
867
- });
868
-
869
- if (result.updatedCount === 0 && result.removedCount === 0) {
870
- console.log('โœ… Index is up to date!');
871
- } else {
872
- console.log(`\nโœ… Updated: ${result.updatedCount} files`);
873
- if (result.removedCount > 0) {
874
- console.log(` Removed: ${result.removedCount} deleted files`);
875
- }
876
- }
877
- break;
878
- }
879
-
880
- case 'cache': {
881
- const [action] = rest;
882
- if (action === 'clear') {
883
- const cleared = SearchCache.clear();
884
- console.log(`๐Ÿ—‘๏ธ Cleared ${cleared} cached search results`);
885
- } else if (action === 'clean') {
886
- const cleaned = SearchCache.clean();
887
- console.log(`๐Ÿงน Cleaned ${cleaned} expired cache entries`);
888
- } else if (action === 'stats') {
889
- const stats = SearchCache.getStats();
890
- console.log('\n๐Ÿ“Š Search Cache Statistics:\n');
891
- console.log(` Total Entries: ${stats.totalEntries}`);
892
- console.log(` Valid Entries: ${stats.validEntries}`);
893
- console.log(` Cache Size: ${(stats.totalSize / 1024).toFixed(2)} KB`);
894
- console.log(` Cache Dir: ${stats.cacheDir}\n`);
895
- } else {
896
- console.log(`
897
- Search Cache Commands:
898
-
899
- smc knowledge cache clear Clear all cached results
900
- smc knowledge cache clean Remove expired entries
901
- smc knowledge cache stats Show cache statistics
902
- `);
903
- }
904
- break;
905
- }
906
-
907
- case 'sync': {
908
- console.log('๐Ÿ”„ NotebookLM sync pending - requires notebooklm-mcp integration');
909
- break;
910
- }
911
-
912
- default:
913
- console.log(`
914
- Knowledge Base Commands:
915
-
916
- smc knowledge add <file|directory> [tags...] Add a knowledge source
917
- smc knowledge list List all sources
918
- smc knowledge query "<question>" [--web] Query the knowledge base
919
- smc knowledge remove <source-id> Remove a source
920
- smc knowledge update Update index for changed files
921
- smc knowledge cache <clear|clean|stats> Manage search cache
922
- smc knowledge stats Show statistics
923
- smc knowledge sync Sync with NotebookLM
924
-
925
- Examples:
926
- smc knowledge add ./docs/best-practices.md architecture
927
- smc knowledge add ./docs --recursive
928
- smc knowledge list
929
- smc knowledge query "What are the best practices for API design?"
930
- smc knowledge query --web "React 19 new features"
931
- smc knowledge update
932
- smc knowledge cache stats
933
- `);
934
- }
935
- }
936
-
937
- module.exports = {
938
- KnowledgeEngine,
939
- getKnowledgeEngine,
940
- handleKnowledgeCommand
941
- };