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,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
- };