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 +3 -1
- package/dist/formatter.js +3 -1
- package/dist/index.js +32 -13
- package/dist/scanner.js +28 -1
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
214
|
-
|
|
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
|
|
227
|
-
|
|
243
|
+
// Skip ignored directories
|
|
244
|
+
const parts = filename.split("/");
|
|
245
|
+
if (parts.some((p) => IGNORE_DIRS.has(p)))
|
|
228
246
|
return;
|
|
229
|
-
|
|
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 {
|
|
418
|
+
const { execFile } = await import("node:child_process");
|
|
400
419
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
401
|
-
|
|
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 ||
|
|
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.
|
|
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": {
|