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.
- package/CHANGELOG.md +9 -0
- package/README.md +261 -12
- package/dist/ai-refactor.js +185 -0
- package/dist/ai-testgen.js +105 -0
- package/dist/analysis.js +134 -0
- package/dist/arch-rules.js +82 -0
- package/dist/callgraph.js +467 -0
- package/dist/check.js +112 -0
- package/dist/cli.js +2284 -0
- package/dist/complexity.js +98 -0
- package/dist/config.js +53 -0
- package/dist/contextpack.js +79 -0
- package/dist/coupling.js +35 -0
- package/dist/covmerge.js +176 -0
- package/dist/crosslang.js +425 -0
- package/dist/dashboard.js +259 -0
- package/dist/diagram.js +264 -0
- package/dist/diskcache.js +97 -0
- package/dist/docgen.js +156 -0
- package/dist/embeddings.js +136 -0
- package/dist/explain.js +123 -0
- package/dist/explorer.js +123 -0
- package/dist/extractors/c.js +204 -0
- package/dist/extractors/common.js +56 -0
- package/dist/extractors/cpp.js +272 -0
- package/dist/extractors/csharp.js +209 -0
- package/dist/extractors/go.js +212 -0
- package/dist/extractors/java.js +152 -0
- package/dist/extractors/kotlin.js +159 -0
- package/dist/extractors/php.js +208 -0
- package/dist/extractors/python.js +153 -0
- package/dist/extractors/ruby.js +146 -0
- package/dist/extractors/rust.js +249 -0
- package/dist/extractors/swift.js +192 -0
- package/dist/extractors/typescript.js +577 -0
- package/dist/fix.js +92 -0
- package/dist/gitdiff.js +178 -0
- package/dist/graph-analysis.js +279 -0
- package/dist/graph.js +165 -0
- package/dist/history.js +36 -0
- package/dist/html.js +658 -0
- package/dist/incremental.js +122 -0
- package/dist/index.js +1945 -0
- package/dist/indexstore.js +105 -0
- package/dist/layers.js +36 -0
- package/dist/lsp.js +238 -0
- package/dist/modulecoupling.js +0 -0
- package/dist/parser.js +84 -0
- package/dist/patch.js +199 -0
- package/dist/plugins.js +88 -0
- package/dist/pool.js +114 -0
- package/dist/prompts.js +67 -0
- package/dist/registry.js +87 -0
- package/dist/report.js +441 -0
- package/dist/resolver.js +222 -0
- package/dist/roots.js +47 -0
- package/dist/search.js +68 -0
- package/dist/security.js +178 -0
- package/dist/semantic.js +365 -0
- package/dist/serve.js +185 -0
- package/dist/sfc.js +27 -0
- package/dist/similar.js +98 -0
- package/dist/skeleton.js +132 -0
- package/dist/smells.js +285 -0
- package/dist/sourcemap.js +60 -0
- package/dist/testgen.js +280 -0
- package/dist/testmap.js +167 -0
- package/dist/tsconfig.js +212 -0
- package/dist/typeflow.js +124 -0
- package/dist/types.js +5 -0
- package/dist/unused-params.js +127 -0
- package/dist/webapp.js +341 -0
- package/dist/worker.js +27 -0
- package/dist/workspace.js +330 -0
- 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
|
-
**
|
|
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]
|
|
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 +
|
|
730
|
-
├── cli.ts — ast-map CLI (
|
|
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
|
-
├──
|
|
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 +
|
|
752
|
-
|
|
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
|
+
}
|