codesight 1.5.0 → 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.
package/README.md CHANGED
@@ -569,12 +569,14 @@ jobs:
569
569
 
570
570
  ## Watch Mode and Git Hook
571
571
 
572
- **Watch mode** re-scans when files change:
572
+ **Watch mode** re-scans automatically when your code changes:
573
573
 
574
574
  ```bash
575
575
  npx codesight --watch
576
576
  ```
577
577
 
578
+ Only triggers on source and config files (`.ts`, `.js`, `.py`, `.go`, `.prisma`, `.env`, etc.). Ignores `node_modules`, build output, and non-code files. Shows which files changed before each re-scan. Your config (disabled detectors, plugins) is preserved across re-scans.
579
+
578
580
  **Git hook** regenerates context on every commit:
579
581
 
580
582
  ```bash
package/dist/formatter.js CHANGED
@@ -239,7 +239,9 @@ function formatCombined(result, sections) {
239
239
  // Token stats
240
240
  const ts = result.tokenStats;
241
241
  lines.push(`> ${result.routes.length} routes | ${result.schemas.length} models | ${result.components.length} components | ${result.libs.length} lib files | ${result.config.envVars.length} env vars | ${result.middleware.length} middleware | ${result.graph.edges.length} import links`);
242
- lines.push(`> **Token savings:** this file is ~${ts.outputTokens.toLocaleString()} tokens. Without it, AI exploration would cost ~${ts.estimatedExplorationTokens.toLocaleString()} tokens. **Saves ~${ts.saved.toLocaleString()} tokens per conversation.**`);
242
+ // Round to nearest 100 to keep output deterministic across runs (avoids git conflicts in worktrees)
243
+ const roundTo100 = (n) => Math.round(n / 100) * 100;
244
+ lines.push(`> **Token savings:** this file is ~${roundTo100(ts.outputTokens).toLocaleString()} tokens. Without it, AI exploration would cost ~${roundTo100(ts.estimatedExplorationTokens).toLocaleString()} tokens. **Saves ~${roundTo100(ts.saved).toLocaleString()} tokens per conversation.**`);
243
245
  lines.push("");
244
246
  lines.push("---");
245
247
  lines.push("");
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { writeOutput } from "./formatter.js";
15
15
  import { generateAIConfigs } from "./generators/ai-config.js";
16
16
  import { generateHtmlReport } from "./generators/html-report.js";
17
17
  import { loadConfig, mergeCliConfig } from "./config.js";
18
- const VERSION = "1.5.0";
18
+ const VERSION = "1.5.2";
19
19
  const BRAND = "codesight";
20
20
  function printHelp() {
21
21
  console.log(`
@@ -201,44 +201,63 @@ async function installGitHook(root, outputDirName) {
201
201
  await chmod(hookPath, 0o755);
202
202
  console.log(` Git pre-commit hook installed at .git/hooks/pre-commit`);
203
203
  }
