universal-ast-mapper 2.0.0 → 2.0.1

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 (75) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +261 -12
  3. package/dist/ai-refactor.js +185 -0
  4. package/dist/ai-testgen.js +105 -0
  5. package/dist/analysis.js +134 -0
  6. package/dist/arch-rules.js +82 -0
  7. package/dist/callgraph.js +467 -0
  8. package/dist/check.js +112 -0
  9. package/dist/cli.js +2284 -0
  10. package/dist/complexity.js +98 -0
  11. package/dist/config.js +53 -0
  12. package/dist/contextpack.js +79 -0
  13. package/dist/coupling.js +35 -0
  14. package/dist/covmerge.js +176 -0
  15. package/dist/crosslang.js +425 -0
  16. package/dist/dashboard.js +259 -0
  17. package/dist/diagram.js +264 -0
  18. package/dist/diskcache.js +97 -0
  19. package/dist/docgen.js +156 -0
  20. package/dist/embeddings.js +136 -0
  21. package/dist/explain.js +123 -0
  22. package/dist/explorer.js +123 -0
  23. package/dist/extractors/c.js +204 -0
  24. package/dist/extractors/common.js +56 -0
  25. package/dist/extractors/cpp.js +272 -0
  26. package/dist/extractors/csharp.js +209 -0
  27. package/dist/extractors/go.js +212 -0
  28. package/dist/extractors/java.js +152 -0
  29. package/dist/extractors/kotlin.js +159 -0
  30. package/dist/extractors/php.js +208 -0
  31. package/dist/extractors/python.js +153 -0
  32. package/dist/extractors/ruby.js +146 -0
  33. package/dist/extractors/rust.js +249 -0
  34. package/dist/extractors/swift.js +192 -0
  35. package/dist/extractors/typescript.js +577 -0
  36. package/dist/fix.js +92 -0
  37. package/dist/gitdiff.js +178 -0
  38. package/dist/graph-analysis.js +279 -0
  39. package/dist/graph.js +165 -0
  40. package/dist/history.js +36 -0
  41. package/dist/html.js +658 -0
  42. package/dist/incremental.js +122 -0
  43. package/dist/index.js +1945 -0
  44. package/dist/indexstore.js +105 -0
  45. package/dist/layers.js +36 -0
  46. package/dist/lsp.js +238 -0
  47. package/dist/modulecoupling.js +0 -0
  48. package/dist/parser.js +84 -0
  49. package/dist/patch.js +199 -0
  50. package/dist/plugins.js +88 -0
  51. package/dist/pool.js +114 -0
  52. package/dist/prompts.js +67 -0
  53. package/dist/registry.js +87 -0
  54. package/dist/report.js +441 -0
  55. package/dist/resolver.js +222 -0
  56. package/dist/roots.js +47 -0
  57. package/dist/search.js +68 -0
  58. package/dist/security.js +178 -0
  59. package/dist/semantic.js +365 -0
  60. package/dist/serve.js +185 -0
  61. package/dist/sfc.js +27 -0
  62. package/dist/similar.js +98 -0
  63. package/dist/skeleton.js +132 -0
  64. package/dist/smells.js +285 -0
  65. package/dist/sourcemap.js +60 -0
  66. package/dist/testgen.js +280 -0
  67. package/dist/testmap.js +167 -0
  68. package/dist/tsconfig.js +212 -0
  69. package/dist/typeflow.js +124 -0
  70. package/dist/types.js +5 -0
  71. package/dist/unused-params.js +127 -0
  72. package/dist/webapp.js +341 -0
  73. package/dist/worker.js +27 -0
  74. package/dist/workspace.js +330 -0
  75. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ since 1.0.0, guarantees a stable MCP tool / CLI surface across the 1.x line.
6
6
 
7
7
  ---
8
8
 
9
+ ## [2.0.1] — 2026-06-21 · patch
10
+
11
+ - **fix:** add `prepare` script so `dist/` is built automatically on `npm install` (cloners no longer need a separate `npm run build`)
12
+ - **fix:** bump `hono` transitive dep via `npm audit fix` (path traversal + CORS issues in unused middleware; no runtime impact on stdio MCP server)
13
+ - **docs:** comprehensive README update — 44 MCP tools / 49 CLI commands, all v1.33–v2.0 features documented, `arch.rules` config, updated project layout, full changelog
14
+ - **ci:** `example-validate.yml` now builds from source instead of relying on published npm package; `action.yml` switches from `npx -p` to `npm exec --package` (npm 10 compatibility)
15
+
16
+ ---
17
+
9
18
  ## [2.0.0] — 2026-06-20 · persistent index, live reload, TF-IDF rerank, auto-patch, arch rules, doc gen
10
19
 
11
20
  ### Breaking
package/README.md CHANGED
@@ -4,7 +4,7 @@ An **MCP server + CLI tool** that turns source code into structured, machine-rea
4
4
 
