@dastbal/nestjs-ai-agent 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.
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AgentDB = void 0;
40
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
41
+ const path = __importStar(require("path"));
42
+ const fs = __importStar(require("fs"));
43
+ /**
44
+ * Singleton Database Manager.
45
+ * Handles the connection to the local SQLite instance used for caching and state management.
46
+ */
47
+ class AgentDB {
48
+ /**
49
+ * Private constructor to enforce Singleton pattern.
50
+ */
51
+ constructor() { }
52
+ /**
53
+ * Retrieves the active database connection.
54
+ * If it doesn't exist, it initializes the DB file and the schema.
55
+ * * @returns {Database.Database} The SQLite connection instance.
56
+ */
57
+ static getInstance() {
58
+ if (!this.instance) {
59
+ const rootDir = process.cwd();
60
+ const dbDir = path.join(rootDir, '.agent'); // Hidden folder in project root
61
+ const dbPath = path.join(dbDir, 'memory.db');
62
+ // Ensure directory exists
63
+ if (!fs.existsSync(dbDir)) {
64
+ fs.mkdirSync(dbDir, { recursive: true });
65
+ }
66
+ console.log(`💾 Connecting to Local State DB: ${dbPath}`);
67
+ this.instance = new better_sqlite3_1.default(dbPath);
68
+ // OPTIMIZATION: Write-Ahead Logging makes writing faster and safer
69
+ this.instance.pragma('journal_mode = WAL');
70
+ this.initSchema();
71
+ }
72
+ return this.instance;
73
+ }
74
+ /**
75
+ * Initializes the database tables based on our architectural plan.
76
+ * 1. file_registry: Tracks file hashes and cached skeletons.
77
+ */
78
+ static initSchema() {
79
+ const db = this.instance;
80
+ db.prepare(`
81
+ CREATE TABLE IF NOT EXISTS file_registry (
82
+ path TEXT PRIMARY KEY, -- Absolute or relative path (Unique ID)
83
+ hash TEXT NOT NULL, -- MD5 checksum of the full content
84
+ last_indexed INTEGER NOT NULL, -- Timestamp (Date.now())
85
+ skeleton_signature TEXT -- JSON String of the file structure (Class/Methods signatures)
86
+ )
87
+ `).run();
88
+ // 2. Dependency Graph (Knowledge Graph)
89
+ // Maps how files relate to each other (imports, inheritance).
90
+ db.prepare(`
91
+ CREATE TABLE IF NOT EXISTS dependency_graph (
92
+ source TEXT NOT NULL,
93
+ target TEXT NOT NULL,
94
+ relation TEXT NOT NULL,
95
+ PRIMARY KEY (source, target),
96
+ FOREIGN KEY(source) REFERENCES file_registry(path) ON DELETE CASCADE
97
+ )
98
+ `).run();
99
+ // 3. Code Chunks (Vector Store)
100
+ // Stores the actual code fragments and their vector embeddings.
101
+ // 'vector_json' stores the float array as a JSON string for simplicity in SQLite.
102
+ db.prepare(`
103
+ CREATE TABLE IF NOT EXISTS code_chunks (
104
+ id TEXT PRIMARY KEY, -- UUID
105
+ file_path TEXT NOT NULL, -- Parent File
106
+ chunk_type TEXT NOT NULL, -- 'method' | 'file' | 'class'
107
+ content TEXT NOT NULL, -- The actual code text
108
+ vector_json TEXT, -- The Embedding [0.1, -0.5, ...]
109
+ metadata TEXT, -- JSON extra info (decorators, lines)
110
+ FOREIGN KEY(file_path) REFERENCES file_registry(path) ON DELETE CASCADE
111
+ )
112
+ `).run();
113
+ // Create indexes for faster retrieval
114
+ db.prepare(`CREATE INDEX IF NOT EXISTS idx_graph_source ON dependency_graph(source)`).run();
115
+ db.prepare(`CREATE INDEX IF NOT EXISTS idx_chunks_file ON code_chunks(file_path)`).run();
116
+ }
117
+ }
118
+ exports.AgentDB = AgentDB;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Represents the database row structure for a file.
3
+ */
4
+ export interface FileRecord {
5
+ path: string;
6
+ hash: string;
7
+ last_indexed: number;
8
+ skeleton_signature: string | null;
9
+ }
10
+ /**
11
+ * Manages the state of the codebase files.
12
+ * Responsible for detecting changes (hashing) and caching skeletons.
13
+ */
14
+ export declare class FileRegistry {
15
+ private db;
16
+ /**
17
+ * Checks if a file has changed since the last index.
18
+ * * @param filePath - Relative path of the file (e.g., 'src/users.service.ts')
19
+ * @returns {boolean} True if the file is new or modified; False if cached.
20
+ */
21
+ isFileChanged(filePath: string): boolean;
22
+ /**
23
+ * Updates the registry with the new file state.
24
+ * This should be called AFTER the vector indexing and skeleton generation is done.
25
+ * * @param filePath - The path of the file
26
+ * @param skeleton - The JSON string representing the AST Skeleton (Class/Method signatures)
27
+ */
28
+ updateFile(filePath: string, skeleton: object | null): void;
29
+ /**
30
+ * Retrieves the cached skeleton for a file.
31
+ * Used by the LLM Provider to build context without reading the full disk.
32
+ */
33
+ getSkeleton(filePath: string): object | null;
34
+ /**
35
+ * Helper: Generates MD5 hash of string content.
36
+ */
37
+ private computeHash;
38
+ }
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileRegistry = void 0;
37
+ const crypto = __importStar(require("crypto"));
38
+ const fs = __importStar(require("fs"));
39
+ const db_1 = require("./db");
40
+ /**
41
+ * Manages the state of the codebase files.
42
+ * Responsible for detecting changes (hashing) and caching skeletons.
43
+ */
44
+ class FileRegistry {
45
+ constructor() {
46
+ this.db = db_1.AgentDB.getInstance();
47
+ }
48
+ /**
49
+ * Checks if a file has changed since the last index.
50
+ * * @param filePath - Relative path of the file (e.g., 'src/users.service.ts')
51
+ * @returns {boolean} True if the file is new or modified; False if cached.
52
+ */
53
+ isFileChanged(filePath) {
54
+ // 1. If file was deleted or doesn't exist, strictly it "changed" (it's gone),
55
+ // but for indexing purposes, we skip it or handle cleanup.
56
+ if (!fs.existsSync(filePath))
57
+ return true;
58
+ // 2. Compute current Hash
59
+ const content = fs.readFileSync(filePath, 'utf-8');
60
+ const newHash = this.computeHash(content);
61
+ // 3. Query DB
62
+ const stmt = this.db.prepare('SELECT hash FROM file_registry WHERE path = ?');
63
+ const row = stmt.get(filePath);
64
+ // 4. Decision Logic
65
+ if (!row) {
66
+ return true; // New file
67
+ }
68
+ if (row.hash !== newHash) {
69
+ return true; // Modified file
70
+ }
71
+ return false; // Pristine (Cached)
72
+ }
73
+ /**
74
+ * Updates the registry with the new file state.
75
+ * This should be called AFTER the vector indexing and skeleton generation is done.
76
+ * * @param filePath - The path of the file
77
+ * @param skeleton - The JSON string representing the AST Skeleton (Class/Method signatures)
78
+ */
79
+ updateFile(filePath, skeleton) {
80
+ if (!fs.existsSync(filePath))
81
+ return;
82
+ const content = fs.readFileSync(filePath, 'utf-8');
83
+ const hash = this.computeHash(content);
84
+ const now = Date.now();
85
+ const skeletonStr = skeleton ? JSON.stringify(skeleton) : null;
86
+ // UPSERT: Insert or Update if exists
87
+ const stmt = this.db.prepare(`
88
+ INSERT OR REPLACE INTO file_registry (path, hash, last_indexed, skeleton_signature)
89
+ VALUES (?, ?, ?, ?)
90
+ `);
91
+ stmt.run(filePath, hash, now, skeletonStr);
92
+ }
93
+ /**
94
+ * Retrieves the cached skeleton for a file.
95
+ * Used by the LLM Provider to build context without reading the full disk.
96
+ */
97
+ getSkeleton(filePath) {
98
+ const stmt = this.db.prepare('SELECT skeleton_signature FROM file_registry WHERE path = ?');
99
+ const row = stmt.get(filePath);
100
+ if (row && row.skeleton_signature) {
101
+ return JSON.parse(row.skeleton_signature);
102
+ }
103
+ return null;
104
+ }
105
+ /**
106
+ * Helper: Generates MD5 hash of string content.
107
+ */
108
+ computeHash(content) {
109
+ return crypto.createHash('md5').update(content).digest('hex');
110
+ }
111
+ }
112
+ exports.FileRegistry = FileRegistry;
@@ -0,0 +1,58 @@
1
+ import { FileAnalysisResult } from '../../types';
2
+ /**
3
+ * The Brain Surgeon 🩺
4
+ * Analyzes TypeScript files using AST to extract intelligent code chunks and dependency graphs.
5
+ * Optimized for NestJS architecture patterns.
6
+ */
7
+ export declare class NestChunker {
8
+ private project;
9
+ constructor();
10
+ /**
11
+ * Analyzes a file content and breaks it down based on its type (Service, DTO, Module).
12
+ * * @param filePath - The relative path of the file.
13
+ * @param content - The raw string content of the file.
14
+ * @param fileHash - The MD5 hash for registry tracking.
15
+ */
16
+ analyze(filePath: string, content: string, fileHash: string): FileAnalysisResult;
17
+ /**
18
+ * Checks if the file should be treated as a single atomic unit.
19
+ * Rules: DTOs, Entities, Interfaces, Enums.
20
+ */
21
+ private isAtomicFile;
22
+ /**
23
+ * Strategy A: Atomic Processing
24
+ * Stores the whole file as one chunk. Essential for DTOs/Entities context.
25
+ */
26
+ private processAtomicFile;
27
+ /**
28
+ * Strategy B: Logic Processing (Parent-Child)
29
+ * Splits Services/Controllers into Class Context (Parent) and Methods (Children).
30
+ */
31
+ private processLogicFile;
32
+ /**
33
+ * Extracts the "Context" of a class without the heavy method implementation.
34
+ * Keeps imports (from file), class decorators, properties, and constructor.
35
+ */
36
+ private extractClassContext;
37
+ /**
38
+ * Extracts static import relationships to build the Dependency Graph.
39
+ * It parses the AST to find all relative imports and resolves them to physical files.
40
+ * * @param sourceFile - The AST SourceFile object from ts-morph.
41
+ * @param sourcePath - The relative path of the file currently being analyzed (e.g., 'src/auth/auth.service.ts').
42
+ * @returns An array of graph edges representing 'import' relationships.
43
+ */
44
+ private extractDependencies;
45
+ private getClassName;
46
+ private generateSkeleton;
47
+ /**
48
+ * Resolves the physical filesystem path for a given import string.
49
+ * Handles TypeScript resolution strategies including file extensions and directory indexes.
50
+ * * @param sourceDir - The absolute directory path of the file containing the import.
51
+ * @param importPath - The raw import string (e.g., './users.service' or './dto').
52
+ * @returns The absolute path to the resolved .ts file, or `null` if not found/external.
53
+ * * @example
54
+ * resolveModulePath('/src/users', './dto');
55
+ * // Returns: '/src/users/dto/index.ts'
56
+ */
57
+ private resolveModulePath;
58
+ }
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NestChunker = void 0;
37
+ const ts_morph_1 = require("ts-morph");
38
+ const uuid_1 = require("uuid");
39
+ const path = __importStar(require("path"));
40
+ const fs = __importStar(require("fs")); // Necesario para verificar si existe el archivo .ts o index.ts
41
+ /**
42
+ * The Brain Surgeon 🩺
43
+ * Analyzes TypeScript files using AST to extract intelligent code chunks and dependency graphs.
44
+ * Optimized for NestJS architecture patterns.
45
+ */
46
+ class NestChunker {
47
+ constructor() {
48
+ // Initialize ts-morph project.
49
+ // We skip loading the whole tsconfig for speed, processing files individually.
50
+ this.project = new ts_morph_1.Project({
51
+ skipAddingFilesFromTsConfig: true,
52
+ useInMemoryFileSystem: true,
53
+ });
54
+ }
55
+ /**
56
+ * Analyzes a file content and breaks it down based on its type (Service, DTO, Module).
57
+ * * @param filePath - The relative path of the file.
58
+ * @param content - The raw string content of the file.
59
+ * @param fileHash - The MD5 hash for registry tracking.
60
+ */
61
+ analyze(filePath, content, fileHash) {
62
+ // 1. Create AST from content
63
+ const sourceFile = this.project.createSourceFile(filePath, content, {
64
+ overwrite: true,
65
+ });
66
+ // 2. Determine Strategy based on file extension/name
67
+ const isAtomic = this.isAtomicFile(filePath);
68
+ // 3. Extract Dependencies (Imports) -> For the Knowledge Graph
69
+ const dependencies = this.extractDependencies(sourceFile, filePath);
70
+ // 4. Generate Chunks
71
+ let chunks = [];
72
+ if (isAtomic) {
73
+ chunks = this.processAtomicFile(sourceFile);
74
+ }
75
+ else {
76
+ chunks = this.processLogicFile(sourceFile);
77
+ }
78
+ // 5. Generate Skeleton (Simplified view for caching)
79
+ // We reuse the logic: if it's atomic, skeleton is full file. If logic, it's signatures.
80
+ const skeleton = isAtomic
81
+ ? { type: 'full', content: '...' }
82
+ : this.generateSkeleton(sourceFile);
83
+ return {
84
+ filePath,
85
+ fileHash,
86
+ chunks,
87
+ dependencies,
88
+ skeleton,
89
+ };
90
+ }
91
+ // ==========================================
92
+ // 🕵️ STRATEGIES
93
+ // ==========================================
94
+ /**
95
+ * Checks if the file should be treated as a single atomic unit.
96
+ * Rules: DTOs, Entities, Interfaces, Enums.
97
+ */
98
+ isAtomicFile(filePath) {
99
+ return (filePath.endsWith('.dto.ts') ||
100
+ filePath.endsWith('.entity.ts') ||
101
+ filePath.endsWith('.interface.ts') ||
102
+ filePath.endsWith('.enum.ts') ||
103
+ filePath.endsWith('.type.ts'));
104
+ }
105
+ /**
106
+ * Strategy A: Atomic Processing
107
+ * Stores the whole file as one chunk. Essential for DTOs/Entities context.
108
+ */
109
+ processAtomicFile(sourceFile) {
110
+ return [
111
+ {
112
+ id: (0, uuid_1.v4)(),
113
+ type: 'file',
114
+ content: sourceFile.getFullText(),
115
+ metadata: {
116
+ startLine: 1,
117
+ endLine: sourceFile.getEndLineNumber(),
118
+ className: this.getClassName(sourceFile),
119
+ },
120
+ },
121
+ ];
122
+ }
123
+ /**
124
+ * Strategy B: Logic Processing (Parent-Child)
125
+ * Splits Services/Controllers into Class Context (Parent) and Methods (Children).
126
+ */
127
+ processLogicFile(sourceFile) {
128
+ const chunks = [];
129
+ const classes = sourceFile.getClasses();
130
+ for (const cls of classes) {
131
+ // 1. Create Parent Chunk (The Class Context)
132
+ // Includes: Decorators, Properties, Constructor. Excludes: Method Bodies.
133
+ const parentId = (0, uuid_1.v4)();
134
+ const classContext = this.extractClassContext(cls);
135
+ chunks.push({
136
+ id: parentId,
137
+ type: 'class_signature',
138
+ content: classContext,
139
+ metadata: {
140
+ startLine: cls.getStartLineNumber(),
141
+ endLine: cls.getEndLineNumber(),
142
+ className: cls.getName(),
143
+ decorators: cls.getDecorators().map((d) => d.getName()),
144
+ },
145
+ });
146
+ // 2. Create Child Chunks (The Methods)
147
+ const methods = cls.getMethods();
148
+ for (const method of methods) {
149
+ chunks.push({
150
+ id: (0, uuid_1.v4)(),
151
+ parentId: parentId, // Link to Parent!
152
+ type: 'method',
153
+ content: method.getFullText(), // Method logic
154
+ metadata: {
155
+ startLine: method.getStartLineNumber(),
156
+ endLine: method.getEndLineNumber(),
157
+ className: cls.getName(),
158
+ methodName: method.getName(),
159
+ decorators: method.getDecorators().map((d) => d.getName()),
160
+ },
161
+ });
162
+ }
163
+ }
164
+ return chunks;
165
+ }
166
+ // ==========================================
167
+ // 🛠️ HELPERS
168
+ // ==========================================
169
+ /**
170
+ * Extracts the "Context" of a class without the heavy method implementation.
171
+ * Keeps imports (from file), class decorators, properties, and constructor.
172
+ */
173
+ extractClassContext(cls) {
174
+ // We clone the structure to manipulate text without breaking the original AST
175
+ // Ideally, we construct a string string representation:
176
+ let text = cls
177
+ .getDecorators()
178
+ .map((d) => d.getText())
179
+ .join('\n');
180
+ text += `\nexport class ${cls.getName()} {\n`;
181
+ // Add properties (e.g., private readonly userService: UserService;)
182
+ cls.getProperties().forEach((prop) => {
183
+ text += ` ${prop.getText()}\n`;
184
+ });
185
+ // Add constructor
186
+ const ctor = cls.getConstructors()[0];
187
+ if (ctor) {
188
+ text += ` ${ctor.getText()}\n`;
189
+ }
190
+ text += ` // Methods are indexed separately as child chunks...\n`;
191
+ text += `}`;
192
+ // Prepend Imports from the source file for full context
193
+ const imports = cls
194
+ .getSourceFile()
195
+ .getImportDeclarations()
196
+ .map((i) => i.getText())
197
+ .join('\n');
198
+ return `${imports}\n\n${text}`;
199
+ }
200
+ /**
201
+ * Extracts static import relationships to build the Dependency Graph.
202
+ * It parses the AST to find all relative imports and resolves them to physical files.
203
+ * * @param sourceFile - The AST SourceFile object from ts-morph.
204
+ * @param sourcePath - The relative path of the file currently being analyzed (e.g., 'src/auth/auth.service.ts').
205
+ * @returns An array of graph edges representing 'import' relationships.
206
+ */
207
+ extractDependencies(sourceFile, sourcePath) {
208
+ const edges = [];
209
+ const imports = sourceFile.getImportDeclarations();
210
+ // Necesitamos el directorio absoluto para resolver, así que combinamos CWD + sourcePath
211
+ // Nota: Asumimos que sourcePath entra como relativa, ej: 'src/users/users.service.ts'
212
+ const absoluteSourcePath = path.resolve(process.cwd(), sourcePath);
213
+ const sourceDir = path.dirname(absoluteSourcePath);
214
+ for (const imp of imports) {
215
+ const moduleSpecifier = imp.getModuleSpecifierValue();
216
+ // 1. Filter: We only care about internal relative imports (starting with '.')
217
+ if (moduleSpecifier.startsWith('.')) {
218
+ // 2. Resolution: Find the physical .ts file on disk
219
+ const resolvedPath = this.resolveModulePath(sourceDir, moduleSpecifier);
220
+ // 3. Validation: Only link if the file actually exists
221
+ if (resolvedPath) {
222
+ // 4. Normalization: Convert back to relative path for the Database
223
+ // We use split/join to force forward slashes (/) even on Windows for DB consistency.
224
+ const relativeTarget = path
225
+ .relative(process.cwd(), resolvedPath)
226
+ .split(path.sep)
227
+ .join('/');
228
+ edges.push({
229
+ sourcePath: sourcePath, // Already relative
230
+ targetPath: relativeTarget,
231
+ relation: 'import',
232
+ });
233
+ }
234
+ }
235
+ }
236
+ return edges;
237
+ }
238
+ getClassName(sourceFile) {
239
+ return sourceFile.getClasses()[0]?.getName();
240
+ }
241
+ generateSkeleton(sourceFile) {
242
+ return {
243
+ // 1. Guardamos imports visuales para que el LLM sepa de dónde vienen los tipos
244
+ imports: sourceFile.getImportDeclarations().map((i) => i.getText()),
245
+ classes: sourceFile.getClasses().map((c) => ({
246
+ name: c.getName(),
247
+ // 2. MEJORA: No guardes solo el nombre "create".
248
+ // Guarda la firma: "create(dto: CreateUserDto): Promise<User>"
249
+ // Cortamos justo antes de la llave '{' para quitar el cuerpo.
250
+ methods: c.getMethods().map((m) => {
251
+ // Obtiene solo la estructura (nombre, args, retorno)
252
+ // const structure = m.getStructure();
253
+ // O reconstrúyelo simple:
254
+ return `${m.getName()}(${m
255
+ .getParameters()
256
+ .map((p) => p.getText())
257
+ .join(', ')}): ${m.getReturnType().getText()};`;
258
+ }),
259
+ })),
260
+ };
261
+ }
262
+ /**
263
+ * Resolves the physical filesystem path for a given import string.
264
+ * Handles TypeScript resolution strategies including file extensions and directory indexes.
265
+ * * @param sourceDir - The absolute directory path of the file containing the import.
266
+ * @param importPath - The raw import string (e.g., './users.service' or './dto').
267
+ * @returns The absolute path to the resolved .ts file, or `null` if not found/external.
268
+ * * @example
269
+ * resolveModulePath('/src/users', './dto');
270
+ * // Returns: '/src/users/dto/index.ts'
271
+ */
272
+ resolveModulePath(sourceDir, importPath) {
273
+ // 1. Construct the potential absolute path
274
+ const absoluteBase = path.join(sourceDir, importPath);
275
+ // Case A: Explicit file extension (rare in imports, but valid)
276
+ // import ... from './file.ts'
277
+ if (fs.existsSync(absoluteBase) && fs.statSync(absoluteBase).isFile()) {
278
+ return absoluteBase;
279
+ }
280
+ // Case B: Implicit .ts extension (Most common)
281
+ // import ... from './users.service' -> checks users.service.ts
282
+ const tsPath = `${absoluteBase}.ts`;
283
+ if (fs.existsSync(tsPath)) {
284
+ return tsPath;
285
+ }
286
+ // Case C: Directory Index (Barrel Files)
287
+ // import ... from './dto' -> checks dto/index.ts
288
+ const indexPath = path.join(absoluteBase, 'index.ts');
289
+ if (fs.existsSync(indexPath)) {
290
+ return indexPath;
291
+ }
292
+ // Case D: Resolution failed
293
+ // Could be a node_module, a path alias (@src/...), or a non-existent file.
294
+ return null;
295
+ }
296
+ }
297
+ exports.NestChunker = NestChunker;
File without changes
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ // import { integrityCheckTool, askCodebaseTool } from '../tools/tools';
3
+ // Nueva Descripción (Concepto): "Semantic Code Analysis Tool. Use this to gain deep context about the project's logic. It retrieves not just code snippets, but also class skeletons and dependency relationships. Returns: Code chunks + File Paths. Strategy: If the result implies a complex file, use the native read_file on the returned path to see the full implementation."
4
+ // Eres un Ingeniero de Software Principal especializado en NestJS (Node.js).
5
+ // Estás operando directamente en el sistema de archivos local de un proyecto real.
6
+ // 💎 ESTÁNDARES DE CALIDAD (INQUEBRANTABLES):
7
+ // 1. **Arquitectura:** Sigue DDD (Domain Driven Development) y Mejores Prácticas de NestJS.
8
+ // - Usa DTOs estrictos con 'class-validator' y 'class-transformer'.
9
+ // - Siempre documenta con TSDocs (inglés o español según contexto, prefiere inglés técnico).
10
+ // 2. **Testing (TDD):** NO escribas código sin su test.
11
+ // - Crea el archivo '.spec.ts' junto con la implementación.
12
+ // - Asegúrate de que los tests pasen.
13
+ // 3. **Manejo de Errores:**
14
+ // - Usa excepciones HTTP estándar de NestJS (NotFoundException, BadRequestException).
15
+ // - Nunca tragues errores silenciosamente (no empty catch).
16
+ // 4. **Tipado:** TypeScript Estricto. Prohibido usar 'any'.
17
+ // ⚙️ PROTOCOLO DE EJECUCIÓN:
18
+ // 1. **PLANIFICAR (write_todos):** Desglosa la tarea. Si es compleja, delega el diseño al 'senior_architect'.
19
+ // 2. **INVESTIGAR (ask_codebase):** ANTES de tocar nada, busca cómo se hacen las cosas en este proyecto.
20
+ // - Ejemplo: "Busca UserEntity antes de crear un DTO relacionado".
21
+ // 3. **IMPLEMENTAR (Safe Write):** Escribe los archivos.
22
+ // - Recuerda: Tienes un sistema de backups activo, trabaja con confianza pero con cuidado.
23
+ // 4. **VALIDAR (run_integrity_check):** OBLIGATORIO.
24
+ // - Después de escribir, corre el chequeo de integridad.
25
+ // - Si falla, AUTO-CORRÍGETE. No preguntes al usuario, arregla el error de compilación.
26
+ // 5. **APRENDER:** Si el usuario te corrige una preferencia de estilo, guárdala en '/memories/style-guide.txt'.
27
+ // 🚨 REGLAS DE SEGURIDAD:
28
+ // - Nunca borres archivos masivamente.
29
+ // - Si modificas un archivo core (como app.module.ts), verifica doblemente los imports.