projscan 0.9.2 → 0.11.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 (115) hide show
  1. package/README.md +37 -7
  2. package/dist/analyzers/deadCodeCheck.d.ts +10 -12
  3. package/dist/analyzers/deadCodeCheck.js +41 -69
  4. package/dist/analyzers/deadCodeCheck.js.map +1 -1
  5. package/dist/analyzers/pythonDependencyRiskCheck.d.ts +2 -0
  6. package/dist/analyzers/pythonDependencyRiskCheck.js +114 -0
  7. package/dist/analyzers/pythonDependencyRiskCheck.js.map +1 -0
  8. package/dist/analyzers/pythonLinterCheck.d.ts +2 -0
  9. package/dist/analyzers/pythonLinterCheck.js +119 -0
  10. package/dist/analyzers/pythonLinterCheck.js.map +1 -0
  11. package/dist/analyzers/pythonTestCheck.d.ts +2 -0
  12. package/dist/analyzers/pythonTestCheck.js +97 -0
  13. package/dist/analyzers/pythonTestCheck.js.map +1 -0
  14. package/dist/analyzers/pythonUnusedDependencyCheck.d.ts +2 -0
  15. package/dist/analyzers/pythonUnusedDependencyCheck.js +76 -0
  16. package/dist/analyzers/pythonUnusedDependencyCheck.js.map +1 -0
  17. package/dist/cli/index.js +294 -10
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/core/ast.d.ts +2 -0
  20. package/dist/core/ast.js +35 -2
  21. package/dist/core/ast.js.map +1 -1
  22. package/dist/core/codeGraph.d.ts +8 -7
  23. package/dist/core/codeGraph.js +50 -72
  24. package/dist/core/codeGraph.js.map +1 -1
  25. package/dist/core/couplingAnalyzer.d.ts +18 -0
  26. package/dist/core/couplingAnalyzer.js +174 -0
  27. package/dist/core/couplingAnalyzer.js.map +1 -0
  28. package/dist/core/fileInspector.d.ts +3 -0
  29. package/dist/core/fileInspector.js +78 -3
  30. package/dist/core/fileInspector.js.map +1 -1
  31. package/dist/core/hotspotAnalyzer.d.ts +13 -0
  32. package/dist/core/hotspotAnalyzer.js +29 -6
  33. package/dist/core/hotspotAnalyzer.js.map +1 -1
  34. package/dist/core/indexCache.js +8 -1
  35. package/dist/core/indexCache.js.map +1 -1
  36. package/dist/core/issueEngine.js +10 -0
  37. package/dist/core/issueEngine.js.map +1 -1
  38. package/dist/core/languages/LanguageAdapter.d.ts +36 -0
  39. package/dist/core/languages/LanguageAdapter.js +2 -0
  40. package/dist/core/languages/LanguageAdapter.js.map +1 -0
  41. package/dist/core/languages/goAdapter.d.ts +2 -0
  42. package/dist/core/languages/goAdapter.js +136 -0
  43. package/dist/core/languages/goAdapter.js.map +1 -0
  44. package/dist/core/languages/goCyclomatic.d.ts +21 -0
  45. package/dist/core/languages/goCyclomatic.js +55 -0
  46. package/dist/core/languages/goCyclomatic.js.map +1 -0
  47. package/dist/core/languages/goExports.d.ts +26 -0
  48. package/dist/core/languages/goExports.js +89 -0
  49. package/dist/core/languages/goExports.js.map +1 -0
  50. package/dist/core/languages/goImports.d.ts +26 -0
  51. package/dist/core/languages/goImports.js +64 -0
  52. package/dist/core/languages/goImports.js.map +1 -0
  53. package/dist/core/languages/goManifests.d.ts +19 -0
  54. package/dist/core/languages/goManifests.js +56 -0
  55. package/dist/core/languages/goManifests.js.map +1 -0
  56. package/dist/core/languages/javascriptAdapter.d.ts +2 -0
  57. package/dist/core/languages/javascriptAdapter.js +68 -0
  58. package/dist/core/languages/javascriptAdapter.js.map +1 -0
  59. package/dist/core/languages/pythonAdapter.d.ts +6 -0
  60. package/dist/core/languages/pythonAdapter.js +147 -0
  61. package/dist/core/languages/pythonAdapter.js.map +1 -0
  62. package/dist/core/languages/pythonCyclomatic.d.ts +18 -0
  63. package/dist/core/languages/pythonCyclomatic.js +45 -0
  64. package/dist/core/languages/pythonCyclomatic.js.map +1 -0
  65. package/dist/core/languages/pythonExports.d.ts +28 -0
  66. package/dist/core/languages/pythonExports.js +169 -0
  67. package/dist/core/languages/pythonExports.js.map +1 -0
  68. package/dist/core/languages/pythonImports.d.ts +22 -0
  69. package/dist/core/languages/pythonImports.js +104 -0
  70. package/dist/core/languages/pythonImports.js.map +1 -0
  71. package/dist/core/languages/pythonManifests.d.ts +34 -0
  72. package/dist/core/languages/pythonManifests.js +344 -0
  73. package/dist/core/languages/pythonManifests.js.map +1 -0
  74. package/dist/core/languages/registry.d.ts +5 -0
  75. package/dist/core/languages/registry.js +31 -0
  76. package/dist/core/languages/registry.js.map +1 -0
  77. package/dist/core/languages/treeSitterLoader.d.ts +14 -0
  78. package/dist/core/languages/treeSitterLoader.js +76 -0
  79. package/dist/core/languages/treeSitterLoader.js.map +1 -0
  80. package/dist/core/monorepo.d.ts +20 -0
  81. package/dist/core/monorepo.js +270 -0
  82. package/dist/core/monorepo.js.map +1 -0
  83. package/dist/core/prDiff.d.ts +43 -0
  84. package/dist/core/prDiff.js +298 -0
  85. package/dist/core/prDiff.js.map +1 -0
  86. package/dist/core/searchIndex.js +8 -0
  87. package/dist/core/searchIndex.js.map +1 -1
  88. package/dist/core/telemetry.d.ts +90 -0
  89. package/dist/core/telemetry.js +199 -0
  90. package/dist/core/telemetry.js.map +1 -0
  91. package/dist/grammars/tree-sitter-go.wasm +0 -0
  92. package/dist/grammars/tree-sitter-python.wasm +0 -0
  93. package/dist/grammars/web-tree-sitter.wasm +0 -0
  94. package/dist/mcp/server.js +22 -0
  95. package/dist/mcp/server.js.map +1 -1
  96. package/dist/mcp/tools.js +317 -20
  97. package/dist/mcp/tools.js.map +1 -1
  98. package/dist/reporters/consoleReporter.d.ts +4 -1
  99. package/dist/reporters/consoleReporter.js +113 -0
  100. package/dist/reporters/consoleReporter.js.map +1 -1
  101. package/dist/reporters/jsonReporter.d.ts +4 -1
  102. package/dist/reporters/jsonReporter.js +9 -0
  103. package/dist/reporters/jsonReporter.js.map +1 -1
  104. package/dist/reporters/markdownReporter.d.ts +4 -1
  105. package/dist/reporters/markdownReporter.js +103 -3
  106. package/dist/reporters/markdownReporter.js.map +1 -1
  107. package/dist/types.d.ts +115 -0
  108. package/dist/utils/cache.d.ts +3 -0
  109. package/dist/utils/cache.js +51 -0
  110. package/dist/utils/cache.js.map +1 -0
  111. package/dist/utils/config.js +10 -0
  112. package/dist/utils/config.js.map +1 -1
  113. package/dist/utils/fileWalker.js +14 -0
  114. package/dist/utils/fileWalker.js.map +1 -1
  115. package/package.json +11 -5
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  AI coding agents are becoming the primary interface to code. Today, when you ask your agent *"which files implement auth?"* or *"what breaks if I bump React from 18 to 19?"* - it either guesses from names, or it shells out to grep and reads raw output not built for it.
22
22
 