5
5
  Built on [tree-sitter](https://tree-sitter.github.io/) WASM grammars. Zero regex guessing — real AST parsing.
6
6
 
7
- **30 MCP tools / 32 CLI commands / 5 MCP prompts** spanning skeletons, dependency graphs, and deep analysis — dead code, cycles, change-impact, complexity, duplicates, unused params, type-flow, decorators, test-coverage mapping — plus monorepo support, an interactive **graph explorer** with a **coupling overlay** (`ast-map explore`), **watch mode**, a one-page **health dashboard** with test-coverage, coupling and SDP cards (`ast-map report`), a **persistent parse cache + parallel parsing** (warm re-scans skip parsing entirely), and a **CI quality gate** (`ast-map check`, baseline ratchet).
7
+ **44 MCP tools / 49 CLI commands / 5 MCP prompts** spanning skeletons, dependency graphs, and deep analysis — dead code, cycles, change-impact, complexity, duplicates, unused params, type-flow, decorators, test-coverage mapping — plus monorepo support, an interactive **graph explorer** with a **coupling overlay** (`ast-map explore`), **watch mode**, a one-page **health dashboard** with test-coverage, coupling and SDP cards (`ast-map report`), a **persistent parse cache + parallel parsing** (warm re-scans skip parsing entirely), a **CI quality gate** (`ast-map check`, baseline ratchet), **code smell + security scanning** with AI-powered auto-patch (`ast-map patch`), **AI test generation** (`ast-map testgen --ai`), **Mermaid diagram export** (`ast-map diagram`), a **full-featured web SPA** (`ast-map serve`), **custom lint plugins** (`ast-map plugins`), and new in **v2.0.0**: persistent skeleton index, architecture import rules, TF-IDF re-ranking for semantic search, and API doc generation.
8
8
 
9
9
  **Supported languages:** TypeScript · TSX · JavaScript (ESM/CJS) · Python · Go · Rust · Java · C# · C · C++ · Kotlin · Swift · Vue · Svelte (SFC `<script>`) · **PHP** · **Ruby**
10
10
 
@@ -118,6 +118,8 @@ ast-map explore [dir] [-o out.html]
118
118
  ast-map watch [dir] [-o out.html]
119
119
  ast-map sourcemap <file>
120
120
  ast-map report [dir] [-o report.html]
121
+ ast-map dashboard [dir] # audit KPI summary (dead/cycles/smells/risk)
122
+ ast-map history [dir] # historical health trend chart
121
123
  ast-map diff [base] [--dir <d>] # git-aware changed symbols + impact
122
124
  ast-map risk [dir] [-n N] # churn × complexity
123
125
  ast-map pack <file> [symbol] [--scan <d>] # minimal context pack
@@ -127,12 +129,27 @@ ast-map modules [dir] # directory-level coupling + ed
127
129
  ast-map cache [stats|clear] # persistent parse cache (.ast-map/cache)
128
130
  ast-map check [dir] [--update-baseline] [--min-score N] [--max-cycles N] ...
129
131
  ast-map search <pattern> [dir] [-m contains|exact|regex] [-k kind] [-e]
130
- ast-map find <query> [dir] [-l N] [-k kind] [-e] # semantic: by meaning
132
+ ast-map find <query> [dir] [-l N] [-k kind] [-e] [--rerank] [--api-key KEY]
131
133
  ast-map tests [dir] [alias: coverage] [-u] [--links] [-n N]
134
+ ast-map testgen <path> [--framework vitest|jest|mocha|node|pytest|gotest] [--ai]
135
+ ast-map smells [path] [--changed-since <ref>] [--json]
136
+ ast-map security [path] [--min-severity critical|high|medium|low] [--changed-since <ref>]
137
+ ast-map diagram [dir] [--type class|deps|modules] [--max-nodes N]
138
+ ast-map fix [dir] [--ai] [--min-priority 1|2|3] [--api-key KEY]
139
+ ast-map init [--defaults] # scaffold .ast-map.json + plugin example
132
140
  ast-map deps <file> [--scan <dir>]
133
141
  ast-map top <dir> [-n 10]
134
142
  ast-map impact <file> <symbol> [--scan <dir>]
135
143
  ast-map calls <file> <fn> [--scan <dir>]
144
+ ast-map explain <file> <symbol> [--scan <dir>] [--ai] [--api-key KEY]
145
+ ast-map similar [dir] [--kinds fn,class] [--min-group N]
146
+ ast-map serve [dir] [--port N] [--watch] # web SPA + REST + SSE
147
+ ast-map covmerge <report> [--format auto|istanbul|lcov|clover|cobertura]
148
+ ast-map plugins [dir] # run .ast-map/plugins/*.mjs
149
+ ast-map index [dir] [--force] # build .ast-map/index.json
150
+ ast-map arch [dir] # check architecture import rules (CI-friendly)
151
+ ast-map patch [dir] [-y] [--severity error|warning] [--smells-only] [--security-only]
152
+ ast-map doc [dir] [-o file] [--html] [--exported-only] [--ai] [--api-key KEY]
136
153
  ```
137
154
 
138
155
  ### Examples
@@ -588,6 +605,166 @@ Return the N most-imported symbols — your codebase's "God Nodes" where a break
588
605
 
589
606
  ---
590
607
 
608
+ ### `detect_code_smells`
609
+ Scan a file or directory for structural code smells: god classes (too many public methods/fields), long methods, long parameter lists, primitive obsession, and shallow wrappers. Returns per-file smell lists with file, line, symbol, severity, and message.
610
+
611
+ ```json
612
+ { "path": "src/", "scanned": 42, "total": 7, "smells": [
613
+ { "file": "src/auth.ts", "kind": "long-method", "symbol": "validate",
614
+ "line": 8, "severity": "warning", "message": "Method exceeds 60 lines (82)" }
615
+ ]}
616
+ ```
617
+
618
+ **Params:** `path`, `max_methods` (default 10), `max_fields` (default 8), `max_method_lines` (default 60), `max_params` (default 4)
619
+
620
+ ---
621
+
622
+ ### `scan_security`
623
+ Static security scan across 12 rules: `eval`, `innerHTML`, `dangerouslySetInnerHTML`, `child_process`, shell-exec, weak-crypto, hardcoded-secret, sql-injection, http-url, no-rate-limit, prototype-pollution. Returns issues with file, rule, severity, line, and snippet.
624
+
625
+ ```json
626
+ { "path": "src/", "scanned": 42, "total": 2, "issues": [
627
+ { "file": "src/db.ts", "rule": "sql-injection", "severity": "critical",
628
+ "line": 14, "snippet": "db.query(`SELECT * FROM users WHERE id = ${id}`)" }
629
+ ]}
630
+ ```
631
+
632
+ **Params:** `path`, `min_severity` (`"critical"` | `"high"` | `"medium"` | `"low"`)
633
+
634
+ ---
635
+
636
+ ### `generate_diagram`
637
+ Generate a Mermaid diagram of the codebase.
638
+ - `type=class` — `classDiagram` of classes, interfaces, enums and their relationships
639
+ - `type=deps` — file dependency graph (`graph TD`)
640
+ - `type=modules` — collapsed module-level graph (`graph LR`)
641
+
642
+ Returns a `mermaid` string you can paste into any Mermaid renderer or GitHub markdown.
643
+
644
+ **Params:** `path`, `type` (`"class"` | `"deps"` | `"modules"`), `max_nodes` (default 50)
645
+
646
+ ---
647
+
648
+ ### `get_fix_suggestions`
649
+ Return actionable, prioritized fix suggestions derived from dead exports, code smells, and security issues. Each suggestion carries a kind, file, line, description, before/after snippet, and priority (1 = must fix, 2 = should fix, 3 = nice to have).
650
+
651
+ ```json
652
+ { "path": "src/", "scanned": 42, "total": 5, "suggestions": [
653
+ { "kind": "security", "priority": 1, "file": "src/db.ts", "line": 14,
654
+ "description": "SQL injection via template literal",
655
+ "before": "db.query(`SELECT * WHERE id=${id}`)",
656
+ "after": "db.query('SELECT * WHERE id=?', [id])" }
657
+ ]}
658
+ ```
659
+
660
+ **Params:** `path`, `min_priority` (1–3, default 3 = all)
661
+
662
+ ---
663
+
664
+ ### `generate_tests`
665
+ Generate test stubs for a source file using its AST skeleton. Supports vitest, jest, mocha, node:test, pytest, and gotest. Returns the generated test file content plus metadata: `testCount`, `framework`, `testFilePath`.
666
+
667
+ **Params:** `path`, `framework` (`"vitest"` | `"jest"` | `"mocha"` | `"node"` | `"pytest"` | `"gotest"`), `exported_only` (default `true`)
668
+
669
+ ---
670
+
671
+ ### `generate_tests_ai`
672
+ Same as `generate_tests` but enhances stubs with real assertions via Claude — no more TODO placeholders. Falls back gracefully to stubs when the API key is absent. Requires `ANTHROPIC_API_KEY` or explicit `api_key`.
673
+
674
+ **Params:** `path`, `framework`, `api_key`, `model`
675
+
676
+ ---
677
+
678
+ ### `ai_refactor`
679
+ Send the smells or security issues found in a file to Claude and receive concrete refactored code. Returns `before`/`after` code blocks plus an explanation for each issue. Requires `ANTHROPIC_API_KEY` or explicit `api_key`.
680
+
681
+ ```json
682
+ { "path": "src/auth.ts", "total": 2, "results": [
683
+ { "kind": "smell", "before": "function auth(u,p,t,r,o,c) {...}",
684
+ "after": "function auth({ user, pass, token }: AuthParams) {...}",
685
+ "explanation": "Replaced long parameter list with a typed options object." }
686
+ ]}
687
+ ```
688
+
689
+ **Params:** `path`, `kind` (`"smell"` | `"security"` | `"both"`), `api_key`, `model`, `limit` (default 3)
690
+
691
+ ---
692
+
693
+ ### `explain_symbol`
694
+ Structural explanation of any named symbol: what it does, who calls it, what it depends on, detected smells, cyclomatic complexity rating, and estimated change risk. With `ai: true`, Claude writes a prose explanation using the structural data.
695
+
696
+ ```json
697
+ { "symbol": "validateSession", "file": "src/auth.ts",
698
+ "calledBy": [{ "file": "src/middleware.ts" }],
699
+ "calls": [{ "callee": "prisma.session.findUnique" }],
700
+ "smells": [], "complexity": "low", "changeRisk": "medium",
701
+ "explanation": "Validates an incoming JWT, looks up the session …" }
702
+ ```
703
+
704
+ **Params:** `path`, `symbol`, `scanDir`, `ai` (default `false`), `api_key`, `model`
705
+
706
+ ---
707
+
708
+ ### `find_similar`
709
+ Find groups of functions, methods, or classes that share the same structural fingerprint (param count, async, return type, size, nesting) across a directory — duplication and consolidation candidates, no AI or text comparison needed.
710
+
711
+ ```json
712
+ { "directory": "src/", "groupCount": 3, "groups": [
713
+ { "fingerprint": "fn|2|async|void|small", "size": 3,
714
+ "members": [
715
+ { "file": "src/a.ts", "name": "fetchUser" },
716
+ { "file": "src/b.ts", "name": "fetchPost" }
717
+ ]}
718
+ ]}
719
+ ```
720
+
721
+ **Params:** `path`, `kinds` (default `["function","method","class"]`), `min_group_size` (default 2)
722
+
723
+ ---
724
+
725
+ ### `merge_coverage`
726
+ Enrich the structural test coverage map with actual line/branch percentages from a real coverage report. Supports Istanbul JSON, lcov, Clover XML, and Cobertura XML. Returns enriched per-file coverage, dead tests (tested but 0% actual coverage), and uncovered files.
727
+
728
+ **Params:** `report` (path to coverage file), `path` (project dir), `format` (`"auto"` | `"istanbul"` | `"lcov"` | `"clover"` | `"cobertura"`)
729
+
730
+ ---
731
+
732
+ ### `run_plugins`
733
+ Load and run all `.mjs`/`.js` plugins from `<root>/.ast-map/plugins/` against the current skeletons. Each plugin exports an `AstMapPlugin` with an `id` and a `run(ctx)` function that returns violations. Returns per-plugin violation lists with file, line, symbol, severity, and message.
734
+
735
+ **Params:** `path` (optional, defaults to root)
736
+
737
+ ---
738
+
739
+ ### `build_index`
740
+ Build or refresh the persistent skeleton index at `.ast-map/index.json`. Subsequent CLI commands and the `check_arch_rules` tool read from this index (SHA-1 hash verified) for 10–100× faster analysis on warm runs.
741
+
742
+ **Params:** `dir` (optional), `force` (ignore cached hashes and rebuild all)
743
+
744
+ ---
745
+
746
+ ### `check_arch_rules`
747
+ Enforce forbidden/required import rules declared in `.ast-map.json` under `arch.rules`. Returns structured violations with file, rule name, and severity (`error` | `warning`). Uses the persistent index when available.
748
+
749
+ ```json
750
+ { "ruleCount": 2, "violationCount": 1, "violations": [
751
+ { "rule": "no-ui-in-domain", "severity": "error",
752
+ "from": "src/domain/order.ts", "to": "src/ui/Button.tsx",
753
+ "message": "Domain layer must not import UI layer" }
754
+ ]}
755
+ ```
756
+
757
+ **Params:** `dir` (optional)
758
+
759
+ ---
760
+
761
+ ### `generate_docs`
762
+ Generate Markdown or HTML API documentation from the skeleton of a directory. Optionally enhance symbol descriptions with Claude (`ai: true`). When `exportedOnly` is `true` (default), only exported symbols are included.
763
+
764
+ **Params:** `dir` (optional), `format` (`"markdown"` | `"html"`), `exportedOnly` (default `true`), `ai` (default `false`), `apiKey`
765
+
766
+ ---
767
+
591
768
  ## Project Config — `.ast-map.config.json`
592
769
 
593
770
  Place in your project root. All fields optional.
@@ -607,12 +784,30 @@ Place in your project root. All fields optional.
607
784
  "maxCycles": 0,
608
785
  "maxSdpViolations": 10,
609
786
  "minScore": 70
787
+ },
788
+ "arch": {
789
+ "rules": [
790
+ {
791
+ "name": "no-ui-in-domain",
792
+ "from": "src/domain/**",
793
+ "forbidImport": "src/ui/**",
794
+ "severity": "error",
795
+ "message": "Domain layer must not import UI layer"
796
+ },
797
+ {
798
+ "name": "services-must-use-repo",
799
+ "from": "src/services/**",
800
+ "requireImport": "src/repositories/**",
801
+ "severity": "warning"
802
+ }
803
+ ]
610
804
  }
611
805
  }
612
806
  ```
613
807
 
614
808
  - `cache` — persistent parse cache in `<root>/.ast-map/cache` (default `true`; also disabled by `AST_MAP_NO_CACHE=1`). Inspect/clear with `ast-map cache [stats|clear]`.
615
809
  - `check` — default thresholds for `ast-map check` / `check_quality_gate`; CLI flags override per run.
810
+ - `arch.rules` — declarative import rules enforced by `ast-map arch` / `check_arch_rules`. Each rule has a `from` glob, a `forbidImport` or `requireImport` glob, optional `name`, `severity` (`"error"` | `"warning"`), and `message`. `ast-map arch` exits non-zero on errors (CI-friendly). Globs support `**` (any depth), `*` (single segment), and `?` (single char).
616
811
 
617
812
  The config is read live — changes take effect on the next call without restarting the MCP server.
618
813
 
@@ -726,30 +921,80 @@ interface SkeletonFile {
726
921
 
727
922
  ```
728
923
  src/
729
- ├── index.ts — MCP server + all 14 tool registrations
730
- ├── cli.ts — ast-map CLI (13 commands)
924
+ ├── index.ts — MCP server + 44 tool registrations
925
+ ├── cli.ts — ast-map CLI (49 commands)
926
+ ├── lsp.ts — JSON-RPC 2.0 LSP server (diagnostics + code lens)
731
927
  ├── types.ts — SkeletonFile, SymbolNode, ImportRef
732
- ├── config.ts — SkeletonOptions, resolveOptions(), loadProjectConfig()
928
+ ├── config.ts — SkeletonOptions, resolveOptions(), loadProjectConfig() + arch rules type
733
929
  ├── registry.ts — language detection + extractor registry
734
930
  ├── parser.ts — tree-sitter WASM loader + AST node helpers
735
931
  ├── skeleton.ts — buildSkeleton(), collectSourceFiles() + parse cache
736
932
  ├── resolver.ts — resolveImportPath(), resolveFileImports() (TS/JS/Python relative)
737
- ├── crosslang.ts Java FQCN / C# namespace / Rust crate / Go module resolvers + index cache
933
+ ├── tsconfig.ts TS/JS path-alias resolution via tsconfig.json / jsconfig.json
934
+ ├── crosslang.ts — Java FQCN / C# namespace / Rust crate / Go module resolvers
935
+ ├── roots.ts — multi-root + unlocked-mode boundary resolution
738
936
  ├── graph.ts — buildSymbolGraph() (language-aware second pass)
739
- ├── graph-analysis.ts — findDeadExports(), findCircularDeps(), getChangeImpact(),
740
- │ getFileDeps(), getTopSymbols()
937
+ ├── graph-analysis.ts — findDeadExports(), findCircularDeps(), getChangeImpact(), getTopSymbols()
741
938
  ├── callgraph.ts — buildCallGraph() — AST-level call extraction
742
939
  ├── analysis.ts — findSymbol(), validate helpers, checkGeneralRules()
743
940
  ├── html.ts — renderHtml(), renderCombinedHtml()
744
941
  ├── search.ts — searchSymbols()
942
+ ├── semantic.ts — semanticSearch(), identifier tokenization, programming thesaurus
943
+ ├── diskcache.ts — persistent parse cache (SHA-1 keyed, .ast-map/cache)
944
+ ├── pool.ts — worker-thread parallel parsing pool
945
+ ├── worker.ts — per-worker parse + skeleton build
946
+ ├── coupling.ts — computeCoupling() (Ca / Ce / instability)
947
+ ├── layers.ts — findLayerViolations() (SDP)
948
+ ├── modulecoupling.ts — computeModuleCoupling() (directory-level)
949
+ ├── gitdiff.ts — getChangedFiles(), computeRisk()
950
+ ├── contextpack.ts — packContext() — minimal context for a symbol
951
+ ├── sourcemap.ts — readSourceMap() — inline + external source map parsing
952
+ ├── workspace.ts — analyzeWorkspace() — monorepo package discovery
953
+ ├── typeflow.ts — traceType() — type-flow tracing across a directory
954
+ ├── complexity.ts — computeFileComplexity() — cyclomatic complexity
955
+ ├── unused-params.ts — findUnusedParams() — unused function parameters
956
+ ├── graph-analysis.ts — findDuplicateSymbols(), getFileDeps()
957
+ ├── testmap.ts — mapTestCoverage() — structural test coverage
958
+ ├── check.ts — runQualityGate() — baseline ratchet + absolute thresholds
959
+ ├── report.ts — generateReport() — premium HTML health dashboard
960
+ ├── dashboard.ts — buildDashboard() — audit KPI summary
961
+ ├── history.ts — buildHistory() — historical health trend
962
+ ├── explorer.ts — renderExplorer() — force-directed file graph HTML
963
+ ├── sfc.ts — Vue / Svelte SFC <script> block extraction
964
+ ├── smells.ts — detectSmells() — 6 structural smell patterns
965
+ ├── security.ts — scanFileForSecurityIssues() — 12 static security rules
966
+ ├── diagram.ts — buildClassDiagram(), buildDepsDiagram(), buildModulesDiagram()
967
+ ├── fix.ts — buildFixSuggestions() — prioritised fix list
968
+ ├── testgen.ts — generateTestFile() — test stubs (6 frameworks)
969
+ ├── ai-testgen.ts — tryAiEnhanceTests() — Claude-enhanced test assertions
970
+ ├── ai-refactor.ts — callClaude(), aiRefactorBatch() — AI refactoring
971
+ ├── explain.ts — buildExplainResult(), aiExplain() — symbol explanation
972
+ ├── similar.ts — findSimilar() — structural fingerprint groups
973
+ ├── incremental.ts — content-hash state, filterToGitChanged(), detectChanges()
974
+ ├── covmerge.ts — mergeCoverage() — 4-format coverage parser + structural merge
975
+ ├── plugins.ts — loadPlugins(), runPlugins() — custom lint plugin runner
976
+ ├── serve.ts — startServe() — HTTP server + REST endpoints + SSE /events
977
+ ├── webapp.ts — self-contained SPA template (D3.js, dark theme, 8 pages)
978
+ ├── indexstore.ts — buildIndex(), loadIndex(), isIndexFresh(), getSkeletons()
979
+ ├── arch-rules.ts — checkArchRules(), loadArchRules(), globToRegex()
980
+ ├── patch.ts — generatePatch(), interactivePatch(), colored unified diff
981
+ ├── docgen.ts — buildDocOutput(), renderMarkdown(), renderDocHtml(), aiEnhanceDocs()
982
+ ├── embeddings.ts — buildTfIdfVectors(), cosineSearch(), rerankWithClaude()
983
+ ├── prompts.ts — 5 MCP prompt definitions
745
984
  └── extractors/
746
985
  ├── common.ts — makeSymbol(), toOutline()
747
- ├── typescript.ts — TS/JS/TSX: symbols + imports + re-exports
748
- ├── python.ts — Python: symbols + relative import resolution
986
+ ├── typescript.ts — TS/JS/TSX: symbols + imports + re-exports + decorators
987
+ ├── python.ts — Python: symbols + relative import resolution + decorators
749
988
  ├── go.ts — Go: symbols + imports
750
989
  ├── rust.ts — Rust: struct/trait/enum/impl + `use` imports
751
- ├── java.ts — Java: class/interface/enum/method/field + package + imports
752
- └── csharp.ts — C#: namespace recursion + class/struct/interface/property + `using`
990
+ ├── java.ts — Java: class/interface/enum/method/field + FQCN
991
+ ├── csharp.ts — C#: namespace recursion + class/struct/interface + `using`
992
+ ├── kotlin.ts — Kotlin: FQCN / package index
993
+ ├── swift.ts — Swift: module = directory under Sources/
994
+ ├── c.ts — C: functions + `#include` with header↔impl pairing
995
+ ├── cpp.ts — C++: classes + `#include`
996
+ ├── php.ts — PHP: classes/interfaces/traits/enums + `use` + require/include
997
+ └── ruby.ts — Ruby: classes/modules/methods + require/require_relative
753
998
  ```
754
999
 
755
1000
  ---
@@ -835,6 +1080,10 @@ Not part of the public API: the internal `src/` module layout and the generated
835
1080
 
836
1081
  | Version | What changed |
837
1082
  |---------|--------------|
1083
+ | **2.0.0** | **6 major features** — **Persistent skeleton index** (`ast-map index`, `build_index`): `.ast-map/index.json` hash-based incremental rebuild, 10–100× warm speed; **SSE live reload** (`ast-map serve --watch`, `/events` endpoint): EventSource client auto-refreshes the web SPA on file changes; **TF-IDF re-ranking** (`ast-map find --rerank`): cosine similarity pre-rank + optional Claude API re-ranking; **Auto-patch** (`ast-map patch`, `generatePatch`): colored unified diff with Claude-generated before/after, readline y/N prompt per issue; **Architecture rules** (`ast-map arch`, `check_arch_rules`): forbidden/required import rules in `.ast-map.json` `arch.rules`, glob patterns, CI exit code; **Doc generation** (`ast-map doc`, `generate_docs`): Markdown + HTML API reference, `--ai` flag for Claude descriptions. **44 MCP tools / 49 CLI commands.** |
1084
+ | **1.35.0** | **Web SPA + symbol explanation + similar + coverage merge + plugins** — new `ast-map serve [dir]` launches a dark-theme web app at `:7337` (D3 dependency graph, all analysis pages, 10 REST endpoints); new `ast-map explain <file> <symbol>` structural explanation with optional `--ai` prose (callers, deps, smells, complexity, risk); new `ast-map similar [dir]` AST fingerprint groups (7-component, no AI); new `ast-map covmerge <report>` merges Istanbul/lcov/Clover/Cobertura with the structural map; new `ast-map plugins [dir]` runs custom `.mjs` lint plugins from `.ast-map/plugins/`. New MCP tools: `explain_symbol`, `find_similar`, `merge_coverage`, `run_plugins`. **41 MCP tools / 45 CLI commands.** |
1085
+ | **1.34.0** | **Code intelligence suite + AI refactor + LSP + init** — 7 new MCP tools: `detect_code_smells` (6 patterns), `scan_security` (12 rules), `generate_diagram` (Mermaid class/deps/modules), `get_fix_suggestions` (P1–P3 prioritized), `generate_tests` (6 frameworks), `generate_tests_ai` (Claude-enhanced assertions), `ai_refactor` (before/after patches from Claude). Full **JSON-RPC 2.0 LSP server** (`ast-map-lsp`): dead-export warnings, security-issue errors, complexity code lenses. `ast-map init` scaffolds `.ast-map.json` + plugin example. **37 MCP tools / 41 CLI commands.** |
1086
+ | **1.33.0** | **AI test generation + VS Code extension** — `ast-map testgen --ai` sends source + stubs to Claude and returns tests with real assertions (graceful fallback to stubs). New VS Code extension: complexity code lens, dead-export diagnostics, security diagnostics, Issues Tree View, commands (Generate Tests, Scan Security, Show Diagram, Open Report), status-bar health score. |
838
1087
  | **1.28.0** | **Test coverage in the dashboard** — `ast-map report` / `get_codebase_report` gain a **Test coverage** card (coverage bar + untested sources ranked by risk with Ca/symbols) and stat tile; structural coverage now factors into the health score (capped penalty). Reporting on `src/` only? Test files are **pulled in from the project root automatically**. |
839
1088
  | **1.27.0** | **Test-coverage mapping** — new MCP tool `get_test_coverage` + CLI `ast-map tests` (alias `coverage`): pairs test files with the sources they exercise (import edges + naming conventions) and lists **untested sources ranked by risk** (fan-in, then symbols). Fixture dirs excluded; orphan tests reported. File-level, zero instrumentation. (**30 tools / 32 commands**) |
840
1089
  | **1.26.0** | **Coupling overlay in the explorer** — `ast-map explore` gains a `color: coupling` mode: nodes shaded by **instability** I = Ce/(Ca+Ce) on a green (stable) → red (volatile) scale, with a legend, and Ca / Ce / I readouts in the hover tooltip and detail sidebar. Spot load-bearing files and volatile hotspots at a glance. |
@@ -0,0 +1,185 @@
1
+ import https from "node:https";
2
+ import fs from "node:fs";
3
+ // ─── Anthropic API ────────────────────────────────────────────────────────────
4
+ export async function callClaude(prompt, opts) {
5
+ const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
6
+ if (!apiKey)
7
+ throw new Error("No Anthropic API key — set ANTHROPIC_API_KEY or pass --api-key");
8
+ const model = opts.model ?? "claude-sonnet-4-6";
9
+ const body = JSON.stringify({
10
+ model,
11
+ max_tokens: opts.maxTokens ?? 4096,
12
+ messages: [{ role: "user", content: prompt }],
13
+ });
14
+ return new Promise((resolve, reject) => {
15
+ const req = https.request({
16
+ hostname: "api.anthropic.com",
17
+ path: "/v1/messages",
18
+ method: "POST",
19
+ headers: {
20
+ "content-type": "application/json",
21
+ "x-api-key": apiKey,
22
+ "anthropic-version": "2023-06-01",
23
+ "content-length": Buffer.byteLength(body),
24
+ },
25
+ }, (res) => {
26
+ const chunks = [];
27
+ res.on("data", (c) => chunks.push(c));
28
+ res.on("end", () => {
29
+ const raw = Buffer.concat(chunks).toString("utf8");
30
+ try {
31
+ const parsed = JSON.parse(raw);
32
+ if (parsed.error)
33
+ reject(new Error(`Anthropic API: ${parsed.error.message}`));
34
+ else
35
+ resolve(parsed.content?.[0]?.text ?? "");
36
+ }
37
+ catch {
38
+ reject(new Error(`Unexpected API response: ${raw.slice(0, 300)}`));
39
+ }
40
+ });
41
+ });
42
+ req.on("error", reject);
43
+ req.write(body);
44
+ req.end();
45
+ });
46
+ }
47
+ // ─── Prompt builders ──────────────────────────────────────────────────────────
48
+ function smellPrompt(smell, sourceCode, language) {
49
+ const langFence = language === "typescript" ? "ts" : language === "javascript" ? "js" : language;
50
+ const symbol = smell.symbol ? ` for \`${smell.symbol}\`` : "";
51
+ return `You are an expert ${language} developer performing a refactoring.
52
+
53
+ ## Problem
54
+ Smell type: **${smell.smell}**${symbol}
55
+ Message: ${smell.message}
56
+ File: ${smell.file}${smell.line ? `, line ${smell.line}` : ""}
57
+
58
+ ## Source file
59
+ \`\`\`${langFence}
60
+ ${sourceCode}
61
+ \`\`\`
62
+
63
+ ## Your task
64
+ Refactor the code to eliminate the smell. Provide:
65
+ 1. The **minimal refactored code** — just the changed function/class (not the whole file unless necessary)
66
+ 2. A one-paragraph **explanation** of what you changed and why
67
+
68
+ Format your response EXACTLY as:
69
+ <before>
70
+ // paste the original problematic code block here
71
+ </before>
72
+ <after>
73
+ // paste the refactored code here
74
+ </after>
75
+ <explanation>
76
+ Your explanation here.
77
+ </explanation>`;
78
+ }
79
+ function securityPrompt(issue, sourceCode, language) {
80
+ const langFence = language === "typescript" ? "ts" : language === "javascript" ? "js" : language;
81
+ return `You are a security expert performing a code fix.
82
+
83
+ ## Security Issue
84
+ Rule: **${issue.rule}** (${issue.severity})
85
+ Message: ${issue.message}
86
+ File: ${issue.file}, line ${issue.line}
87
+ Snippet: \`${issue.snippet}\`
88
+
89
+ ## Source file
90
+ \`\`\`${langFence}
91
+ ${sourceCode}
92
+ \`\`\`
93
+
94
+ ## Your task
95
+ Fix the security vulnerability. Provide:
96
+ 1. The **minimal fixed code** — just the changed lines/block
97
+ 2. A one-paragraph **explanation** of the vulnerability and how the fix addresses it
98
+
99
+ Format your response EXACTLY as:
100
+ <before>
101
+ ${issue.snippet}
102
+ </before>
103
+ <after>
104
+ // fixed code here
105
+ </after>
106
+ <explanation>
107
+ Your explanation here.
108
+ </explanation>`;
109
+ }
110
+ // ─── Response parser ──────────────────────────────────────────────────────────
111
+ function parseResponse(raw) {
112
+ const extract = (tag) => {
113
+ const m = raw.match(new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`));
114
+ return m ? m[1].trim() : "";
115
+ };
116
+ return {
117
+ before: extract("before"),
118
+ after: extract("after"),
119
+ explanation: extract("explanation"),
120
+ };
121
+ }
122
+ // ─── Public API ───────────────────────────────────────────────────────────────
123
+ /** Send one refactor target to Claude and return a structured RefactorResult. */
124
+ export async function aiRefactor(target, opts = {}) {
125
+ const model = opts.model ?? "claude-sonnet-4-6";
126
+ let prompt;
127
+ let issue;
128
+ let symbol;
129
+ if (target.kind === "smell" && target.smell) {
130
+ prompt = smellPrompt(target.smell, target.sourceCode, target.language);
131
+ issue = `${target.smell.smell}${target.smell.symbol ? `: ${target.smell.symbol}` : ""}`;
132
+ symbol = target.smell.symbol;
133
+ }
134
+ else if (target.kind === "security" && target.security) {
135
+ prompt = securityPrompt(target.security, target.sourceCode, target.language);
136
+ issue = `${target.security.rule} (${target.security.severity})`;
137
+ }
138
+ else {
139
+ throw new Error("Invalid refactor target: must have smell or security");
140
+ }
141
+ const raw = await callClaude(prompt, opts);
142
+ const { before, after, explanation } = parseResponse(raw);
143
+ return {
144
+ filePath: target.filePath,
145
+ symbol,
146
+ issue,
147
+ before: before || "(see source)",
148
+ after: after || raw,
149
+ explanation: explanation || "(no explanation provided)",
150
+ model,
151
+ };
152
+ }
153
+ /**
154
+ * Batch-refactor: takes a list of targets and calls Claude once per target.
155
+ * Returns results in the same order; errors produce a result with `error` set.
156
+ */
157
+ export async function aiRefactorBatch(targets, opts = {}) {
158
+ const results = [];
159
+ for (const target of targets) {
160
+ try {
161
+ results.push(await aiRefactor(target, opts));
162
+ }
163
+ catch (e) {
164
+ results.push({
165
+ filePath: target.filePath,
166
+ issue: target.kind === "smell" ? target.smell?.smell ?? "smell" : target.security?.rule ?? "security",
167
+ before: "",
168
+ after: "",
169
+ explanation: "",
170
+ model: opts.model ?? "claude-sonnet-4-6",
171
+ error: e instanceof Error ? e.message : String(e),
172
+ });
173
+ }
174
+ }
175
+ return results;
176
+ }
177
+ /** Read source code for a file, returning empty string on error. */
178
+ export function readSource(filePath) {
179
+ try {
180
+ return fs.readFileSync(filePath, "utf8");
181
+ }
182
+ catch {
183
+ return "";
184
+ }
185
+ }
@@ -0,0 +1,105 @@
1
+ import https from "node:https";
2
+ async function callClaude(prompt, opts) {
3
+ const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
4
+ if (!apiKey)
5
+ throw new Error("No Anthropic API key — set ANTHROPIC_API_KEY or pass --api-key");
6
+ const body = JSON.stringify({
7
+ model: opts.model ?? "claude-sonnet-4-6",
8
+ max_tokens: opts.maxTokens ?? 4096,
9
+ messages: [{ role: "user", content: prompt }],
10
+ });
11
+ return new Promise((resolve, reject) => {
12
+ const req = https.request({
13
+ hostname: "api.anthropic.com",
14
+ path: "/v1/messages",
15
+ method: "POST",
16
+ headers: {
17
+ "content-type": "application/json",
18
+ "x-api-key": apiKey,
19
+ "anthropic-version": "2023-06-01",
20
+ "content-length": Buffer.byteLength(body),
21
+ },
22
+ }, (res) => {
23
+ const chunks = [];
24
+ res.on("data", (c) => chunks.push(c));
25
+ res.on("end", () => {
26
+ const raw = Buffer.concat(chunks).toString("utf8");
27
+ try {
28
+ const parsed = JSON.parse(raw);
29
+ if (parsed.error) {
30
+ reject(new Error(`Anthropic API: ${parsed.error.message}`));
31
+ }
32
+ else {
33
+ resolve(parsed.content?.[0]?.text ?? "");
34
+ }
35
+ }
36
+ catch {
37
+ reject(new Error(`Unexpected API response: ${raw.slice(0, 300)}`));
38
+ }
39
+ });
40
+ });
41
+ req.on("error", reject);
42
+ req.write(body);
43
+ req.end();
44
+ });
45
+ }
46
+ // ─── Prompt builder ───────────────────────────────────────────────────────────
47
+ function buildPrompt(sourceFile, sourceCode, stubContent, framework, language) {
48
+ const langFence = language === "typescript" ? "ts" : language === "javascript" ? "js" : language;
49
+ return `You are an expert ${language} developer and TDD practitioner.
50
+
51
+ Your task: given a source file and its auto-generated test stubs, replace all TODO placeholders with real, meaningful assertions.
52
+
53
+ ## Source file: ${sourceFile}
54
+ \`\`\`${langFence}
55
+ ${sourceCode}
56
+ \`\`\`
57
+
58
+ ## Generated stubs to fill in:
59
+ \`\`\`${langFence}
60
+ ${stubContent}
61
+ \`\`\`
62
+
63
+ ## Rules:
64
+ - Test framework: **${framework}**
65
+ - Keep every test name and describe block from the stubs exactly as-is
66
+ - Replace "// TODO: arrange" with real setup code using the source implementation
67
+ - Replace generic assertions (toBeDefined, is not None, assertNotNull) with precise assertions that verify actual return values, side effects, or thrown errors
68
+ - For functions with clear deterministic behavior, use concrete expected values
69
+ - Cover happy path AND at least one edge case (empty/null/zero input) for each test
70
+ - For async functions, always use async/await
71
+ - Do NOT import anything extra beyond what the stubs already import
72
+ - Do NOT add tests beyond what is in the stubs
73
+ - Return ONLY the complete test file — no markdown fences, no explanation`;
74
+ }
75
+ // ─── Stripper ─────────────────────────────────────────────────────────────────
76
+ /** Remove leading/trailing markdown code fences if Claude adds them anyway. */
77
+ function stripFences(text) {
78
+ return text
79
+ .replace(/^```[\w]*\r?\n/m, "")
80
+ .replace(/\r?\n```$/m, "")
81
+ .trim();
82
+ }
83
+ // ─── Public API ───────────────────────────────────────────────────────────────
84
+ /**
85
+ * Enhance a stub-only TestGenResult by asking Claude to fill in real assertions.
86
+ * Falls back to the original stubs if the API is unavailable or `opts.apiKey` / the
87
+ * ANTHROPIC_API_KEY env var is not set.
88
+ */
89
+ export async function aiEnhanceTests(result, sourceCode, language, opts = {}) {
90
+ const enhanced = await callClaude(buildPrompt(result.sourceFile, sourceCode, result.content, result.framework, language), opts);
91
+ const cleaned = stripFences(enhanced);
92
+ return { ...result, content: cleaned, aiEnhanced: true };
93
+ }
94
+ /**
95
+ * Like `aiEnhanceTests` but never throws — returns original stubs if the API call
96
+ * fails, and sets `aiEnhanced: false` along with an `error` field for diagnostics.
97
+ */
98
+ export async function tryAiEnhanceTests(result, sourceCode, language, opts = {}) {
99
+ try {
100
+ return await aiEnhanceTests(result, sourceCode, language, opts);
101
+ }
102
+ catch (e) {
103
+ return { ...result, aiEnhanced: false, error: e instanceof Error ? e.message : String(e) };
104
+ }
105
+ }