@toolbaux/guardian 0.1.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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +366 -0
  3. package/dist/adapters/csharp-adapter.js +149 -0
  4. package/dist/adapters/go-adapter.js +96 -0
  5. package/dist/adapters/index.js +16 -0
  6. package/dist/adapters/java-adapter.js +122 -0
  7. package/dist/adapters/python-adapter.js +183 -0
  8. package/dist/adapters/runner.js +69 -0
  9. package/dist/adapters/types.js +1 -0
  10. package/dist/adapters/typescript-adapter.js +179 -0
  11. package/dist/benchmarking/framework.js +91 -0
  12. package/dist/cli.js +343 -0
  13. package/dist/commands/analyze-depth.js +43 -0
  14. package/dist/commands/api-spec-extractor.js +52 -0
  15. package/dist/commands/breaking-change-analyzer.js +334 -0
  16. package/dist/commands/config-compliance.js +219 -0
  17. package/dist/commands/constraints.js +221 -0
  18. package/dist/commands/context.js +101 -0
  19. package/dist/commands/data-flow-tracer.js +291 -0
  20. package/dist/commands/dependency-impact-analyzer.js +27 -0
  21. package/dist/commands/diff.js +146 -0
  22. package/dist/commands/discrepancy.js +71 -0
  23. package/dist/commands/doc-generate.js +163 -0
  24. package/dist/commands/doc-html.js +120 -0
  25. package/dist/commands/drift.js +88 -0
  26. package/dist/commands/extract.js +16 -0
  27. package/dist/commands/feature-context.js +116 -0
  28. package/dist/commands/generate.js +339 -0
  29. package/dist/commands/guard.js +182 -0
  30. package/dist/commands/init.js +209 -0
  31. package/dist/commands/intel.js +20 -0
  32. package/dist/commands/license-dependency-auditor.js +33 -0
  33. package/dist/commands/performance-hotspot-profiler.js +42 -0
  34. package/dist/commands/search.js +314 -0
  35. package/dist/commands/security-boundary-auditor.js +359 -0
  36. package/dist/commands/simulate.js +294 -0
  37. package/dist/commands/summary.js +27 -0
  38. package/dist/commands/test-coverage-mapper.js +264 -0
  39. package/dist/commands/verify-drift.js +62 -0
  40. package/dist/config.js +441 -0
  41. package/dist/extract/ai-context-hints.js +107 -0
  42. package/dist/extract/analyzers/backend.js +1704 -0
  43. package/dist/extract/analyzers/depth.js +264 -0
  44. package/dist/extract/analyzers/frontend.js +2221 -0
  45. package/dist/extract/api-usage-tracker.js +19 -0
  46. package/dist/extract/cache.js +53 -0
  47. package/dist/extract/codebase-intel.js +190 -0
  48. package/dist/extract/compress.js +452 -0
  49. package/dist/extract/context-block.js +356 -0
  50. package/dist/extract/contracts.js +183 -0
  51. package/dist/extract/discrepancies.js +233 -0
  52. package/dist/extract/docs-loader.js +110 -0
  53. package/dist/extract/docs.js +2379 -0
  54. package/dist/extract/drift.js +1578 -0
  55. package/dist/extract/duplicates.js +435 -0
  56. package/dist/extract/feature-arcs.js +138 -0
  57. package/dist/extract/graph.js +76 -0
  58. package/dist/extract/html-doc.js +1409 -0
  59. package/dist/extract/ignore.js +45 -0
  60. package/dist/extract/index.js +455 -0
  61. package/dist/extract/llm-client.js +159 -0
  62. package/dist/extract/pattern-registry.js +141 -0
  63. package/dist/extract/product-doc.js +497 -0
  64. package/dist/extract/python.js +1202 -0
  65. package/dist/extract/runtime.js +193 -0
  66. package/dist/extract/schema-evolution-validator.js +35 -0
  67. package/dist/extract/test-gap-analyzer.js +20 -0
  68. package/dist/extract/tests.js +74 -0
  69. package/dist/extract/types.js +1 -0
  70. package/dist/extract/validate-backend.js +30 -0
  71. package/dist/extract/writer.js +11 -0
  72. package/dist/output-layout.js +37 -0
  73. package/dist/project-discovery.js +309 -0
  74. package/dist/schema/architecture.js +350 -0
  75. package/dist/schema/feature-spec.js +89 -0
  76. package/dist/schema/index.js +8 -0
  77. package/dist/schema/ux.js +46 -0
  78. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harish Kumar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,366 @@