23
- **projscan is the first code-intelligence tool built for agents, not for humans.** Your agent gets a fast, AST-accurate, context-budget-aware view of your codebase through 13 structured MCP tools. It can query the import graph, find symbol definitions, preview upgrades, rank hotspots - without loading the file tree into its context.
23
+ **projscan is the first code-intelligence tool built for agents, not for humans.** Your agent gets a fast, AST-accurate, context-budget-aware view of your codebase through 17 structured MCP tools. It can query the import graph, find symbol definitions, preview upgrades, rank hotspots, diff structural changes between refs, and surface coupling/cycle hotspots - without loading the file tree into its context.
24
24
 
25
25
  Humans get the same thing through the CLI.
26
26
 
@@ -190,10 +190,34 @@ This outputs a [shields.io](https://shields.io) badge URL and markdown snippet y
190
190
 
191
191
  ## What It Detects
192
192
 
193
- **Languages**: TypeScript, JavaScript, Python, Go, Rust, Java, Ruby, C/C++, PHP, Swift, Kotlin, and 20+ more
193
+ **Languages**: TypeScript, JavaScript, Python, and Go (full AST analysis for all four), plus file-level detection for Rust, Java, Ruby, C/C++, PHP, Swift, Kotlin, and 20+ more.
194
194
 
195
195
  **Frameworks**: React, Next.js, Vue, Nuxt, Svelte, Angular, Express, Fastify, NestJS, Vite, Tailwind CSS, Prisma, and more
196
196
 
197
+ ### Python (0.10)
198
+
199
+ Python repos now get the same treatment JS/TS has had since 0.6:
200
+
201
+ - **AST-accurate import graph.** `from pkg.mod import x`, relative imports, `__init__.py` packages, `__all__`. Parsed via tree-sitter-python (wasm, offline).
202
+ - **Python-aware analyzers.** Missing pytest / ruff / black config. Deprecated packages (nose, simplejson, pycrypto). Unused `pyproject.toml` / `requirements.txt` deps. Missing lockfile.
203
+ - **Code search.** BM25 and semantic modes work on `.py` files out of the box.
204
+ - **Hotspots + dead code.** Same scoring as JS/TS, with `__init__.py` and pytest test-file conventions understood.
205
+ - **MCP tools work unchanged.** `projscan_graph`, `projscan_search`, `projscan_doctor`, `projscan_hotspots`, etc. all accept Python projects. Agents can ask "which files import `pkg.core`?" and get an answer in milliseconds.
206
+
207
+ `projscan_upgrade` remains Node-only for now - a Python equivalent (reading pip / poetry metadata) is on the roadmap.
208
+
209
+ ### Bundle (0.11)
210
+
211
+ 0.11.0 is a multi-theme bundle. Five releases of work shipped under one version:
212
+
213
+ - **Signal Quality (was 0.11)** - the hotspot risk score now uses AST-derived **cyclomatic complexity** instead of a line-count proxy. Per-file CC is exposed via `projscan_file` and the new `projscan_coupling` tool. Coupling metrics (fan-in / fan-out / instability) and **circular-import detection** (Tarjan SCC) ship as a first-class `projscan coupling` command and MCP tool.
214
+ - **PR Native (was 0.12)** - new `projscan_pr_diff` tool returns the **structural** diff between two refs: exports added/removed, imports added/removed, call sites added/removed, ΔCC, Δfan-in. Stands up a temporary git worktree at the base ref to get a clean second graph. What an agent reviewing a PR actually wants to know.
215
+ - **Monorepo (was 0.13)** - workspace detection for npm/yarn workspaces, pnpm-workspace.yaml, and Nx/Turbo/Lerna fallback. New `projscan workspaces` command lists every package; `--package <name>` (and the `package` MCP arg) scope `hotspots` and `coupling` to a single workspace.
216
+ - **Observability (was 0.14)** - **opt-in**, privacy-preserving telemetry. Records only tool name, duration, success, version, timestamp; never source content, paths, or arguments. Off by default; enable via `.projscanrc` `telemetry.enabled` or `PROJSCAN_TELEMETRY=1`. Sink is a local JSONL file you control. New `projscan_telemetry` tool surfaces effective state.
217
+ - **Second Language (was 0.15)** - **Go** via tree-sitter-go. Go files now flow through the same graph / hotspot / coupling / pr-diff pipeline as JS/TS and Python. `go.mod` parsed for module-path resolution; capitalization rule applied for export visibility.
218
+
219
+ Cache version bumped 2 → 3 (CC stored per file). Existing v2 caches are discarded on first 0.11 run and rebuilt automatically.
220
+
197
221
  **Issues**:
198
222
  - Missing linting (ESLint) and formatting (Prettier) configuration
199
223
  - Missing test framework
@@ -210,7 +234,7 @@ This outputs a [shields.io](https://shields.io) badge URL and markdown snippet y
210
234
  - **5,000 files** analyzed in under 1.5 seconds
211
235
  - **20,000 files** analyzed in under 3 seconds
212
236
  - **Zero network requests** - everything runs locally
213
- - **4 runtime dependencies** - minimal footprint
237
+ - **9 runtime dependencies** - still minimal (the three tree-sitter packages bring ~850 KB of vendored wasm: web-tree-sitter ~190 KB, tree-sitter-python ~450 KB, tree-sitter-go ~210 KB)
214
238
 
215
239
  ## CI/CD Integration
216
240
 
@@ -417,17 +441,19 @@ claude mcp add projscan -- npx projscan mcp
417
441
  - *"What breaks if I bump chalk to 6?"* → `projscan_upgrade { package: "chalk" }`
418
442
  - *"Where should I refactor first?"* → `projscan_hotspots`
419
443
 
420
- ### The 13 MCP tools
444
+ ### The 17 MCP tools
421
445
 
422
- **Structural (0.6.0 - new, agent-native):**
446
+ **Structural (0.6.0 / 0.11 agent-native):**
423
447
  - **`projscan_graph`** - query the AST-based code graph. Directions: `imports`, `exports`, `importers`, `symbol_defs`, `package_importers`. Millisecond responses on a warm cache.
424
448
  - **`projscan_search`** - fast search across `symbols` (exported names), `files` (path substring), or `content` (source substring with line + excerpt). Replaces the temptation to shell out to grep.
449
+ - **`projscan_coupling`** *(0.11)* - per-file fan-in / fan-out / instability + circular-import cycles (Tarjan SCC). Filter by `direction: cycles_only | high_fan_in | high_fan_out`.
450
+ - **`projscan_pr_diff`** *(0.11)* - structural diff between two git refs. Returns added/removed/modified files with explicit lists of exports, imports, and call sites that changed, plus ΔCC and Δfan-in.
425
451
 
426
452
  **Analysis:**
427
453
  - `projscan_analyze` - full project report
428
454
  - `projscan_doctor` - health score + issues
429
- - `projscan_hotspots` - risk-ranked files (churn × complexity × issues × ownership × coverage)
430
- - `projscan_file` - per-file risk + ownership + related issues
455
+ - `projscan_hotspots` - risk-ranked files (churn × **AST cyclomatic complexity** × issues × ownership × coverage; falls back to LOC for non-AST languages)
456
+ - `projscan_file` - per-file risk + ownership + related issues + CC + fan-in/fan-out
431
457
  - `projscan_explain` - per-file purpose, imports, exports, smells
432
458
  - `projscan_structure` - directory tree
433
459
  - `projscan_coverage` - scariest untested files (coverage × hotspots)
@@ -438,6 +464,10 @@ claude mcp add projscan -- npx projscan mcp
438
464
  - `projscan_audit` - normalized `npm audit`
439
465
  - `projscan_upgrade` - upgrade preview (CHANGELOG + importers, offline)
440
466
 
467
+ **Workspace + observability (0.11):**
468
+ - `projscan_workspaces` - list monorepo packages (npm/yarn/pnpm/Nx/Turbo/Lerna). Use the `name` as the `package` arg on `projscan_hotspots` / `projscan_coupling` to scope.
469
+ - `projscan_telemetry` - inspect opt-in telemetry state (enabled?, sink path, env override).
470
+
441
471
  ### Context-window budgeting
442
472
 
443
473
  **Every MCP tool accepts an optional `max_tokens` argument.** Set it and projscan serializes the result, and - if over budget - truncates the largest array field record-by-record until it fits. Responses include a `_budget` sidecar when truncated so your agent knows it got a partial view.
@@ -1,18 +1,16 @@
1
1
  import type { FileEntry, Issue } from '../types.js';
2
2
  /**
3
- * Flag exports that are never imported anywhere in the project. This catches:
4
- * - dead named exports left over from refactors
5
- * - utilities that are implemented but never hooked up
3
+ * Flag source files whose exports nothing imports. Language-agnostic: uses the
4
+ * code graph directly, so JS/TS/Python all get the same correctness guarantees.
6
5
  *
7
- * Does NOT flag:
8
- * - files listed as the package's public entry points (main, exports, types, bin)
9
- * - default exports (too many false positives - framework conventions)
10
- * - test files (they're not supposed to export)
11
- * - index files (barrels re-export for public use)
6
+ * Skipped:
7
+ * - public package entries (main/exports/bin/types in package.json)
8
+ * - test files (JS conventions + pytest `test_*.py` / `*_test.py` / `tests/`)
9
+ * - barrel files (`index.*` for JS, `__init__.py` for Python)
10
+ * - default-only exports (too many framework false positives)
12
11
  *
13
- * False-positive guard: if a file is the target of at least one import, we treat
14
- * all its exports as "possibly used" - the regex-based graph can't tell which
15
- * named export is imported via `import { ... } from './barrel'`. This keeps
16
- * noise low at the cost of missing some dead exports that live in used files.
12
+ * False-positive guard: if any import resolves to this file, we treat ALL its
13
+ * exports as possibly used - the graph can't always tell which named export
14
+ * got picked up from a barrel.
17
15
  */
18
16
  export declare function check(rootPath: string, files: FileEntry[]): Promise<Issue[]>;
@@ -1,47 +1,36 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { buildImportGraph } from '../core/importGraph.js';
4
- import { extractExports } from '../core/fileInspector.js';
5
- const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts']);
6
- // Never flag these - they're public API by definition
3
+ import { buildCodeGraph } from '../core/codeGraph.js';
4
+ import { getAdapterFor } from '../core/languages/registry.js';
5
+ const SOURCE_EXTENSIONS = new Set([
6
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts',
7
+ '.py', '.pyw',
8
+ ]);
9
+ // Never flag these - they're public API by definition (JS convention).
7
10
  const PUBLIC_PATH_PREFIXES = ['src/index', 'index.'];
11
+ // Names (sans extension) that are barrel-equivalents and should never be
12
+ // flagged as dead. `index` for JS/TS, `__init__` for Python packages.
13
+ const BARREL_BASENAMES = new Set(['index', '__init__']);
8
14
  /**
9
- * Flag exports that are never imported anywhere in the project. This catches:
10
- * - dead named exports left over from refactors
11
- * - utilities that are implemented but never hooked up
15
+ * Flag source files whose exports nothing imports. Language-agnostic: uses the
16
+ * code graph directly, so JS/TS/Python all get the same correctness guarantees.
12
17
  *
13
- * Does NOT flag:
14
- * - files listed as the package's public entry points (main, exports, types, bin)
15
- * - default exports (too many false positives - framework conventions)
16
- * - test files (they're not supposed to export)
17
- * - index files (barrels re-export for public use)
18
+ * Skipped:
19
+ * - public package entries (main/exports/bin/types in package.json)
20
+ * - test files (JS conventions + pytest `test_*.py` / `*_test.py` / `tests/`)
21
+ * - barrel files (`index.*` for JS, `__init__.py` for Python)
22
+ * - default-only exports (too many framework false positives)
18
23
  *
19
- * False-positive guard: if a file is the target of at least one import, we treat
20
- * all its exports as "possibly used" - the regex-based graph can't tell which
21
- * named export is imported via `import { ... } from './barrel'`. This keeps
22
- * noise low at the cost of missing some dead exports that live in used files.
24
+ * False-positive guard: if any import resolves to this file, we treat ALL its
25
+ * exports as possibly used - the graph can't always tell which named export
26
+ * got picked up from a barrel.
23
27
  */
24
28
  export async function check(rootPath, files) {
25
29
  const sourceFiles = files.filter((f) => SOURCE_EXTENSIONS.has(f.extension));
26
30
  if (sourceFiles.length === 0)
27
31
  return [];
28
32
  const publicEntries = await loadPublicEntries(rootPath);
29
- const graph = await buildImportGraph(rootPath, sourceFiles);
30
- // Build a set of files that are the target of at least one relative import.
31
- // A relative import specifier is resolved against its importing file's dir,
32
- // so we convert each relative specifier into a candidate target path.
33
- const importedTargets = new Set();
34
- for (const [importingFile, specifiers] of graph.byFile) {
35
- const importingDir = path.posix.dirname(importingFile);
36
- for (const spec of specifiers) {
37
- if (!spec.startsWith('.'))
38
- continue;
39
- const resolved = path.posix.normalize(path.posix.join(importingDir, spec));
40
- for (const candidate of resolutionCandidates(resolved)) {
41
- importedTargets.add(candidate);
42
- }
43
- }
44
- }
33
+ const graph = await buildCodeGraph(rootPath, sourceFiles);
45
34
  const issues = [];
46
35
  for (const file of sourceFiles) {
47
36
  if (isTestFile(file.relativePath))
@@ -50,27 +39,25 @@ export async function check(rootPath, files) {
50
39
  continue;
51
40
  if (isPublicEntry(file.relativePath, publicEntries))
52
41
  continue;
53
- if (importedTargets.has(file.relativePath))
42
+ // Any importer → file is used.
43
+ if ((graph.localImporters.get(file.relativePath)?.size ?? 0) > 0)
54
44
  continue;
55
- if (importedTargets.has(stripExtension(file.relativePath)))
45
+ const graphFile = graph.files.get(file.relativePath);
46
+ if (!graphFile || !graphFile.parseOk)
56
47
  continue;
57
- let content;
58
- try {
59
- content = await fs.readFile(file.absolutePath, 'utf-8');
60
- }
61
- catch {
62
- continue;
63
- }
64
- const exports = extractExports(content).filter((e) => e.type !== 'default' && e.name !== 'default');
65
- if (exports.length === 0)
48
+ const namedExports = graphFile.exports.filter((e) => e.name !== 'default' && e.kind !== 'default');
49
+ if (namedExports.length === 0)
66
50
  continue;
51
+ const adapter = getAdapterFor(file.relativePath);
52
+ const languageLabel = adapter?.id ?? 'file';
53
+ const kindLabel = languageLabel === 'python' ? 'name' : 'export';
67
54
  issues.push({
68
55
  id: `unused-exports-${file.relativePath}`,
69
- title: `Unused exports in ${file.relativePath}`,
70
- description: `${exports.length} named export${exports.length === 1 ? '' : 's'} (${exports
56
+ title: `Unused ${kindLabel}s in ${file.relativePath}`,
57
+ description: `${namedExports.length} named ${kindLabel}${namedExports.length === 1 ? '' : 's'} (${namedExports
71
58
  .slice(0, 5)
72
59
  .map((e) => e.name)
73
- .join(', ')}${exports.length > 5 ? `, … +${exports.length - 5}` : ''}) but nothing in the project imports this file. Dead code or awaiting wiring?`,
60
+ .join(', ')}${namedExports.length > 5 ? `, … +${namedExports.length - 5}` : ''}) but nothing in the project imports this file. Dead code or awaiting wiring?`,
74
61
  severity: 'info',
75
62
  category: 'architecture',
76
63
  fixAvailable: false,
@@ -80,14 +67,19 @@ export async function check(rootPath, files) {
80
67
  return issues;
81
68
  }
82
69
  function isTestFile(relativePath) {
70
+ const base = path.basename(relativePath);
83
71
  return (relativePath.includes('.test.') ||
84
72
  relativePath.includes('.spec.') ||
85
73
  relativePath.includes('__tests__') ||
86
- relativePath.startsWith('tests/'));
74
+ relativePath.startsWith('tests/') ||
75
+ relativePath.includes('/tests/') ||
76
+ // pytest conventions
77
+ /^test_.+\.py$/.test(base) ||
78
+ /^.+_test\.py$/.test(base));
87
79
  }
88
80
  function isBarrelFile(relativePath) {
89
81
  const base = path.basename(relativePath, path.extname(relativePath));
90
- return base === 'index';
82
+ return BARREL_BASENAMES.has(base);
91
83
  }
92
84
  function isPublicEntry(relativePath, publicEntries) {
93
85
  if (publicEntries.has(relativePath))
@@ -104,26 +96,6 @@ function stripExtension(p) {
104
96
  const ext = path.extname(p);
105
97
  return ext ? p.slice(0, -ext.length) : p;
106
98
  }
107
- /**
108
- * Return the set of possible resolution targets for a relative import
109
- * specifier (already joined+normalized). For './foo' we yield:
110
- * foo, foo.ts, foo.tsx, foo.js, foo.jsx, foo.mjs, foo.cjs,
111
- * foo/index.ts, foo/index.tsx, foo/index.js, ...
112
- */
113
- function resolutionCandidates(base) {
114
- const exts = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts'];
115
- const out = [base];
116
- for (const ext of exts) {
117
- out.push(base + ext);
118
- out.push(base + '/index' + ext);
119
- }
120
- // Handle imports written with an explicit ".js" that actually resolve to a .ts file (ESM+NodeNext)
121
- if (base.endsWith('.js')) {
122
- const noJs = base.slice(0, -3);
123
- out.push(noJs + '.ts', noJs + '.tsx');
124
- }
125
- return out;
126
- }
127
99
  async function loadPublicEntries(rootPath) {
128
100
  const entries = new Set();
129
101
  const pkgPath = path.join(rootPath, 'package.json');
@@ -143,7 +115,7 @@ async function loadPublicEntries(rootPath) {
143
115
  collectExports(pkg.exports, entries);
144
116
  }
145
117
  catch {
146
- // package.json missing/unreadable - don't guard, every file is a candidate
118
+ // package.json missing - nothing to guard
147
119
  }
148
120
  return entries;
149
121
  }
@@ -1 +1 @@
1
- {"version":3,"file":"deadCodeCheck.js","sourceRoot":"","sources":["../../src/analyzers/deadCodeCheck.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deadCodeCheck.js","sourceRoot":"","sources":["../../src/analyzers/deadCodeCheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FileEntry, Issue } from '../types.js';
2
+ export declare function check(rootPath: string, files: FileEntry[]): Promise<Issue[]>;
@@ -0,0 +1,114 @@
1
+ import { detectPythonProject } from '../core/languages/pythonManifests.js';
2
+ // Small, conservative seed lists. Easy to extend later.
3
+ const DEPRECATED_WARNING = new Set([
4
+ 'nose',
5
+ 'simplejson',
6
+ 'pycrypto',
7
+ 'mysql-python',
8
+ ]);
9
+ const DEPRECATED_INFO = new Set([
10
+ 'python-dateutil',
11
+ ]);
12
+ const HEAVY_INFO = new Set([
13
+ 'pandas',
14
+ 'numpy',
15
+ 'torch',
16
+ 'tensorflow',
17
+ ]);
18
+ const DEPRECATION_REASONS = {
19
+ nose: 'nose is retired. Use pytest instead.',
20
+ simplejson: 'simplejson is no longer needed; the stdlib json module is equivalent for most use cases.',
21
+ pycrypto: 'pycrypto is unmaintained. Use pycryptodome as a drop-in replacement.',
22
+ 'mysql-python': 'mysql-python is Python 2 only. Use mysqlclient or PyMySQL.',
23
+ 'python-dateutil': 'python-dateutil is heavy. Many use cases are now covered by stdlib datetime.',
24
+ };
25
+ const HEAVY_REASONS = {
26
+ pandas: 'pandas is a heavy dependency (~30MB). Reach for it only if you actually need dataframes.',
27
+ numpy: 'numpy is a heavy dependency (~15MB). Only pull it if you need numerical arrays.',
28
+ torch: 'torch is a very heavy dependency. Consider torch-cpu if GPU is not needed.',
29
+ tensorflow: 'tensorflow is a very heavy dependency. Consider tensorflow-cpu for inference-only use.',
30
+ };
31
+ export async function check(rootPath, files) {
32
+ const info = await detectPythonProject(rootPath, files);
33
+ if (!info)
34
+ return [];
35
+ const issues = [];
36
+ const seen = new Set();
37
+ for (const dep of info.declared) {
38
+ const id = `dep-risk-${dep.name}`;
39
+ if (seen.has(id))
40
+ continue;
41
+ if (DEPRECATED_WARNING.has(dep.name)) {
42
+ seen.add(id);
43
+ issues.push({
44
+ id,
45
+ title: `Deprecated Python package: ${dep.name}`,
46
+ description: DEPRECATION_REASONS[dep.name] ?? `${dep.name} is deprecated.`,
47
+ severity: 'error',
48
+ category: 'dependencies',
49
+ fixAvailable: false,
50
+ locations: [{ file: dep.source, line: dep.line }],
51
+ });
52
+ continue;
53
+ }
54
+ if (DEPRECATED_INFO.has(dep.name)) {
55
+ seen.add(id);
56
+ issues.push({
57
+ id,
58
+ title: `Consider alternatives to ${dep.name}`,
59
+ description: DEPRECATION_REASONS[dep.name] ?? `${dep.name} may be avoidable.`,
60
+ severity: 'info',
61
+ category: 'dependencies',
62
+ fixAvailable: false,
63
+ locations: [{ file: dep.source, line: dep.line }],
64
+ });
65
+ continue;
66
+ }
67
+ if (HEAVY_INFO.has(dep.name)) {
68
+ seen.add(id);
69
+ issues.push({
70
+ id,
71
+ title: `Heavy Python dependency: ${dep.name}`,
72
+ description: HEAVY_REASONS[dep.name] ?? `${dep.name} is a heavy dependency.`,
73
+ severity: 'info',
74
+ category: 'dependencies',
75
+ fixAvailable: false,
76
+ locations: [{ file: dep.source, line: dep.line }],
77
+ });
78
+ continue;
79
+ }
80
+ // Wildcard / unpinned: only flag entries that came from a requirements file
81
+ // (pyproject.toml dependency strings without a version spec are common and
82
+ // not a smell). An empty versionSpec from requirements.txt IS a smell.
83
+ if (dep.source.endsWith('.txt') &&
84
+ (dep.versionSpec === '' || dep.versionSpec === '*')) {
85
+ seen.add(id);
86
+ issues.push({
87
+ id,
88
+ title: `Unpinned Python dependency: ${dep.name}`,
89
+ description: `\`${dep.name}\` in ${dep.source} has no version constraint. Pin to a specific version (==X.Y.Z) for reproducible builds.`,
90
+ severity: 'error',
91
+ category: 'dependencies',
92
+ fixAvailable: false,
93
+ locations: [{ file: dep.source, line: dep.line }],
94
+ });
95
+ }
96
+ }
97
+ // No-lockfile: only emit if there ARE declared deps AND no lockfile-like
98
+ // thing exists. pyproject-only projects without a lockfile are fine for
99
+ // library-style packages, so this is a warning not an error.
100
+ if (!info.hasLockfile && info.declared.length > 0) {
101
+ const severity = 'warning';
102
+ issues.push({
103
+ id: 'dep-risk-no-python-lockfile',
104
+ title: 'No Python lockfile detected',
105
+ description: 'No lockfile (poetry.lock / Pipfile.lock / pdm.lock / uv.lock / conda-lock.yml) or pinned requirements.txt found. Builds may resolve different versions over time.',
106
+ severity,
107
+ category: 'dependencies',
108
+ fixAvailable: false,
109
+ locations: [{ file: info.manifestFiles[0] ?? 'pyproject.toml' }],
110
+ });
111
+ }
112
+ return issues;
113
+ }
114
+ //# sourceMappingURL=pythonDependencyRiskCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pythonDependencyRiskCheck.js","sourceRoot":"","sources":["../../src/analyzers/pythonDependencyRiskCheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FileEntry, Issue } from '../types.js';
2
+ export declare function check(rootPath: string, files: FileEntry[]): Promise<Issue[]>;
@@ -0,0 +1,119 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ const LINTER_CONFIG_FILES = [
4
+ 'ruff.toml',
5
+ '.ruff.toml',
6
+ '.flake8',
7
+ '.pylintrc',
8
+ 'pylintrc',
9
+ ];
10
+ const FORMATTER_CONFIG_FILES = [
11
+ '.autopep8',
12
+ '.yapfrc',
13
+ 'yapf.ini',
14
+ ];
15
+ async function tryRead(absolutePath) {
16
+ try {
17
+ return await fs.readFile(absolutePath, 'utf-8');
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export async function check(rootPath, files) {
24
+ const hasPython = files.some((f) => f.extension === '.py' || f.extension === '.pyw');
25
+ if (!hasPython)
26
+ return [];
27
+ const rootBasenames = new Set(files
28
+ .filter((f) => !f.directory || f.directory === '.')
29
+ .map((f) => path.basename(f.relativePath)));
30
+ const pyproject = await tryRead(path.join(rootPath, 'pyproject.toml'));
31
+ const setupCfg = await tryRead(path.join(rootPath, 'setup.cfg'));
32
+ const reqRels = files
33
+ .filter((f) => (!f.directory || f.directory === '.') &&
34
+ /^requirements(-.*)?\.txt$/i.test(path.basename(f.relativePath)))
35
+ .map((f) => f.relativePath);
36
+ let requirementsBlob = '';
37
+ for (const rel of reqRels) {
38
+ const content = await tryRead(path.join(rootPath, rel));
39
+ if (content)
40
+ requirementsBlob += content + '\n';
41
+ }
42
+ const manifestHaystack = [pyproject ?? '', setupCfg ?? '', requirementsBlob].join('\n');
43
+ // ── Linter detection ──
44
+ let hasLinter = false;
45
+ for (const f of LINTER_CONFIG_FILES) {
46
+ if (rootBasenames.has(f)) {
47
+ hasLinter = true;
48
+ break;
49
+ }
50
+ }
51
+ if (!hasLinter && pyproject) {
52
+ if (/\[tool\.ruff\]|\[tool\.flake8\]|\[tool\.pylint\]|\[tool\.pylint\./.test(pyproject)) {
53
+ hasLinter = true;
54
+ }
55
+ }
56
+ if (!hasLinter && setupCfg) {
57
+ if (/\[flake8\]|\[pylint\]/.test(setupCfg))
58
+ hasLinter = true;
59
+ }
60
+ if (!hasLinter) {
61
+ for (const name of ['ruff', 'flake8', 'pylint', 'pyflakes']) {
62
+ const re = new RegExp(`(^|[\\s"'\`\\[\\],={}><~^!;])${name}(\\b|[^a-zA-Z0-9_.-])`, 'im');
63
+ if (re.test(manifestHaystack)) {
64
+ hasLinter = true;
65
+ break;
66
+ }
67
+ }
68
+ }
69
+ // ── Formatter detection ──
70
+ // ruff also formats (ruff format), so if linter is ruff that satisfies formatter.
71
+ let hasFormatter = false;
72
+ for (const f of FORMATTER_CONFIG_FILES) {
73
+ if (rootBasenames.has(f)) {
74
+ hasFormatter = true;
75
+ break;
76
+ }
77
+ }
78
+ if (!hasFormatter && pyproject) {
79
+ if (/\[tool\.black\]|\[tool\.autopep8\]|\[tool\.yapf\]|\[tool\.ruff\.format\]|\[tool\.ruff\]/.test(pyproject)) {
80
+ hasFormatter = true;
81
+ }
82
+ }
83
+ if (!hasFormatter && setupCfg) {
84
+ if (/\[yapf\]/.test(setupCfg))
85
+ hasFormatter = true;
86
+ }
87
+ if (!hasFormatter) {
88
+ for (const name of ['black', 'ruff', 'autopep8', 'yapf']) {
89
+ const re = new RegExp(`(^|[\\s"'\`\\[\\],={}><~^!;])${name}(\\b|[^a-zA-Z0-9_.-])`, 'im');
90
+ if (re.test(manifestHaystack)) {
91
+ hasFormatter = true;
92
+ break;
93
+ }
94
+ }
95
+ }
96
+ const issues = [];
97
+ if (!hasLinter) {
98
+ issues.push({
99
+ id: 'missing-python-linter',
100
+ title: 'No Python linter configured',
101
+ description: 'No ruff / flake8 / pylint configuration or dependency found. A linter catches bugs and enforces style.',
102
+ severity: 'warning',
103
+ category: 'linting',
104
+ fixAvailable: false,
105
+ });
106
+ }
107
+ if (!hasFormatter) {
108
+ issues.push({
109
+ id: 'missing-python-formatter',
110
+ title: 'No Python formatter configured',
111
+ description: 'No black / ruff-format / autopep8 / yapf configuration or dependency found. A formatter ensures consistent code style.',
112
+ severity: 'warning',
113
+ category: 'formatting',
114
+ fixAvailable: false,
115
+ });
116
+ }
117
+ return issues;
118
+ }
119
+ //# sourceMappingURL=pythonLinterCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pythonLinterCheck.js","sourceRoot":"","sources":["../../src/analyzers/pythonLinterCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,mBAAmB,GAAG;IAC1B,WAAW;IACX,YAAY;IACZ,SAAS;IACT,WAAW;IACX,UAAU;CACX,CAAC;AAEF,MAAM,sBAAsB,GAAG;IAC7B,WAAW;IACX,SAAS;IACT,UAAU;CACX,CAAC;AAEF,KAAK,UAAU,OAAO,CAAC,YAAoB;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAgB,EAAE,KAAkB;IAC9D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC;IACrF,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,KAAK;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC;SAClD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAC7C,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,KAAK;SAClB,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC;QACrC,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CACnE;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAC9B,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QACxD,IAAI,OAAO;YAAE,gBAAgB,IAAI,OAAO,GAAG,IAAI,CAAC;IAClD,CAAC;IACD,MAAM,gBAAgB,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,QAAQ,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExF,yBAAyB;IACzB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,CAAC,IAAI,mBAAmB,EAAE,CAAC;QACpC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,CAAC,SAAS,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,mEAAmE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACxF,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,SAAS,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS,GAAG,IAAI,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;YAC5D,MAAM,EAAE,GAAG,IAAI,MAAM,CACnB,gCAAgC,IAAI,uBAAuB,EAC3D,IAAI,CACL,CAAC;YACF,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC9B,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,kFAAkF;IAClF,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACvC,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,YAAY,GAAG,IAAI,CAAC;YACpB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,yFAAyF,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9G,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,QAAQ,EAAE,CAAC;QAC9B,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,YAAY,GAAG,IAAI,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;YACzD,MAAM,EAAE,GAAG,IAAI,MAAM,CACnB,gCAAgC,IAAI,uBAAuB,EAC3D,IAAI,CACL,CAAC;YACF,IAAI,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC9B,YAAY,GAAG,IAAI,CAAC;gBACpB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,uBAAuB;YAC3B,KAAK,EAAE,6BAA6B;YACpC,WAAW,EACT,wGAAwG;YAC1G,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,SAAS;YACnB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,0BAA0B;YAC9B,KAAK,EAAE,gCAAgC;YACvC,WAAW,EACT,wHAAwH;YAC1H,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,YAAY;YACtB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { FileEntry, Issue } from '../types.js';
2
+ export declare function check(rootPath: string, files: FileEntry[]): Promise<Issue[]>;