codesight 1.0.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.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # codesight
2
+
3
+ See your codebase clearly. One command gives your AI assistant complete project understanding.
4
+
5
+ ```bash
6
+ npx codesight
7
+ ```
8
+
9
+ **Works with Claude Code, Cursor, GitHub Copilot, OpenAI Codex, Windsurf, Cline, and any AI coding tool.**
10
+
11
+ ## The Problem
12
+
13
+ Every AI coding conversation starts the same way: your assistant burns thousands of tokens exploring files, grepping for patterns, and reading configs just to understand your project. On a 65-route Hono API with 18 Drizzle models, that costs **~68,000 tokens per conversation** in exploration alone.
14
+
15
+ codesight pre-generates that understanding into ~4,000 tokens of structured context. **Your AI starts every conversation already knowing your routes, schema, components, dependencies, and architecture.**
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ npx codesight # Scan and generate context
21
+ npx codesight --init # Also generate CLAUDE.md, .cursorrules, codex.md, AGENTS.md
22
+ npx codesight --open # Also open interactive HTML report in browser
23
+ ```
24
+
25
+ ## What It Generates
26
+
27
+ ```
28
+ .codesight/
29
+ CODESIGHT.md # Combined AI context map (point your assistant here)
30
+ routes.md # API routes with methods, params, request/response types, tags
31
+ schema.md # Database models with fields, types, PKs, FKs, relations
32
+ components.md # UI components with props (shadcn/radix filtered out)
33
+ libs.md # Library exports with function signatures
34
+ config.md # Env vars (required vs default), config files, key deps
35
+ middleware.md # Auth, rate limiting, CORS, validation, logging
36
+ graph.md # Dependency graph: most-imported files + import map
37
+ report.html # Interactive visual report (with --html or --open)
38
+ ```
39
+
40
+ ## Real Output Example
41
+
42
+ From a production SaaS monorepo (Hono + Drizzle + React):
43
+
44
+ ```markdown
45
+ # savemrr — AI Context Map
46
+
47
+ > **Stack:** hono | drizzle | react | typescript
48
+ > **Monorepo:** @savemrr/api, @savemrr/dashboard, @savemrr/widget, @savemrr/shared
49
+
50
+ > 65 routes | 18 models | 16 components | 36 lib files | 22 env vars | 5 middleware | 53 import links
51
+ > **Token savings:** this file is ~4,041 tokens. Without it, AI exploration would cost ~68,640 tokens.
52
+ > **Saves ~64,599 tokens per conversation.**
53
+
54
+ ---
55
+
56
+ # Routes
57
+
58
+ - `POST` `/magic-link` [auth, db, cache, email]
59
+ - `POST` `/verify` [auth, db, cache, email]
60
+ - `GET` `/me` [auth, db, cache, email]
61
+ - `GET` `/overview` [auth, db, queue, payment, ai]
62
+ - `GET` `/shield` [auth, db, queue, payment, ai]
63
+ - `GET` `/rescue` [auth, db, queue, payment, ai]
64
+ - `POST` `/rescue/:id/send-now` params(id) [auth, db, queue, payment, ai]
65
+ ...
66
+
67
+ # Schema
68
+
69
+ ### users
70
+ - id: uuid (pk, default)
71
+ - email: text (unique, required)
72
+ - tier: text (required, default)
73
+ - polarSubId: text (fk)
74
+ - settings: jsonb (required, default)
75
+
76
+ ### stripe_connections
77
+ - id: uuid (pk, default)
78
+ - userId: uuid (fk)
79
+ ...
80
+
81
+ # Dependency Graph
82
+
83
+ ## Most Imported Files (change these carefully)
84
+ - `packages/shared/src/index.ts` — imported by **12** files
85
+ - `apps/api/src/lib/db.ts` — imported by **8** files
86
+ - `apps/api/src/lib/auth.ts` — imported by **6** files
87
+ ```
88
+
89
+ ## What It Detects
90
+
91
+ | Category | Supported |
92
+ |---|---|
93
+ | **Routes** | Hono, Express, Fastify, Next.js (App + Pages), Koa, FastAPI, Flask, Django, Go (net/http, Gin, Fiber) |
94
+ | **Schema** | Drizzle, Prisma, TypeORM, SQLAlchemy |
95
+ | **Components** | React, Vue, Svelte (filters out shadcn/radix primitives) |
96
+ | **Libraries** | TypeScript/JavaScript, Python, Go exports with function signatures |
97
+ | **Config** | Environment variables (required vs defaults), config files, notable dependencies |
98
+ | **Middleware** | Auth, rate limiting, CORS, validation, logging, error handlers |
99
+ | **Dependencies** | Import graph with hot file detection (most imported = highest blast radius) |
100
+ | **Contracts** | URL params, request types, response types extracted from route handlers |
101
+ | **Monorepos** | pnpm, npm, yarn workspaces with cross-workspace detection |
102
+ | **Languages** | TypeScript, JavaScript, Python, Go |
103
+
104
+ ## Auto-Generate AI Config Files
105
+
106
+ ```bash
107
+ npx codesight --init
108
+ ```
109
+
110
+ Generates instruction files for every major AI coding tool:
111
+
112
+ | File | Tool |
113
+ |---|---|
114
+ | `CLAUDE.md` | Claude Code |
115
+ | `.cursorrules` | Cursor |
116
+ | `.github/copilot-instructions.md` | GitHub Copilot |
117
+ | `codex.md` | OpenAI Codex CLI |
118
+ | `AGENTS.md` | OpenAI Codex |
119
+
120
+ Each file includes your project's stack, key architecture facts, hot files, and required env vars so your AI assistant knows the project from the first message.
121
+
122
+ ## Interactive HTML Report
123
+
124
+ ```bash
125
+ npx codesight --open
126
+ ```
127
+
128
+ Generates a visual dashboard with:
129
+ - Token savings hero metric
130
+ - Route table with methods, contracts, and tags
131
+ - Schema cards with fields and relations
132
+ - Dependency hot files with impact bars
133
+ - Environment variables (required vs defaults)
134
+ - Full middleware map
135
+
136
+ Screenshots of this report are what go viral on Twitter.
137
+
138
+ ## MCP Server
139
+
140
+ ```bash
141
+ npx codesight --mcp
142
+ ```
143
+
144
+ Runs as a Model Context Protocol server that Claude Code and Cursor can connect to. Add to your MCP config:
145
+
146
+ ```json
147
+ {
148
+ "mcpServers": {
149
+ "codesight": {
150
+ "command": "npx",
151
+ "args": ["codesight", "--mcp"]
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ Your AI assistant can then call the `codesight_scan` tool to get full project context on demand.
158
+
159
+ ## Watch Mode
160
+
161
+ ```bash
162
+ npx codesight --watch
163
+ ```
164
+
165
+ Re-scans automatically when files change. Keeps context fresh during development.
166
+
167
+ ## Git Pre-Commit Hook
168
+
169
+ ```bash
170
+ npx codesight --hook
171
+ ```
172
+
173
+ Installs a git pre-commit hook that regenerates context on every commit. Context stays up to date automatically.
174
+
175
+ ## All Options
176
+
177
+ ```bash
178
+ npx codesight # Scan current directory
179
+ npx codesight ./my-project # Scan specific directory
180
+ npx codesight --init # Generate AI config files
181
+ npx codesight --open # Open interactive HTML report
182
+ npx codesight --html # Generate HTML report (no open)
183
+ npx codesight --mcp # Start MCP server
184
+ npx codesight --watch # Watch mode
185
+ npx codesight --hook # Install git pre-commit hook
186
+ npx codesight --json # Output JSON
187
+ npx codesight -o .ai-context # Custom output directory
188
+ npx codesight -d 5 # Limit directory depth
189
+ ```
190
+
191
+ ## Why Not Repomix?
192
+
193
+ Repomix dumps your entire codebase into one file. codesight maps your architecture.
194
+
195
+ | | Repomix | codesight |
196
+ |---|---|---|
197
+ | **Approach** | Raw code dump | Structured intelligence |
198
+ | **Output** | One giant text blob | Semantic context files |
199
+ | **AI gets** | "Here is all the code" | "Here is the architecture" |
200
+ | **Token cost** | Huge (entire codebase) | Tiny (~4K tokens for full map) |
201
+ | **Route discovery** | Read every file | Instant from routes.md |
202
+ | **Schema understanding** | Read every migration | Instant from schema.md |
203
+ | **Change blast radius** | Unknown | Visible in dependency graph |
204
+ | **AI config generation** | No | CLAUDE.md, .cursorrules, codex.md, AGENTS.md |
205
+ | **Runtime deps** | 26 | **Zero** |
206
+
207
+ ## Zero Dependencies
208
+
209
+ codesight has zero runtime dependencies. Just Node.js built-ins. Fast, portable, no supply chain risk.
210
+
211
+ ## License
212
+
213
+ MIT
@@ -0,0 +1,2 @@
1
+ import type { ComponentInfo, ProjectInfo } from "../types.js";
2
+ export declare function detectComponents(files: string[], project: ProjectInfo): Promise<ComponentInfo[]>;
@@ -0,0 +1,237 @@
1
+ import { relative, basename, extname } from "node:path";
2
+ import { readFileSafe } from "../scanner.js";
3
+ // shadcn/ui + radix primitives to filter out
4
+ const UI_PRIMITIVES = new Set([
5
+ "accordion",
6
+ "alert",
7
+ "alert-dialog",
8
+ "aspect-ratio",
9
+ "avatar",
10
+ "badge",
11
+ "breadcrumb",
12
+ "button",
13
+ "calendar",
14
+ "card",
15
+ "carousel",
16
+ "chart",
17
+ "checkbox",
18
+ "collapsible",
19
+ "command",
20
+ "context-menu",
21
+ "data-table",
22
+ "date-picker",
23
+ "dialog",
24
+ "drawer",
25
+ "dropdown-menu",
26
+ "form",
27
+ "hover-card",
28
+ "input",
29
+ "input-otp",
30
+ "label",
31
+ "menubar",
32
+ "navigation-menu",
33
+ "pagination",
34
+ "popover",
35
+ "progress",
36
+ "radio-group",
37
+ "resizable",
38
+ "scroll-area",
39
+ "select",
40
+ "separator",
41
+ "sheet",
42
+ "sidebar",
43
+ "skeleton",
44
+ "slider",
45
+ "sonner",
46
+ "switch",
47
+ "table",
48
+ "tabs",
49
+ "textarea",
50
+ "toast",
51
+ "toaster",
52
+ "toggle",
53
+ "toggle-group",
54
+ "tooltip",
55
+ ]);
56
+ function isUIPrimitive(filePath) {
57
+ const name = basename(filePath, extname(filePath)).toLowerCase();
58
+ return (UI_PRIMITIVES.has(name) ||
59
+ filePath.includes("/ui/") ||
60
+ filePath.includes("/components/ui/") ||
61
+ filePath.includes("@radix-ui") ||
62
+ filePath.includes("@shadcn"));
63
+ }
64
+ export async function detectComponents(files, project) {
65
+ switch (project.componentFramework) {
66
+ case "react":
67
+ return detectReactComponents(files, project);
68
+ case "vue":
69
+ return detectVueComponents(files, project);
70
+ case "svelte":
71
+ return detectSvelteComponents(files, project);
72
+ default:
73
+ return [];
74
+ }
75
+ }
76
+ // --- React ---
77
+ async function detectReactComponents(files, project) {
78
+ const componentFiles = files.filter((f) => (f.endsWith(".tsx") || f.endsWith(".jsx")) &&
79
+ !f.includes("node_modules") &&
80
+ !f.endsWith(".test.tsx") &&
81
+ !f.endsWith(".test.jsx") &&
82
+ !f.endsWith(".spec.tsx") &&
83
+ !f.endsWith(".spec.jsx") &&
84
+ !f.endsWith(".stories.tsx") &&
85
+ !f.endsWith(".stories.jsx"));
86
+ const components = [];
87
+ for (const file of componentFiles) {
88
+ if (isUIPrimitive(file))
89
+ continue;
90
+ const content = await readFileSafe(file);
91
+ if (!content)
92
+ continue;
93
+ const rel = relative(project.root, file);
94
+ // Detect component name from export
95
+ let name = "";
96
+ // export default function ComponentName
97
+ const defaultFnMatch = content.match(/export\s+default\s+function\s+(\w+)/);
98
+ if (defaultFnMatch) {
99
+ name = defaultFnMatch[1];
100
+ }
101
+ // export function ComponentName (starts with uppercase)
102
+ if (!name) {
103
+ const namedFnMatch = content.match(/export\s+(?:async\s+)?function\s+([A-Z]\w+)/);
104
+ if (namedFnMatch)
105
+ name = namedFnMatch[1];
106
+ }
107
+ // const ComponentName = ... (with export)
108
+ if (!name) {
109
+ const constMatch = content.match(/export\s+const\s+([A-Z]\w+)\s*(?::\s*\w+)?\s*=/);
110
+ if (constMatch)
111
+ name = constMatch[1];
112
+ }
113
+ // Fallback: any function starting with uppercase that returns JSX
114
+ if (!name) {
115
+ const anyComponent = content.match(/(?:function|const)\s+([A-Z]\w+)/);
116
+ if (anyComponent && (content.includes("<") || content.includes("jsx"))) {
117
+ name = anyComponent[1];
118
+ }
119
+ }
120
+ if (!name)
121
+ continue;
122
+ // Detect props
123
+ const props = [];
124
+ // interface/type Props { ... }
125
+ const propsPattern = /(?:interface|type)\s+(?:\w*Props\w*)\s*(?:=\s*)?\{([^}]*)}/;
126
+ const propsMatch = content.match(propsPattern);
127
+ if (propsMatch) {
128
+ const propsBody = propsMatch[1];
129
+ for (const line of propsBody.split("\n")) {
130
+ const propMatch = line.match(/^\s*(\w+)\s*[?]?\s*:/);
131
+ if (propMatch && propMatch[1] !== "children") {
132
+ props.push(propMatch[1]);
133
+ }
134
+ }
135
+ }
136
+ // Destructured props: function Component({ prop1, prop2 }: Props)
137
+ if (props.length === 0) {
138
+ const destructuredMatch = content.match(new RegExp(`function\\s+${name}\\s*\\(\\s*\\{([^}]*)\\}`));
139
+ if (destructuredMatch) {
140
+ for (const prop of destructuredMatch[1].split(",")) {
141
+ const trimmed = prop.trim().split(/[=:]/)[0].trim();
142
+ if (trimmed && trimmed !== "children" && !trimmed.startsWith("...")) {
143
+ props.push(trimmed);
144
+ }
145
+ }
146
+ }
147
+ }
148
+ const isClient = content.slice(0, 50).includes("use client");
149
+ const isServer = content.slice(0, 50).includes("use server");
150
+ components.push({
151
+ name,
152
+ file: rel,
153
+ props: props.slice(0, 10), // limit to 10 props
154
+ isClient,
155
+ isServer,
156
+ });
157
+ }
158
+ return components;
159
+ }
160
+ // --- Vue ---
161
+ async function detectVueComponents(files, project) {
162
+ const vueFiles = files.filter((f) => f.endsWith(".vue") && !f.includes("node_modules"));
163
+ const components = [];
164
+ for (const file of vueFiles) {
165
+ if (isUIPrimitive(file))
166
+ continue;
167
+ const content = await readFileSafe(file);
168
+ const rel = relative(project.root, file);
169
+ const name = basename(file, ".vue");
170
+ const props = [];
171
+ // defineProps<{ ... }>() or defineProps({ ... })
172
+ const definePropsMatch = content.match(/defineProps\s*[<(]\s*\{([^}]*)\}/);
173
+ if (definePropsMatch) {
174
+ for (const line of definePropsMatch[1].split("\n")) {
175
+ const propMatch = line.match(/^\s*(\w+)\s*[?]?\s*:/);
176
+ if (propMatch)
177
+ props.push(propMatch[1]);
178
+ }
179
+ }
180
+ // props: { ... } in Options API
181
+ if (props.length === 0) {
182
+ const optionsPropsMatch = content.match(/props\s*:\s*\{([^}]*)\}/);
183
+ if (optionsPropsMatch) {
184
+ for (const line of optionsPropsMatch[1].split("\n")) {
185
+ const propMatch = line.match(/^\s*(\w+)\s*[?]?\s*:/);
186
+ if (propMatch)
187
+ props.push(propMatch[1]);
188
+ }
189
+ }
190
+ }
191
+ components.push({
192
+ name,
193
+ file: rel,
194
+ props: props.slice(0, 10),
195
+ isClient: true,
196
+ isServer: false,
197
+ });
198
+ }
199
+ return components;
200
+ }
201
+ // --- Svelte ---
202
+ async function detectSvelteComponents(files, project) {
203
+ const svelteFiles = files.filter((f) => f.endsWith(".svelte") && !f.includes("node_modules"));
204
+ const components = [];
205
+ for (const file of svelteFiles) {
206
+ if (isUIPrimitive(file))
207
+ continue;
208
+ const content = await readFileSafe(file);
209
+ const rel = relative(project.root, file);
210
+ const name = basename(file, ".svelte");
211
+ const props = [];
212
+ // export let propName (Svelte 4)
213
+ const exportLetPattern = /export\s+let\s+(\w+)/g;
214
+ let match;
215
+ while ((match = exportLetPattern.exec(content)) !== null) {
216
+ props.push(match[1]);
217
+ }
218
+ // $props() (Svelte 5)
219
+ const propsMatch = content.match(/let\s+\{([^}]*)\}\s*=\s*\$props\(\)/);
220
+ if (propsMatch) {
221
+ for (const prop of propsMatch[1].split(",")) {
222
+ const trimmed = prop.trim().split(/[=:]/)[0].trim();
223
+ if (trimmed && !trimmed.startsWith("...")) {
224
+ props.push(trimmed);
225
+ }
226
+ }
227
+ }
228
+ components.push({
229
+ name,
230
+ file: rel,
231
+ props: props.slice(0, 10),
232
+ isClient: true,
233
+ isServer: false,
234
+ });
235
+ }
236
+ return components;
237
+ }
@@ -0,0 +1,2 @@
1
+ import type { ConfigInfo, ProjectInfo } from "../types.js";
2
+ export declare function detectConfig(files: string[], project: ProjectInfo): Promise<ConfigInfo>;
@@ -0,0 +1,142 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { relative, join, basename } from "node:path";
3
+ import { readFileSafe } from "../scanner.js";
4
+ const CONFIG_FILES = [
5
+ "tsconfig.json",
6
+ "next.config.js",
7
+ "next.config.mjs",
8
+ "next.config.ts",
9
+ "vite.config.ts",
10
+ "vite.config.js",
11
+ "tailwind.config.ts",
12
+ "tailwind.config.js",
13
+ "drizzle.config.ts",
14
+ "wrangler.toml",
15
+ "docker-compose.yml",
16
+ "docker-compose.yaml",
17
+ "Dockerfile",
18
+ ".env.example",
19
+ "pyproject.toml",
20
+ "go.mod",
21
+ "Cargo.toml",
22
+ "railway.json",
23
+ "vercel.json",
24
+ "fly.toml",
25
+ "render.yaml",
26
+ ];
27
+ export async function detectConfig(files, project) {
28
+ // Find config files
29
+ const configFiles = files
30
+ .filter((f) => {
31
+ const name = basename(f);
32
+ return CONFIG_FILES.includes(name);
33
+ })
34
+ .map((f) => relative(project.root, f));
35
+ // Also check root for config files that might not have code extensions
36
+ for (const cf of CONFIG_FILES) {
37
+ const content = await readFileSafe(join(project.root, cf));
38
+ if (content) {
39
+ const rel = cf;
40
+ if (!configFiles.includes(rel))
41
+ configFiles.push(rel);
42
+ }
43
+ }
44
+ // Detect env vars
45
+ const envVars = await detectEnvVars(files, project);
46
+ // Detect dependencies
47
+ let dependencies = {};
48
+ let devDependencies = {};
49
+ try {
50
+ const pkg = JSON.parse(await readFile(join(project.root, "package.json"), "utf-8"));
51
+ dependencies = pkg.dependencies || {};
52
+ devDependencies = pkg.devDependencies || {};
53
+ }
54
+ catch { }
55
+ return {
56
+ envVars,
57
+ configFiles: configFiles.sort(),
58
+ dependencies,
59
+ devDependencies,
60
+ };
61
+ }
62
+ async function detectEnvVars(files, project) {
63
+ const envMap = new Map();
64
+ // Parse .env.example and .env files for declarations
65
+ const envFiles = files.filter((f) => basename(f) === ".env" ||
66
+ basename(f) === ".env.example" ||
67
+ basename(f) === ".env.local");
68
+ for (const file of envFiles) {
69
+ const content = await readFileSafe(file);
70
+ for (const line of content.split("\n")) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed || trimmed.startsWith("#"))
73
+ continue;
74
+ const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)\s*=/);
75
+ if (match) {
76
+ const name = match[1];
77
+ const hasDefault = trimmed.includes("=") && trimmed.split("=")[1].trim().length > 0;
78
+ envMap.set(name, {
79
+ name,
80
+ source: relative(project.root, file),
81
+ hasDefault,
82
+ });
83
+ }
84
+ }
85
+ }
86
+ // Scan code for process.env.VAR_NAME or os.environ["VAR_NAME"] or os.Getenv("VAR_NAME")
87
+ const codeFiles = files.filter((f) => f.match(/\.(ts|js|tsx|jsx|mjs|cjs|py|go)$/) &&
88
+ !f.includes("node_modules"));
89
+ for (const file of codeFiles) {
90
+ const content = await readFileSafe(file);
91
+ const rel = relative(project.root, file);
92
+ // process.env.VAR_NAME or process.env["VAR_NAME"]
93
+ const nodeEnvPattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
94
+ let match;
95
+ while ((match = nodeEnvPattern.exec(content)) !== null) {
96
+ const name = match[1];
97
+ if (!envMap.has(name)) {
98
+ envMap.set(name, { name, source: rel, hasDefault: false });
99
+ }
100
+ }
101
+ const nodeEnvBracket = /process\.env\[['"]([A-Z_][A-Z0-9_]*)['"]\]/g;
102
+ while ((match = nodeEnvBracket.exec(content)) !== null) {
103
+ const name = match[1];
104
+ if (!envMap.has(name)) {
105
+ envMap.set(name, { name, source: rel, hasDefault: false });
106
+ }
107
+ }
108
+ // Bun.env.VAR_NAME
109
+ const bunEnvPattern = /Bun\.env\.([A-Z_][A-Z0-9_]*)/g;
110
+ while ((match = bunEnvPattern.exec(content)) !== null) {
111
+ const name = match[1];
112
+ if (!envMap.has(name)) {
113
+ envMap.set(name, { name, source: rel, hasDefault: false });
114
+ }
115
+ }
116
+ // import.meta.env.VITE_VAR_NAME
117
+ const viteEnvPattern = /import\.meta\.env\.([A-Z_][A-Z0-9_]*)/g;
118
+ while ((match = viteEnvPattern.exec(content)) !== null) {
119
+ const name = match[1];
120
+ if (!envMap.has(name)) {
121
+ envMap.set(name, { name, source: rel, hasDefault: false });
122
+ }
123
+ }
124
+ // Python: os.environ["VAR"] or os.environ.get("VAR") or os.getenv("VAR")
125
+ const pyEnvPattern = /os\.(?:environ\[['"]|environ\.get\s*\(['"]|getenv\s*\(['"])([A-Z_][A-Z0-9_]*)['"]/g;
126
+ while ((match = pyEnvPattern.exec(content)) !== null) {
127
+ const name = match[1];
128
+ if (!envMap.has(name)) {
129
+ envMap.set(name, { name, source: rel, hasDefault: false });
130
+ }
131
+ }
132
+ // Go: os.Getenv("VAR")
133
+ const goEnvPattern = /os\.Getenv\(["']([A-Z_][A-Z0-9_]*)["']\)/g;
134
+ while ((match = goEnvPattern.exec(content)) !== null) {
135
+ const name = match[1];
136
+ if (!envMap.has(name)) {
137
+ envMap.set(name, { name, source: rel, hasDefault: false });
138
+ }
139
+ }
140
+ }
141
+ return Array.from(envMap.values()).sort((a, b) => a.name.localeCompare(b.name));
142
+ }
@@ -0,0 +1,6 @@
1
+ import type { RouteInfo, ProjectInfo } from "../types.js";
2
+ /**
3
+ * Enhances route info with request/response type information
4
+ * by scanning the route handler files for type annotations
5
+ */
6
+ export declare function enrichRouteContracts(routes: RouteInfo[], project: ProjectInfo): Promise<RouteInfo[]>;