1
+ # Guardian
2
+
3
+ Architectural intelligence for codebases. One command turns your repo into compact, machine-readable context that AI coding tools can reason about without hallucinating.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Initialize a project (creates .specs/, config, pre-commit hook, CLAUDE.md context)
9
+ guardian init
10
+
11
+ # Or run extraction manually
12
+ guardian extract
13
+ guardian generate --ai-context
14
+ ```
15
+
16
+ After `guardian init`, your project gets:
17
+ - `.specs/` directory with architecture snapshots
18
+ - `CLAUDE.md` with auto-injected architecture context (between `<!-- guardian:auto-context -->` markers)
19
+ - Pre-commit hook that keeps context fresh on every commit
20
+ - `guardian.config.json` with auto-detected backend/frontend roots
21
+
22
+ ## What Guardian Solves
23
+
24
+ **Without Guardian:** AI invents fake schemas, imports wrong components, edits high-coupling files blindly, guesses at endpoint paths.
25
+
26
+ **With Guardian:** AI gets a deterministic map of your repo — exact boundaries, coupling hotspots, model-to-endpoint relationships, page routes, and data flows — in ~3,000 tokens.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ # From source
32
+ git clone <repo>
33
+ cd guardian
34
+ npm install && npm run build
35
+ npm link # makes `guardian` available globally
36
+
37
+ # Or install from npm
38
+ npm install -g @toolbaux/guardian
39
+
40
+ # Initialize a project
41
+ cd /path/to/your/project
42
+ guardian init
43
+ ```
44
+
45
+ ### VSCode Extension
46
+
47
+ The extension adds a status bar indicator, background auto-extract on save, and command palette integration.
48
+
49
+ **Install via symlink (development):**
50
+ ```bash
51
+ ln -sf /path/to/guardian/vscode-extension ~/.vscode/extensions/guardian-vscode
52
+ # Reload VSCode: Cmd+Shift+P → "Reload Window"
53
+ ```
54
+
55
+ **Install via .vsix (packaged):**
56
+ ```bash
57
+ cd guardian/vscode-extension
58
+ npx vsce package --allow-missing-repository
59
+ # In VSCode: Cmd+Shift+P → "Extensions: Install from VSIX" → select the .vsix file
60
+ ```
61
+
62
+ **What the extension provides:**
63
+ - Status bar: `✓ Guardian: stable · 35 ep · 8 pg` — click to run drift check
64
+ - Background extract on file save (5s debounce, code files only)
65
+ - Auto-injects fresh context into CLAUDE.md on each save
66
+ - Command palette: 8 commands (see below)
67
+
68
+ **Extension settings:**
69
+ | Setting | Default | Description |
70
+ |---------|---------|-------------|
71
+ | `guardian.autoExtract` | `true` | Auto-run extract + context on file save |
72
+ | `guardian.runOnSave` | `false` | Also run drift check on save (heavier) |
73
+ | `guardian.backendRoot` | `"backend"` | Backend root relative to workspace |
74
+ | `guardian.frontendRoot` | `"frontend"` | Frontend root relative to workspace |
75
+ | `guardian.configPath` | `""` | Path to guardian.config.json |
76
+ | `guardian.debounceMs` | `750` | Debounce for drift-on-save |
77
+
78
+ ## All Commands (18)
79
+
80
+ ### Project Setup
81
+
82
+ ```bash
83
+ # Initialize project: config, .specs dir, pre-commit hook, CLAUDE.md
84
+ guardian init
85
+
86
+ # Full extraction: architecture + UX snapshots + codebase intelligence + docs
87
+ guardian extract
88
+
89
+ # AI context file only (compact ~3K token summary)
90
+ guardian generate --ai-context
91
+
92
+ # Build codebase-intelligence.json from existing snapshots
93
+ guardian intel
94
+ ```
95
+
96
+ ### Search & Context
97
+
98
+ ```bash
99
+ # Search all artifacts by keyword (models, endpoints, components, modules, tasks)
100
+ guardian search --query "session"
101
+ guardian search --query "auth" --types models,endpoints
102
+
103
+ # Render focused AI context block (stdout or append to file)
104
+ guardian context --focus "auth"
105
+ guardian context --output CLAUDE.md # injects between auto-context markers
106
+ guardian context --focus "session" --output CLAUDE.md
107
+
108
+ # Executive summary from existing snapshots
109
+ guardian summary
110
+ ```
111
+
112
+ ### Architectural Metrics
113
+
114
+ ```bash
115
+ # Compute drift metrics (D_t, K_t, delta, entropy, cycles)
116
+ guardian drift
117
+ guardian drift --baseline # save baseline for future comparison
118
+
119
+ # Verify drift stays within threshold (for CI gates)
120
+ guardian verify-drift --baseline specs-out/machine/drift.baseline.json
121
+
122
+ # Generate constraints JSON (duplicates, cycles, similar endpoints)
123
+ guardian constraints
124
+
125
+ # Structural complexity analysis for a feature area
126
+ guardian analyze-depth --query "session"
127
+ guardian analyze-depth --query "payment" --ci # exit 1 on HIGH complexity
128
+
129
+ # Diff between two snapshots
130
+ guardian diff --baseline old.yaml --current new.yaml
131
+ ```
132
+
133
+ ### Documentation
134
+
135
+ ```bash
136
+ # LLM-powered product document (uses Ollama or Anthropic)
137
+ guardian doc-generate
138
+ guardian doc-generate --update-baseline # freeze for discrepancy tracking
139
+
140
+ # HTML Javadoc-style viewer (no server needed, open in browser)
141
+ guardian doc-html
142
+
143
+ # Discrepancy report: code vs baseline (JSON + Markdown)
144
+ guardian discrepancy
145
+ ```
146
+
147
+ ### Simulation & LLM Guardrails
148
+
149
+ ```bash
150
+ # Simulate drift impact of a patch before merging
151
+ guardian simulate --patch changes.patch
152
+
153
+ # LLM-guided code generation with drift guardrails
154
+ guardian guard --task "add payment endpoint"
155
+ guardian guard --task "refactor auth" --print-context # print without calling LLM
156
+
157
+ # Generate filtered context packet for implementing a single feature
158
+ guardian feature-context --spec feature-specs/billing.yaml
159
+ ```
160
+
161
+ ### VSCode Commands (Command Palette)
162
+
163
+ | Command | Description |
164
+ |---------|-------------|
165
+ | `Guardian: Initialize Project` | Run `guardian init` on workspace |
166
+ | `Guardian: Generate AI Context` | Generate architecture-context.md |
167
+ | `Guardian: Drift Check` | Compute drift metrics |
168
+ | `Guardian: Generate Constraints` | Find duplicates, cycles, similar endpoints |
169
+ | `Guardian: Copy Constraints Prompt` | Copy LLM guardrail prompt to clipboard |
170
+ | `Guardian: Simulate Drift` | Simulate drift without a patch |
171
+ | `Guardian: Guard Patch (Simulate)` | Pick a .patch file and simulate its impact |
172
+ | `Guardian: Guarded Run` | Constraints + simulation in one step |
173
+
174
+ ## Output Structure
175
+
176
+ ```
177
+ .specs/ (or specs-out/)
178
+ ├── machine/
179
+ │ ├── architecture-context.md ← AI context (~3K tokens, injected into CLAUDE.md)
180
+ │ ├── architecture.snapshot.yaml ← full architecture snapshot
181
+ │ ├── ux.snapshot.yaml ← frontend components + pages
182
+ │ ├── codebase-intelligence.json ← unified registry for all downstream commands
183
+ │ ├── structural-intelligence.json ← per-module complexity analysis
184
+ │ ├── drift.heatmap.json ← coupling scores per module
185
+ │ ├── drift.report.json ← drift metrics
186
+ │ ├── constraints.json ← duplicates, cycles, similar endpoints
187
+ │ ├── discrepancies.json ← code vs baseline diff
188
+ │ └── docs/ ← generated markdown docs
189
+ │ ├── summary.md ← product overview with quality signals
190
+ │ ├── hld.md ← system diagrams, coupling heatmap, subsystems
191
+ │ ├── integration.md ← all API endpoints grouped by domain
192
+ │ ├── data.md ← data models and schemas
193
+ │ ├── ux.md ← pages, components, interaction maps
194
+ │ ├── diff.md ← changelog between snapshots
195
+ │ ├── runtime.md ← Docker services, background tasks
196
+ │ ├── infra.md ← manifests, scripts, Makefiles
197
+ │ ├── tests.md ← behavioral test specs
198
+ │ ├── stakeholder.md ← one-page executive view
199
+ │ └── index.md ← table of contents
200
+ ├── human/
201
+ │ ├── product-document.md ← LLM-powered comprehensive product doc
202
+ │ ├── discrepancies.md ← human-readable drift report
203
+ │ ├── start-here.md ← onboarding guide
204
+ │ ├── system-overview.md ← boundaries, risk zones
205
+ │ ├── backend-overview.md ← modules by layer
206
+ │ ├── frontend-overview.md ← page/component inventory
207
+ │ ├── data-and-flows.md ← models and cross-stack contracts
208
+ │ ├── change-guide.md ← what changed, what's risky
209
+ │ └── docs/ ← HTML viewer (open index.html in browser)
210
+ │ ├── index.html ← overview with product context from README
211
+ │ ├── architecture.html ← system diagram, workflow sequences, coupling
212
+ │ ├── api-surface.html ← all endpoints by domain
213
+ │ ├── data-models.html ← models with role badges
214
+ │ ├── quality.html ← patterns, duplicates, orphans
215
+ │ ├── frontend.html ← pages with component trees
216
+ │ ├── tasks.html ← background tasks
217
+ │ └── discrepancies.html ← code vs spec drift
218
+ ```
219
+
220
+ ## Configuration
221
+
222
+ `guardian.config.json` at project root (auto-created by `guardian init`):
223
+
224
+ ```json
225
+ {
226
+ "project": {
227
+ "backendRoot": "./backend",
228
+ "frontendRoot": "./frontend",
229
+ "description": "Short product description for generated docs"
230
+ },
231
+ "frontend": {
232
+ "routeDirs": ["app"],
233
+ "aliases": { "@": "./frontend" }
234
+ },
235
+ "python": {
236
+ "absoluteImportRoots": ["backend"]
237
+ },
238
+ "drift": {
239
+ "layers": {
240
+ "core": ["shared"],
241
+ "top": ["service-conversation"],
242
+ "isolated": ["service-auth", "service-content"]
243
+ },
244
+ "domains": {
245
+ "session": ["service-conversation", "shared"],
246
+ "auth": ["service-auth"]
247
+ }
248
+ },
249
+ "llm": {
250
+ "command": "ollama",
251
+ "args": ["run", "llama3"]
252
+ },
253
+ "docs": {
254
+ "mode": "full"
255
+ },
256
+ "ignore": {
257
+ "directories": ["venv", "node_modules", "__pycache__"],
258
+ "paths": ["backend/alembic/versions"]
259
+ }
260
+ }
261
+ ```
262
+
263
+ ## Claude Code Integration
264
+
265
+ Guardian auto-injects architecture context into `CLAUDE.md` so Claude Code reads it at session start:
266
+
267
+ ```markdown
268
+ # my-project
269
+
270
+ ## Guardian Architecture Context
271
+
272
+ <!-- guardian:auto-context -->
273
+ ## Codebase Map
274
+ **Backend:** 16 schemas · 35 endpoints · 9 modules
275
+ **Frontend:** 10 components · 8 pages
276
+
277
+ ### High-Coupling Files
278
+ - shared/policy/__init__.py (score 1.00)
279
+ - service-conversation/engine.py (score 0.40)
280
+ ...
281
+ <!-- /guardian:auto-context -->
282
+ ```
283
+
284
+ The block between `<!-- guardian:auto-context -->` markers is replaced on every save (via VSCode extension) and every commit (via pre-commit hook). Your manual content in CLAUDE.md outside the markers is never touched.
285
+
286
+ ## Key Architectural Outputs
287
+
288
+ ### Workflow Sequence Diagrams
289
+ Auto-generated Mermaid sequence diagrams for your most complex endpoints, showing the full call chain from client through handler to services and data stores.
290
+
291
+ ### System Architecture Diagram
292
+ Full system view: frontend → backend services → data stores → external APIs, with actual endpoint paths shown per service.
293
+
294
+ ### Service Communication Map
295
+ Cross-service dependency flowchart showing which modules import from which, proxy patterns, and external API calls.
296
+
297
+ ### Model Role Badges
298
+ Each data model gets an inferred role badge: API Request, API Response, Configuration, Safety Policy, Entity Profile, Content Entity, etc.
299
+
300
+ ### Subsystem Diagrams with Entity Names
301
+ Backend module diagrams show actual class names (e.g., `ConversationEngine`, `ContentRetriever`, `SessionStateMachine`) instead of generic file counts.
302
+
303
+ ## Framework Support
304
+
305
+ ### Frontend Frameworks
306
+ - **Expo Router** — auto-detected from `package.json`. Every `.tsx` in `app/` is a page (except `_layout`, `_error`). Route derived from filename.
307
+ - **Next.js** — `page.tsx` convention in `app/` directory.
308
+ - **React Router** — route definitions parsed from JSX `<Route>` elements and `createBrowserRouter()`.
309
+
310
+ ### Backend Frameworks
311
+ - **Python**: FastAPI, Django, Pydantic, SQLAlchemy
312
+ - **TypeScript/JavaScript**: Express, React, Next.js
313
+ - **Java**: Spring Boot (`@RestController`, JPA)
314
+ - **Go**: Gin routing, struct models
315
+ - **C#**: ASP.NET Core HTTP endpoints, POCO schemas
316
+
317
+ All extraction uses Tree-Sitter AST parsing — deterministic, no LLM involved.
318
+
319
+ ## Key Metrics
320
+
321
+ | Metric | Meaning |
322
+ |--------|---------|
323
+ | **D_t** | Coupling delta (lower = less entangled) |
324
+ | **K_t** | Architectural complexity |
325
+ | **Delta** | Overall drift score |
326
+ | **Coupling score** | Per-module dependency pressure (0-1) |
327
+ | **Shape fingerprint** | Change = structural refactor. Same shape + different fingerprint = additive change |
328
+
329
+ ## Automation Flow
330
+
331
+ ```
332
+ Developer writes code
333
+ ↓ (save)
334
+ VSCode extension (5s debounce)
335
+
336
+ guardian extract → .specs/
337
+ guardian generate --ai-context → .specs/
338
+ guardian context → CLAUDE.md (between markers)
339
+ Status bar: "✓ Guardian: stable · 35 ep · 8 pg"
340
+ ↓ (git commit)
341
+ Pre-commit hook: extract + context → auto-staged
342
+
343
+ Claude Code reads CLAUDE.md → fresh architecture context
344
+ ```
345
+
346
+ ## GitHub Action
347
+
348
+ ```yaml
349
+ uses: ./
350
+ with:
351
+ project-root: .
352
+ output: specs-out
353
+ ```
354
+
355
+ See [`.github/workflows/guardian-example.yml`](./.github/workflows/guardian-example.yml).
356
+
357
+ ## Development
358
+
359
+ ```bash
360
+ npm install
361
+ npm run dev -- extract . # run from source
362
+ npm run build # compile to dist/
363
+ npm run start -- extract . # run compiled
364
+ npm run typecheck # type check only
365
+ npm test # run tests
366
+ ```
@@ -0,0 +1,149 @@
1
+ import CSharp from "tree-sitter-c-sharp";
2
+ import Parser from "tree-sitter";
3
+ function text(node) {
4
+ return node ? node.text : "";
5
+ }
6
+ export const CSharpAdapter = {
7
+ name: "C# ASP.NET Core Adapter",
8
+ language: CSharp,
9
+ fileExtensions: [".cs"],
10
+ queries: {
11
+ // Endpoints: Methods with an attribute [HttpXxx("...")]
12
+ endpoints: "(method_declaration name: (identifier) @handler) @endpoint",
13
+ // Models: C# classes without Controller or Service suffixes usually.
14
+ models: "(class_declaration name: (identifier) @name) @model",
15
+ // Tests: Methods with [Fact], [Test], [TestMethod], [Theory]
16
+ tests: `
17
+ (method_declaration
18
+ (attribute_list (attribute name: (identifier) @test_attr (#match? @test_attr "^Fact$|^Test$|^TestMethod$|^Theory$")))
19
+ name: (identifier) @test_name
20
+ )
21
+ `
22
+ },
23
+ extract(file, source, root) {
24
+ const endpoints = [];
25
+ const models = [];
26
+ const components = []; // N/A
27
+ const tests = [];
28
+ const epQuery = new Parser.Query(this.language, this.queries.endpoints);
29
+ const mdQuery = new Parser.Query(this.language, this.queries.models);
30
+ // Extract C# Endpoints
31
+ const epMatches = epQuery.matches(root);
32
+ for (const match of epMatches) {
33
+ const handlerNode = match.captures.find(c => c.name === "handler")?.node;
34
+ const endpointNode = match.captures.find(c => c.name === "endpoint")?.node;
35
+ if (handlerNode && endpointNode) {
36
+ let isEndpoint = false;
37
+ let method = "ANY";
38
+ let routePath = "/";
39
+ // Scan the attributes of this method dynamically
40
+ for (let i = 0; i < endpointNode.childCount; i++) {
41
+ const child = endpointNode.child(i);
42
+ if (!child)
43
+ continue;
44
+ if (child.type === "attribute_list") {
45
+ const attrText = text(child);
46
+ if (attrText.includes("HttpGet")) {
47
+ isEndpoint = true;
48
+ method = "GET";
49
+ }
50
+ else if (attrText.includes("HttpPost")) {
51
+ isEndpoint = true;
52
+ method = "POST";
53
+ }
54
+ else if (attrText.includes("HttpPut")) {
55
+ isEndpoint = true;
56
+ method = "PUT";
57
+ }
58
+ else if (attrText.includes("HttpDelete")) {
59
+ isEndpoint = true;
60
+ method = "DELETE";
61
+ }
62
+ else if (attrText.includes("HttpPatch")) {
63
+ isEndpoint = true;
64
+ method = "PATCH";
65
+ }
66
+ // Extract path if present
67
+ const strMatch = attrText.match(/\\"([^\\"]+)\\"/);
68
+ if (strMatch)
69
+ routePath = strMatch[1];
70
+ }
71
+ }
72
+ if (isEndpoint) {
73
+ let requestSchema = null;
74
+ let responseSchema = null;
75
+ const typeNode = endpointNode.childForFieldName("type");
76
+ if (typeNode)
77
+ responseSchema = text(typeNode);
78
+ const paramsNode = endpointNode.childForFieldName("parameters");
79
+ if (paramsNode) {
80
+ for (let i = 0; i < paramsNode.childCount; i++) {
81
+ const pChild = paramsNode.child(i);
82
+ if (!pChild)
83
+ continue;
84
+ if (text(pChild).includes("[FromBody]")) {
85
+ const typeChild = pChild.childForFieldName("type");
86
+ if (typeChild)
87
+ requestSchema = text(typeChild);
88
+ }
89
+ else if (pChild.type === "parameter") {
90
+ // Default ASP.NET Core Model Binding implicit body binding
91
+ const typeChild = pChild.childForFieldName("type");
92
+ if (typeChild && !["string", "int", "long", "boolean"].includes(text(typeChild).toLowerCase())) {
93
+ if (!requestSchema)
94
+ requestSchema = text(typeChild);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ endpoints.push({
100
+ method,
101
+ path: routePath,
102
+ handler: text(handlerNode),
103
+ file,
104
+ request_schema: requestSchema,
105
+ response_schema: responseSchema,
106
+ service_calls: []
107
+ });
108
+ }
109
+ }
110
+ }
111
+ // Extract C# Models
112
+ const mdMatches = mdQuery.matches(root);
113
+ for (const match of mdMatches) {
114
+ const nameNode = match.captures.find(c => c.name === "name")?.node;
115
+ const modelNode = match.captures.find(c => c.name === "model")?.node;
116
+ if (nameNode && modelNode) {
117
+ const name = text(nameNode);
118
+ // Skip controllers, services, interfaces (which start with I usually), and unmapped classes
119
+ if (name.endsWith("Controller") || name.endsWith("Service") || name.startsWith("I")) {
120
+ continue;
121
+ }
122
+ const fields = [];
123
+ const bodyNode = modelNode.childForFieldName("body");
124
+ if (bodyNode) {
125
+ for (let i = 0; i < bodyNode.childCount; i++) {
126
+ const child = bodyNode.child(i);
127
+ if (!child)
128
+ continue;
129
+ if (child.type === "property_declaration") {
130
+ const fieldNameNode = child.childForFieldName("name");
131
+ if (fieldNameNode)
132
+ fields.push(text(fieldNameNode));
133
+ }
134
+ }
135
+ }
136
+ if (fields.length > 0) {
137
+ models.push({
138
+ name,
139
+ file,
140
+ framework: "csharp-poco",
141
+ fields,
142
+ relationships: []
143
+ });
144
+ }
145
+ }
146
+ }
147
+ return { endpoints, models, components, tests };
148
+ }
149
+ };
@@ -0,0 +1,96 @@
1
+ import { createRequire } from "node:module";
2
+ import Parser from "tree-sitter";
3
+ const require = createRequire(import.meta.url);
4
+ const Go = require("tree-sitter-go");
5
+ function text(node) {
6
+ return node ? node.text : "";
7
+ }
8
+ export const GoAdapter = {
9
+ name: "Go Gin Adapter",
10
+ language: Go,
11
+ fileExtensions: [".go"],
12
+ queries: {
13
+ // Endpoints: r.GET("/route", handler)
14
+ endpoints: "(call_expression function: (selector_expression field: (field_identifier) @method) arguments: (argument_list (interpreted_string_literal) @path)) @endpoint",
15
+ // Models: type User struct { ... }
16
+ models: "(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @model",
17
+ // Tests: func TestXxx(t *testing.T)
18
+ tests: `(function_declaration name: (identifier) @test_name (#match? @test_name "^Test"))`
19
+ },
20
+ extract(file, source, root) {
21
+ const endpoints = [];
22
+ const models = [];
23
+ const components = [];
24
+ const tests = [];
25
+ const epQuery = new Parser.Query(this.language, this.queries.endpoints);
26
+ const mdQuery = new Parser.Query(this.language, this.queries.models);
27
+ // Extract Gin Endpoints
28
+ const epMatches = epQuery.matches(root);
29
+ for (const match of epMatches) {
30
+ const methodNode = match.captures.find(c => c.name === "method")?.node;
31
+ const pathNode = match.captures.find(c => c.name === "path")?.node;
32
+ const endpointNode = match.captures.find(c => c.name === "endpoint")?.node;
33
+ if (methodNode && pathNode && endpointNode) {
34
+ let routePath = text(pathNode).replace(/\\"/g, "");
35
+ const method = text(methodNode).toUpperCase();
36
+ // Filter out non-http methods dynamically
37
+ if (!["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "ANY"].includes(method))
38
+ continue;
39
+ // Find handler: It's the last argument in the call.
40
+ let handler = "anonymous";
41
+ const argsNode = endpointNode.childForFieldName("arguments");
42
+ if (argsNode && argsNode.childCount > 0) {
43
+ const lastChild = argsNode.child(argsNode.childCount - 2); // -1 is ')', -2 is the last arg
44
+ if (lastChild)
45
+ handler = text(lastChild);
46
+ }
47
+ endpoints.push({
48
+ method,
49
+ path: routePath,
50
+ handler,
51
+ file,
52
+ request_schema: null,
53
+ response_schema: null,
54
+ service_calls: []
55
+ });
56
+ }
57
+ }
58
+ // Extract Struct Models
59
+ const mdMatches = mdQuery.matches(root);
60
+ for (const match of mdMatches) {
61
+ const nameNode = match.captures.find(c => c.name === "name")?.node;
62
+ const modelNode = match.captures.find(c => c.name === "model")?.node;
63
+ if (nameNode && modelNode) {
64
+ const name = text(nameNode);
65
+ const fields = [];
66
+ const typeSpec = modelNode.child(0);
67
+ if (typeSpec) {
68
+ const structType = typeSpec.childForFieldName("type");
69
+ if (structType && structType.type === "struct_type") {
70
+ const fieldList = structType.childForFieldName("field_declaration_list");
71
+ if (fieldList) {
72
+ for (let i = 0; i < fieldList.childCount; i++) {
73
+ const child = fieldList.child(i);
74
+ if (!child)
75
+ continue;
76
+ if (child.type === "field_declaration") {
77
+ const fieldNameNode = child.childForFieldName("name");
78
+ if (fieldNameNode)
79
+ fields.push(text(fieldNameNode));
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ models.push({
86
+ name,
87
+ file,
88
+ framework: "go-struct",
89
+ fields,
90
+ relationships: []
91
+ });
92
+ }
93
+ }
94
+ return { endpoints, models, components, tests };
95
+ }
96
+ };
@@ -0,0 +1,16 @@
1
+ import { PythonAdapter } from "./python-adapter.js";
2
+ import { TypeScriptAdapter } from "./typescript-adapter.js";
3
+ import { JavaAdapter } from "./java-adapter.js";
4
+ import { GoAdapter } from "./go-adapter.js";
5
+ import { CSharpAdapter } from "./csharp-adapter.js";
6
+ import { runAdapter } from "./runner.js";
7
+ export { PythonAdapter, TypeScriptAdapter, JavaAdapter, GoAdapter, CSharpAdapter, runAdapter };
8
+ export const ADAPTERS = [PythonAdapter, TypeScriptAdapter, JavaAdapter, GoAdapter, CSharpAdapter];
9
+ export function getAdapterForFile(file) {
10
+ for (const adapter of ADAPTERS) {
11
+ if (adapter.fileExtensions.some(ext => file.endsWith(ext))) {
12
+ return adapter;
13
+ }
14
+ }
15
+ return null;
16
+ }