204
- async function watchMode(root, outputDirName, maxDepth) {
204
+ async function watchMode(root, outputDirName, maxDepth, userConfig = {}) {
205
205
  console.log(` Watching for changes... (Ctrl+C to stop)\n`);
206
+ const WATCH_EXTENSIONS = new Set([
207
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
208
+ ".py", ".go", ".vue", ".svelte", ".rb", ".ex", ".exs",
209
+ ".java", ".kt", ".rs", ".php",
210
+ ".json", ".yaml", ".yml", ".toml", ".env",
211
+ ".prisma", ".graphql", ".gql",
212
+ ]);
213
+ const IGNORE_DIRS = new Set([
214
+ "node_modules", ".git", ".next", ".nuxt", ".svelte-kit",
215
+ "__pycache__", ".venv", "venv", "dist", "build", "out",
216
+ ".output", "coverage", ".turbo", ".vercel", ".cache",
217
+ outputDirName,
218
+ ]);
206
219
  let debounceTimer = null;
207
220
  let isScanning = false;
221
+ let changedFiles = [];
208
222
  const runScan = async () => {
209
223
  if (isScanning)
210
224
  return;
211
225
  isScanning = true;
226
+ const files = [...changedFiles];
227
+ changedFiles = [];
212
228
  try {
213
- console.log("\n Changes detected, re-scanning...\n");
214
- await scan(root, outputDirName, maxDepth);
229
+ const fileList = files.length <= 5 ? files.join(", ") : `${files.length} files`;
230
+ console.log(`\n Changes detected (${fileList}), re-scanning...\n`);
231
+ await scan(root, outputDirName, maxDepth, userConfig);
215
232
  }
216
233
  catch (err) {
217
234
  console.error(" Scan error:", err.message);
218
235
  }
219
236
  isScanning = false;
220
237
  };
221
- // Use polling approach for cross-platform compatibility
222
238
  const { watch } = await import("node:fs");
239
+ const { extname: ext } = await import("node:path");
223
240
  const watcher = watch(root, { recursive: true }, (_event, filename) => {
224
241
  if (!filename)
225
242
  return;
226
- // Skip output directory and hidden files
227
- if (filename.startsWith(outputDirName) || filename.startsWith(".git"))
243
+ // Skip ignored directories
244
+ const parts = filename.split("/");
245
+ if (parts.some((p) => IGNORE_DIRS.has(p)))
228
246
  return;
229
- if (filename.includes("node_modules"))
247
+ // Only trigger on relevant file extensions
248
+ const fileExt = ext(filename);
249
+ if (!WATCH_EXTENSIONS.has(fileExt))
230
250
  return;
251
+ changedFiles.push(filename);
231
252
  if (debounceTimer)
232
253
  clearTimeout(debounceTimer);
233
254
  debounceTimer = setTimeout(runScan, 500);
234
255
  });
235
- // Keep process alive
236
256
  process.on("SIGINT", () => {
237
257
  watcher.close();
238
258
  console.log("\n Watch mode stopped.");
239
259
  process.exit(0);
240
260
  });
241
- // Wait forever
242
261
  await new Promise(() => { });
243
262
  }
244
263
  async function main() {
@@ -396,9 +415,9 @@ async function main() {
396
415
  const reportPath = await generateHtmlReport(result, outputDir);
397
416
  console.log(` ${outputDirName}/report.html`);
398
417
  if (doOpen) {
399
- const { exec } = await import("node:child_process");
418
+ const { execFile } = await import("node:child_process");
400
419
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
401
- exec(`${cmd} "${reportPath}"`);
420
+ execFile(cmd, [reportPath]);
402
421
  console.log(" Opening in browser...");
403
422
  }
404
423
  }
@@ -473,7 +492,7 @@ async function main() {
473
492
  }
474
493
  // Watch mode (blocks)
475
494
  if (doWatch) {
476
- await watchMode(root, outputDirName, maxDepth);
495
+ await watchMode(root, outputDirName, maxDepth, config);
477
496
  }
478
497
  }
479
498
  main().catch((err) => {
package/dist/scanner.js CHANGED
@@ -90,7 +90,7 @@ export async function detectProject(root) {
90
90
  pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
91
91
  }
92
92
  catch { }
93
- const name = pkg.name || basename(root);
93
+ const name = pkg.name || await resolveRepoName(root);
94
94
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
95
95
  // Detect monorepo
96
96
  const isMonorepo = !!(pkg.workspaces || await fileExists(join(root, "pnpm-workspace.yaml")));
@@ -429,6 +429,33 @@ async function getGoDeps(root) {
429
429
  catch { }
430
430
  return deps;
431
431
  }
432
+ /**
433
+ * Resolve the repo name, handling git worktrees.
434
+ * In a worktree, basename(root) is a random name — resolve the actual repo instead.
435
+ */
436
+ async function resolveRepoName(root) {
437
+ try {
438
+ // Check if .git is a file (worktree) vs directory (normal repo)
439
+ const gitPath = join(root, ".git");
440
+ const gitStat = await stat(gitPath);
441
+ if (gitStat.isFile()) {
442
+ // Worktree: .git is a file containing "gitdir: /path/to/main/.git/worktrees/name"
443
+ const gitContent = await readFile(gitPath, "utf-8");
444
+ const gitdirMatch = gitContent.match(/gitdir:\s*(.+)/);
445
+ if (gitdirMatch) {
446
+ // Resolve back to main repo: /repo/.git/worktrees/name -> /repo
447
+ const worktreeGitDir = gitdirMatch[1].trim();
448
+ // Go up from .git/worktrees/name to the repo root
449
+ const mainGitDir = join(worktreeGitDir, "..", "..");
450
+ const mainRepoRoot = join(mainGitDir, "..");
451
+ return basename(mainRepoRoot);
452
+ }
453
+ }
454
+ }
455
+ catch { }
456
+ // Fallback: use directory name
457
+ return basename(root);
458
+ }
432
459
  async function fileExists(path) {
433
460
  try {
434
461
  await stat(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codesight",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "See your codebase clearly. Universal AI context generator that maps routes, schema, components, dependencies, and more for Claude Code, Cursor, Copilot, Codex, and any AI coding tool.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {