codeimpact 0.4.2 → 0.4.3

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.
package/dist/index.js CHANGED
@@ -28332,15 +28332,16 @@ var EXCLUDED_PATH_PATTERNS = [
28332
28332
  "knowledge/"
28333
28333
  ];
28334
28334
  function PATH_EXCLUSION_SQL(col = "path") {
28335
- return `${col} NOT LIKE '%node_modules%'
28336
- AND ${col} NOT LIKE '%.git%'
28335
+ return `${col} NOT LIKE '%node_modules/%'
28336
+ AND ${col} NOT LIKE '%.git/%'
28337
28337
  AND ${col} NOT LIKE '%/dist/%'
28338
28338
  AND ${col} NOT LIKE '%/build/%'
28339
28339
  AND ${col} NOT LIKE '%/venv/%'
28340
28340
  AND ${col} NOT LIKE '%/.venv/%'
28341
28341
  AND ${col} NOT LIKE '%/env/%'
28342
28342
  AND ${col} NOT LIKE '%__pycache__%'
28343
- AND ${col} NOT LIKE '%/knowledge/%'`;
28343
+ AND ${col} NOT LIKE '%/knowledge/%'
28344
+ AND ${col} NOT LIKE 'knowledge/%'`;
28344
28345
  }
28345
28346
  var Tier2Storage = class {
28346
28347
  db;
@@ -28392,6 +28393,33 @@ var Tier2Storage = class {
28392
28393
  const stmt = this.db.prepare("DELETE FROM files WHERE path = ?");
28393
28394
  stmt.run(path);
28394
28395
  }
28396
+ /**
28397
+ * Bulk-delete files whose paths match excluded patterns (node_modules, venv, etc.).
28398
+ * Call this during initialization to clean up stale data from previous indexing runs.
28399
+ * Returns the number of rows deleted.
28400
+ */
28401
+ purgeExcludedFiles() {
28402
+ const excludedIds = this.db.prepare(`
28403
+ SELECT id FROM files
28404
+ WHERE NOT (${PATH_EXCLUSION_SQL("path")})
28405
+ `).all();
28406
+ if (excludedIds.length === 0) return 0;
28407
+ const ids = excludedIds.map((r) => r.id);
28408
+ const chunkSize = 500;
28409
+ let deleted = 0;
28410
+ for (let i = 0; i < ids.length; i += chunkSize) {
28411
+ const chunk = ids.slice(i, i + chunkSize);
28412
+ const placeholders = chunk.map(() => "?").join(",");
28413
+ this.db.prepare(`DELETE FROM embeddings WHERE file_id IN (${placeholders})`).run(...chunk);
28414
+ this.db.prepare(`DELETE FROM symbols WHERE file_id IN (${placeholders})`).run(...chunk);
28415
+ this.db.prepare(`DELETE FROM imports WHERE file_id IN (${placeholders})`).run(...chunk);
28416
+ this.db.prepare(`DELETE FROM exports WHERE file_id IN (${placeholders})`).run(...chunk);
28417
+ this.db.prepare(`DELETE FROM dependencies WHERE source_file_id IN (${placeholders}) OR target_file_id IN (${placeholders})`).run(...chunk, ...chunk);
28418
+ const result = this.db.prepare(`DELETE FROM files WHERE id IN (${placeholders})`).run(...chunk);
28419
+ deleted += result.changes;
28420
+ }
28421
+ return deleted;
28422
+ }
28395
28423
  getAllFiles() {
28396
28424
  const stmt = this.db.prepare(`
28397
28425
  SELECT id, path, content_hash as contentHash, preview, language,
@@ -28671,6 +28699,10 @@ var Tier2Storage = class {
28671
28699
  const stmt = this.db.prepare("DELETE FROM dependencies WHERE source_file_id = ?");
28672
28700
  stmt.run(fileId);
28673
28701
  }
28702
+ /** Remove ALL dependency edges. Used before a full rebuild pass. */
28703
+ clearAllDependencies() {
28704
+ this.db.prepare("DELETE FROM dependencies").run();
28705
+ }
28674
28706
  // Project summary
28675
28707
  updateProjectSummary(name, description, languages, keyDirectories, architectureNotes) {
28676
28708
  const stmt = this.db.prepare(`
@@ -30294,6 +30326,28 @@ var ASTParser = class {
30294
30326
  import chokidar from "chokidar";
30295
30327
  import { EventEmitter } from "events";
30296
30328
  import { relative } from "path";
30329
+ function buildIgnoreMatcher(patterns) {
30330
+ const segments = /* @__PURE__ */ new Set();
30331
+ for (const p of patterns) {
30332
+ const m = p.match(/^\*\*\/([^/*]+)\/\*\*?$/);
30333
+ if (m && m[1]) {
30334
+ segments.add(m[1]);
30335
+ }
30336
+ }
30337
+ for (const s of ["node_modules", ".git", "venv", ".venv", "env", "__pycache__", "vendor", "knowledge", ".next", ".nuxt"]) {
30338
+ segments.add(s);
30339
+ }
30340
+ const segmentArray = [...segments];
30341
+ return (testPath) => {
30342
+ const normalised = testPath.replace(/\\/g, "/");
30343
+ for (const seg of segmentArray) {
30344
+ if (normalised.includes("/" + seg + "/") || normalised.endsWith("/" + seg) || normalised === seg) {
30345
+ return true;
30346
+ }
30347
+ }
30348
+ return false;
30349
+ };
30350
+ }
30297
30351
  var FileWatcher = class extends EventEmitter {
30298
30352
  watcher = null;
30299
30353
  projectPath;
@@ -30307,15 +30361,16 @@ var FileWatcher = class extends EventEmitter {
30307
30361
  if (this.watcher) {
30308
30362
  return;
30309
30363
  }
30364
+ const ignoreFn = buildIgnoreMatcher(this.ignorePatterns);
30310
30365
  this.watcher = chokidar.watch(this.projectPath, {
30311
30366
  ignored: [
30312
30367
  /(^|[\/\\])\../,
30313
30368
  // dotfiles
30314
- ...this.ignorePatterns
30369
+ ignoreFn
30315
30370
  ],
30316
30371
  persistent: true,
30317
- ignoreInitial: false,
30318
- // We want initial add events for indexing
30372
+ ignoreInitial: true,
30373
+ // Don't emit 'add' for existing files — initial index uses glob
30319
30374
  awaitWriteFinish: {
30320
30375
  stabilityThreshold: 300,
30321
30376
  pollInterval: 100
@@ -30334,7 +30389,7 @@ var FileWatcher = class extends EventEmitter {
30334
30389
  });
30335
30390
  }
30336
30391
  handleEvent(type, path) {
30337
- const relativePath = relative(this.projectPath, path);
30392
+ const relativePath = relative(this.projectPath, path).replace(/\\/g, "/");
30338
30393
  const event = {
30339
30394
  type,
30340
30395
  path,
@@ -30460,6 +30515,31 @@ function countLines(content) {
30460
30515
  }
30461
30516
 
30462
30517
  // src/indexing/indexer.ts
30518
+ var EXCLUDED_PATH_SEGMENTS = [
30519
+ "node_modules",
30520
+ ".git",
30521
+ "venv",
30522
+ ".venv",
30523
+ "env",
30524
+ "__pycache__",
30525
+ ".pytest_cache",
30526
+ ".mypy_cache",
30527
+ ".ruff_cache",
30528
+ ".tox",
30529
+ ".nox",
30530
+ "vendor",
30531
+ ".gradle",
30532
+ "Pods",
30533
+ "DerivedData",
30534
+ ".next",
30535
+ ".nuxt",
30536
+ ".svelte-kit",
30537
+ "knowledge"
30538
+ ];
30539
+ function isExcludedPath(relPath) {
30540
+ const segments = relPath.replace(/\\/g, "/").split("/");
30541
+ return segments.some((seg) => EXCLUDED_PATH_SEGMENTS.includes(seg));
30542
+ }
30463
30543
  var Indexer = class extends EventEmitter2 {
30464
30544
  config;
30465
30545
  embeddingGenerator;
@@ -30525,9 +30605,12 @@ var Indexer = class extends EventEmitter2 {
30525
30605
  }
30526
30606
  async indexFile(absolutePath) {
30527
30607
  try {
30608
+ const relativePath = relative2(this.config.projectPath, absolutePath).replace(/\\/g, "/");
30609
+ if (isExcludedPath(relativePath)) {
30610
+ return false;
30611
+ }
30528
30612
  const content = readFileSync3(absolutePath, "utf-8");
30529
30613
  const stats = statSync(absolutePath);
30530
- const relativePath = relative2(this.config.projectPath, absolutePath);
30531
30614
  const contentHash = hashContent(content);
30532
30615
  const existingFile = this.tier2.getFile(relativePath);
30533
30616
  if (existingFile && existingFile.contentHash === contentHash) {
@@ -30604,6 +30687,10 @@ var Indexer = class extends EventEmitter2 {
30604
30687
  this.isIndexing = true;
30605
30688
  this.emit("indexingStarted");
30606
30689
  try {
30690
+ const purged = this.tier2.purgeExcludedFiles();
30691
+ if (purged > 0) {
30692
+ console.error(`[Index] Purged ${purged} excluded files from database`);
30693
+ }
30607
30694
  const patterns = [
30608
30695
  "**/*.ts",
30609
30696
  "**/*.tsx",
@@ -30662,6 +30749,10 @@ var Indexer = class extends EventEmitter2 {
30662
30749
  console.error(`Error indexing ${file2}:`, error2);
30663
30750
  }
30664
30751
  }
30752
+ const depCount = this.rebuildDependencies();
30753
+ if (depCount > 0) {
30754
+ console.error(`[Index] Built ${depCount} dependency edges`);
30755
+ }
30665
30756
  this.emit("indexingComplete", {
30666
30757
  total: checked,
30667
30758
  indexed,
@@ -30671,6 +30762,23 @@ var Indexer = class extends EventEmitter2 {
30671
30762
  this.isIndexing = false;
30672
30763
  }
30673
30764
  }
30765
+ /**
30766
+ * Rebuild all dependency edges from stored imports.
30767
+ * Run after initial indexing so all target files are in the DB.
30768
+ */
30769
+ rebuildDependencies() {
30770
+ this.tier2.clearAllDependencies();
30771
+ const allImports = this.tier2.getAllImports();
30772
+ let count = 0;
30773
+ for (const imp of allImports) {
30774
+ const targetFile = this.tier2.resolveImportToFile(imp.filePath, imp.importedFrom, this.config.projectPath);
30775
+ if (targetFile) {
30776
+ this.tier2.addDependency(imp.fileId, targetFile.id, "imports");
30777
+ count++;
30778
+ }
30779
+ }
30780
+ return count;
30781
+ }
30674
30782
  startWatching() {
30675
30783
  this.watcher.start();
30676
30784
  }
@@ -30885,7 +30993,7 @@ ${result.preview}
30885
30993
  if (currentFile && dirname3(r.file) === dirname3(currentFile)) {
30886
30994
  score *= 1.5;
30887
30995
  }
30888
- const hoursSinceModified = (Date.now() - r.lastModified) / 36e5;
30996
+ const hoursSinceModified = (Date.now() - r.lastModified * 1e3) / 36e5;
30889
30997
  if (hoursSinceModified < 24) {
30890
30998
  score *= 1 + 0.3 * (24 - hoursSinceModified) / 24;
30891
30999
  }
@@ -35630,7 +35738,7 @@ ${code}` : code;
35630
35738
  file: result.path,
35631
35739
  similarity,
35632
35740
  snippet: result.preview,
35633
- lastModified: file2 ? new Date(file2.lastModified) : void 0,
35741
+ lastModified: file2 ? new Date(file2.lastModified * 1e3) : void 0,
35634
35742
  usageCount: dependents.length + 1,
35635
35743
  function: symbols.length > 0 ? symbols[0].name : void 0
35636
35744
  });
@@ -45196,7 +45304,10 @@ var CodeImpactEngine = class {
45196
45304
  this.featureContextManager.onFileOpened(path);
45197
45305
  this.summarizer.invalidateSummaryByPath(path);
45198
45306
  this.knowledgeOrchestrator.schedule("file_indexed", [path]);
45199
- this.pendingComponentDocPaths.add(path);
45307
+ const normalizedPath = path.replace(/\\/g, "/");
45308
+ if (!normalizedPath.includes("knowledge/") && !normalizedPath.includes(".code-impact/")) {
45309
+ this.pendingComponentDocPaths.add(path);
45310
+ }
45200
45311
  if (this.componentDocTimer) clearTimeout(this.componentDocTimer);
45201
45312
  this.componentDocTimer = setTimeout(() => {
45202
45313
  this.batchGenerateComponentDocs(Array.from(this.pendingComponentDocPaths)).catch((err) => {
@@ -45367,6 +45478,11 @@ var CodeImpactEngine = class {
45367
45478
  FROM files f
45368
45479
  LEFT JOIN dependencies d ON d.target_file_id = f.id
45369
45480
  WHERE f.language IS NOT NULL
45481
+ AND f.path NOT LIKE '%node_modules%'
45482
+ AND f.path NOT LIKE '%knowledge/%'
45483
+ AND f.path NOT LIKE '%.code-impact/%'
45484
+ AND f.path NOT LIKE '%/venv/%'
45485
+ AND f.path NOT LIKE '%/.venv/%'
45370
45486
  GROUP BY f.id
45371
45487
  ORDER BY dep_count DESC
45372
45488
  LIMIT 20