@dastbal/nestjs-ai-agent 1.0.1 → 1.0.3
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 +51 -88
- package/dist/bin/cli.js +2 -2
- package/dist/core/agent/factory.js +11 -10
- package/dist/core/tools/tools.d.ts +26 -9
- package/dist/core/tools/tools.js +114 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,116 +1,79 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @dastbal/nestjs-ai-agent 🧙♂️
|
|
2
|
+
### Autonomous Principal Software Engineer for NestJS
|
|
2
3
|
|
|
3
|
-
](https://www.npmjs.com/package/@dastbal/nestjs-ai-agent)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
6
|
|
|
7
|
+
Transform your NestJS development with an agent that doesn't just "chat", but **operates** directly on your codebase with Senior-level precision.
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
---
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
## 🚀 Quick Start
|
|
9
12
|
|
|
10
|
-
## Features
|
|
11
|
-
- **The Surgeon Rule**: Reads and analyzes files before writing code.
|
|
12
|
-
- **RAG Powered**: Uses your codebase as context for precise implementations.
|
|
13
|
-
- **SQLite Persistence**: Remembers conversation history between sessions.
|
|
14
|
-
- **Self-Correcting**: Automatically runs integrity checks and fixes compilation errors.
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
13
|
```bash
|
|
14
|
+
# Install the agent
|
|
18
15
|
npm install @dastbal/nestjs-ai-agent
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
🔐 Configuration & Authentication
|
|
24
|
-
This agent uses Google Vertex AI. To allow the agent to operate, you must provide your Google Cloud credentials.
|
|
25
|
-
|
|
26
|
-
Create your credentials file: Place your Google Service Account JSON file in the root of your project and name it exactly: credentials_vertex.json
|
|
27
|
-
|
|
28
|
-
Setup your environment: Create a .env file in your project root and point to that file:
|
|
29
|
-
|
|
30
|
-
Code snippet
|
|
31
|
-
GOOGLE_APPLICATION_CREDENTIALS="./credentials_vertex.json"
|
|
32
|
-
# Optional: specify your project and location
|
|
33
|
-
GCP_PROJECT_ID="your-project-id"
|
|
34
|
-
GCP_LOCATION="us-central1"
|
|
35
|
-
⚠️ Important Security Note: Never commit your credentials_vertex.json or .env files to version control. Add them to your .gitignore.
|
|
36
|
-
|
|
37
|
-
## Introduction
|
|
38
|
-
|
|
39
|
-
I am a specialized AI, a Principal Software Engineer, crafted to assist you in your NestJS projects. I operate directly on the project's local file system, enabling me to read, understand, and modify your codebase with precision and efficiency. My goal is to help you write clean, well-documented, and testable code, adhering to the best practices of NestJS and Domain-Driven Design (DDD).
|
|
40
|
-
|
|
41
|
-
## Core Functionality
|
|
42
|
-
|
|
43
|
-
Here's a breakdown of how I work:
|
|
44
|
-
|
|
45
|
-
1. **Understanding Your Needs:** I start by carefully interpreting your requests. I clarify any ambiguities and break down complex tasks into manageable steps.
|
|
46
|
-
|
|
47
|
-
2. **Code Exploration (🔍 `ask_codebase`):**
|
|
48
|
-
* Before making any changes, I use the `ask_codebase` tool. This is my primary research instrument.
|
|
49
|
-
* It allows me to perform semantic searches and analyze the project's dependency graph.
|
|
50
|
-
* I use it to find relevant code snippets, understand how different parts of the system interact, and locate the exact file paths I need.
|
|
17
|
+
# Run your first command
|
|
18
|
+
npx gen "Create a new Payments service with DDD patterns"
|
|
19
|
+
```
|
|
51
20
|
|
|
52
|
-
|
|
53
|
-
* When I need to modify a file, I *always* read its contents first using `safe_read_file`.
|
|
54
|
-
* This ensures I understand the existing code, its structure, and any existing documentation (TSDocs).
|
|
55
|
-
* This is crucial for avoiding regressions and maintaining code quality.
|
|
21
|
+
---
|
|
56
22
|
|
|
57
|
-
|
|
58
|
-
* Based on your instructions and my understanding of the code, I generate or modify code.
|
|
59
|
-
* I adhere to strict TypeScript typing, DDD principles, and NestJS best practices.
|
|
60
|
-
* I also create corresponding tests to ensure code reliability.
|
|
23
|
+
## 💎 Key Features
|
|
61
24
|
|
|
62
|
-
|
|
63
|
-
* I write the generated or modified code to the file system using `safe_write_file`.
|
|
64
|
-
* This tool automatically creates a backup of the original file, providing a safety net.
|
|
25
|
+
This agent operates with a strict set of principles and advanced capabilities:
|
|
65
26
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
27
|
+
* **🔍 RAG Search:** Performs semantic search across your entire codebase before proposing changes, ensuring context-aware development.
|
|
28
|
+
* **🩺 The Surgeon Rule:** Never overwrites a file without reading and analyzing it first, preserving existing logic and intent.
|
|
29
|
+
* **✅ Self-Healing:** Runs integrity checks (TypeScript compiler) and attempts to auto-fix compilation errors (up to 3 retries).
|
|
30
|
+
* **💾 Safe Writes:** Automatically creates backups before any file modification, ensuring data safety.
|
|
31
|
+
* **🧠 SQLite Memory:** Remembers conversation threads and learned preferences across restarts using a local SQLite database.
|
|
32
|
+
* **🔐 Configuration:** Leverages Google Vertex AI. Requires a service account JSON file (`credentials_vertex.json`) in the root folder and specific environment variables.
|
|
70
33
|
|
|
71
|
-
|
|
72
|
-
* If the integrity check fails, I analyze the error messages and attempt to fix the code myself.
|
|
73
|
-
* I will retry up to three times. If I cannot fix the error, I will ask for human assistance.
|
|
34
|
+
**Credentials File:** Place your Google Service Account JSON in the root folder and name it exactly `credentials_vertex.json`.
|
|
74
35
|
|
|
75
|
-
|
|
76
|
-
|
|
36
|
+
**Environment Variables:** Add the following to your `.env` file:
|
|
37
|
+
```dotenv
|
|
38
|
+
GOOGLE_APPLICATION_CREDENTIALS="./credentials_vertex.json"
|
|
39
|
+
GCP_PROJECT_ID="your-project-id"
|
|
40
|
+
GCP_LOCATION="us-central1"
|
|
41
|
+
```
|
|
42
|
+
**[CAUTION] Security First:** Always add `credentials_vertex.json` and `.env` to your `.gitignore` file to protect your credentials.
|
|
77
43
|
|
|
78
|
-
|
|
79
|
-
* I always strive to maintain and improve code documentation (TSDocs).
|
|
44
|
+
---
|
|
80
45
|
|
|
81
|
-
|
|
82
|
-
* I learn from your feedback. If you correct a style preference, I store it in `/memories/style-guide.txt` to improve future responses.
|
|
46
|
+
## ⚙️ Internal Workflow
|
|
83
47
|
|
|
84
|
-
|
|
48
|
+
The agent follows a strict Principal Engineer protocol:
|
|
85
49
|
|
|
86
|
-
|
|
50
|
+
1. **Research:** Uses `ask_codebase` to find existing patterns, logic, and dependencies.
|
|
51
|
+
2. **Comprehension:** Reads existing code using `safe_read_file` to understand context and avoid regressions.
|
|
52
|
+
3. **Implementation:** Writes new code adhering to DDD principles, strict TypeScript typing (no `any`), and TSDocs.
|
|
53
|
+
4. **Validation:** Runs `run_integrity_check` (TypeScript compiler) immediately after implementation to ensure type safety.
|
|
54
|
+
5. **Safety:** Creates backups before writing files using `safe_write_file`.
|
|
55
|
+
6. **Human-in-the-loop:** Pauses for explicit approval before performing critical file operations or major changes.
|
|
87
56
|
|
|
88
|
-
|
|
89
|
-
* **Retrieval-Augmented Generation (RAG):** I utilize a live RAG system. This means I can dynamically access and process information from your codebase to provide accurate and relevant responses. This allows me to understand the context of your project and generate code that fits seamlessly.
|
|
90
|
-
* **NestJS Expertise:** I have been specifically trained on NestJS best practices, DDD, and related concepts.
|
|
91
|
-
* **TypeScript:** I am fluent in TypeScript and adhere to strict typing.
|
|
92
|
-
* **Libraries:** I utilize a suite of libraries for code analysis, generation, and validation.
|
|
93
|
-
* **File System Access:** I have secure access to the project's local file system, allowing me to directly modify your code.
|
|
57
|
+
---
|
|
94
58
|
|
|
95
|
-
##
|
|
59
|
+
## 💡 Usage Examples
|
|
96
60
|
|
|
97
|
-
|
|
98
|
-
* **Comprehensive Testing:** I create tests alongside the code to ensure its reliability.
|
|
99
|
-
* **Error Handling:** I use standard NestJS HTTP exceptions and avoid swallowing errors silently.
|
|
100
|
-
* **Code Reviews:** I am designed to be used in conjunction with human code reviews to ensure the highest quality.
|
|
61
|
+
Try these commands to see the agent in action:
|
|
101
62
|
|
|
102
|
-
|
|
63
|
+
* **Scaffolding:** `"Create a UserEntity with email and password fields using TypeORM"`
|
|
64
|
+
* **Logic Implementation:** `"Add a validation pipe to the login DTO"`
|
|
65
|
+
* **Testing:** `"Write a unit test for the AuthService including mocks for the repository"`
|
|
66
|
+
* **Refactoring:** `"Standardize all HTTP exceptions in the users controller"`
|
|
67
|
+
* **Code Generation:** `"Generate a NestJS module for handling user authentication"`
|
|
103
68
|
|
|
104
|
-
|
|
69
|
+
---
|
|
105
70
|
|
|
106
|
-
|
|
107
|
-
* "Create a service to handle authentication."
|
|
108
|
-
* "Write a unit test for the UserService."
|
|
71
|
+
## 🧠 Learning & Adaptation
|
|
109
72
|
|
|
110
|
-
|
|
73
|
+
The agent learns from your feedback. If you provide a style correction or a new pattern, it stores this information in `.agent/memories/style-guide.txt` to ensure future code generation aligns with your preferences.
|
|
111
74
|
|
|
112
|
-
|
|
75
|
+
---
|
|
113
76
|
|
|
114
|
-
|
|
77
|
+
## 📄 License
|
|
115
78
|
|
|
116
|
-
|
|
79
|
+
This project is released under the MIT License. Build something amazing! ✨
|
package/dist/bin/cli.js
CHANGED
|
@@ -61,10 +61,10 @@ program
|
|
|
61
61
|
}
|
|
62
62
|
log.sys("Inicializando Agente en modo CLI...");
|
|
63
63
|
// El threadId puede ser fijo para sesiones de CLI o dinámico
|
|
64
|
-
const threadId = "cli-user
|
|
64
|
+
const threadId = "cli-user";
|
|
65
65
|
const agent = await factory_1.AgentFactory.create(threadId);
|
|
66
66
|
log.ai(`Procesando: "${instruction}"`);
|
|
67
|
-
const response = await agent.invoke({ messages: [{ role: "user", content: instruction }] }, { configurable: { thread_id: threadId } });
|
|
67
|
+
const response = await agent.invoke({ messages: [{ role: "user", content: instruction }] }, { configurable: { thread_id: threadId }, recursionLimit: 50 });
|
|
68
68
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
69
69
|
if (lastMessage && lastMessage.content) {
|
|
70
70
|
console.log("\n" + chalk_1.default.green("--- RESPUESTA DEL AGENTE ---"));
|
|
@@ -42,14 +42,14 @@ const tools_1 = require("../tools/tools");
|
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
44
|
class AgentFactory {
|
|
45
|
-
static async create(threadId =
|
|
45
|
+
static async create(threadId = "cli-session") {
|
|
46
46
|
const rootDir = process.cwd();
|
|
47
47
|
// Configuración de directorios
|
|
48
|
-
const agentDir = path.join(rootDir,
|
|
48
|
+
const agentDir = path.join(rootDir, ".agent");
|
|
49
49
|
if (!fs.existsSync(agentDir))
|
|
50
50
|
fs.mkdirSync(agentDir, { recursive: true });
|
|
51
51
|
// 1. Persistencia (Checkpointer)
|
|
52
|
-
const dbPath = path.join(agentDir,
|
|
52
|
+
const dbPath = path.join(agentDir, "history.db");
|
|
53
53
|
const checkpointer = langgraph_checkpoint_sqlite_1.SqliteSaver.fromConnString(dbPath);
|
|
54
54
|
// 2. Store (Opcional en createAgent, pero lo mantenemos por si lo usas en el runtime)
|
|
55
55
|
const memoryStore = new langgraph_checkpoint_1.InMemoryStore();
|
|
@@ -85,7 +85,7 @@ Typing: Strict TypeScript. The use of any is FORBIDDEN.
|
|
|
85
85
|
|
|
86
86
|
⚙️ EXECUTION PROTOCOL:
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
- You operate on a NestJS project. The root directory is: ${process.cwd()}
|
|
89
89
|
RESEARCH (ask_codebase): BEFORE touching anything, search for existing patterns in the project.
|
|
90
90
|
|
|
91
91
|
Example: "Search for UserEntity before creating a related DTO."
|
|
@@ -113,13 +113,14 @@ NOTE ON INDEXING:
|
|
|
113
113
|
- If 'ask_codebase' fails to find recent changes, use 'refresh_project_index'.
|
|
114
114
|
|
|
115
115
|
Wait for human approval. If rejected, propose a different solution.
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
|
|
116
|
+
- Use RELATIVE PATHS for all file operations.
|
|
117
|
+
- All source code is inside the 'src' folder (e.g., 'src/app.module.ts').
|
|
118
|
+
- DO NOT use the '/project/' prefix anymore. Just use the path relative to the root.
|
|
119
|
+
|
|
120
|
+
🔍 EXAMPLE:
|
|
121
|
+
- Correct: safe_write_file('src/calculator/calculator.controller.ts', '...')
|
|
122
|
+
- Incorrect: safe_write_file('/project/src/...', '...')
|
|
120
123
|
|
|
121
|
-
ALWAYS use the '/project/' prefix when reading or writing source code.
|
|
122
|
-
Example: write_file('/project/src/app.service.ts', '...')
|
|
123
124
|
🔍 CODE REFINEMENT & INTEGRITY (THE SURGEON'S RULE):
|
|
124
125
|
|
|
125
126
|
1. Read-Before-Write: NEVER overwrite a file without reading it first using 'safe_read_file'. You must understand the existing logic, TSDocs, and dependencies before making any changes.
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Tool for safely writing content to a file on the real disk.
|
|
4
|
+
* It creates a backup before writing and then triggers a project re-indexing.
|
|
5
|
+
* @param {object} params - The parameters for the tool.
|
|
6
|
+
* @param {string} params.filePath - The relative path where the file should be saved.
|
|
7
|
+
* @param {string} params.content - The content to write to the file.
|
|
8
|
+
* @returns {Promise<string>} A message indicating success or failure.
|
|
9
|
+
*/
|
|
2
10
|
export declare const safeWriteFileTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
3
11
|
filePath: z.ZodString;
|
|
4
12
|
content: z.ZodString;
|
|
@@ -9,6 +17,12 @@ export declare const safeWriteFileTool: import("@langchain/core/tools").DynamicS
|
|
|
9
17
|
filePath: string;
|
|
10
18
|
content: string;
|
|
11
19
|
}, string, "safe_write_file">;
|
|
20
|
+
/**
|
|
21
|
+
* Tool for safely reading the content of a file from the real disk.
|
|
22
|
+
* @param {object} params - The parameters for the tool.
|
|
23
|
+
* @param {string} params.filePath - The relative path of the file to read.
|
|
24
|
+
* @returns {Promise<string>} The content of the file, or an error message if reading fails.
|
|
25
|
+
*/
|
|
12
26
|
export declare const safeReadFileTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
13
27
|
filePath: z.ZodString;
|
|
14
28
|
}, z.core.$strip>, {
|
|
@@ -17,8 +31,11 @@ export declare const safeReadFileTool: import("@langchain/core/tools").DynamicSt
|
|
|
17
31
|
filePath: string;
|
|
18
32
|
}, string, "safe_read_file">;
|
|
19
33
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
34
|
+
* Tool to query the codebase using semantic search and dependency graph analysis.
|
|
35
|
+
* It's the primary way for the agent to explore and understand the project structure and logic.
|
|
36
|
+
* @param {object} params - The parameters for the tool.
|
|
37
|
+
* @param {string} params.query - A natural language query describing the code or functionality to find.
|
|
38
|
+
* @returns {Promise<string>} A report containing relevant code snippets, file paths, and dependencies.
|
|
22
39
|
*/
|
|
23
40
|
export declare const askCodebaseTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
24
41
|
query: z.ZodString;
|
|
@@ -28,15 +45,15 @@ export declare const askCodebaseTool: import("@langchain/core/tools").DynamicStr
|
|
|
28
45
|
query: string;
|
|
29
46
|
}, string, "ask_codebase">;
|
|
30
47
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
48
|
+
* Tool to run the TypeScript compiler (tsc) for type checking.
|
|
49
|
+
* This is crucial for maintaining code quality and catching errors early.
|
|
50
|
+
* @returns {Promise<string>} A message indicating whether the integrity check passed or failed, including compiler output on failure.
|
|
33
51
|
*/
|
|
34
52
|
export declare const integrityCheckTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, z.core.$strip>, Record<string, never>, Record<string, never>, string, "run_integrity_check">;
|
|
35
53
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* * @returns {Promise<string>} A confirmation message or error details.
|
|
54
|
+
* Tool to refresh the project's index, forcing a re-scan and re-vectorization of all files.
|
|
55
|
+
* This is useful when the agent needs to be absolutely sure it's working with the latest code,
|
|
56
|
+
* especially after significant changes or if the automatic indexing seems to be lagging.
|
|
57
|
+
* @returns {Promise<string>} A confirmation message or details about any errors encountered during indexing.
|
|
41
58
|
*/
|
|
42
59
|
export declare const refreshIndexTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, z.core.$strip>, Record<string, never>, Record<string, never>, string, "refresh_project_index">;
|
package/dist/core/tools/tools.js
CHANGED
|
@@ -52,41 +52,79 @@ const log = {
|
|
|
52
52
|
tool: (msg) => console.log(chalk_1.default.yellow('🛠️ [TOOL]: ') + msg),
|
|
53
53
|
sys: (msg) => console.log(chalk_1.default.gray('⚙️ [SYS]: ') + msg),
|
|
54
54
|
error: (msg) => console.log(chalk_1.default.red('❌ [ERR]: ') + msg),
|
|
55
|
+
debug: (msg) => console.log(chalk_1.default.magenta('🐛 [DEBUG]: ') + msg), // Added for debugging
|
|
55
56
|
};
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
+
* Creates a backup of a file before it is modified.
|
|
59
|
+
* The backup is stored in the .agent/backups directory with a timestamp.
|
|
60
|
+
* @param {string} filePath - The relative path of the file to back up.
|
|
58
61
|
*/
|
|
59
62
|
const createBackup = (filePath) => {
|
|
63
|
+
log.debug(`Starting backup process for file: ${filePath}`);
|
|
60
64
|
const rootDir = process.cwd();
|
|
61
65
|
const backupDir = path.join(rootDir, '.agent', 'backups');
|
|
62
|
-
|
|
66
|
+
log.debug(`Backup directory resolved to: ${backupDir}`);
|
|
67
|
+
if (!fs.existsSync(backupDir)) {
|
|
68
|
+
log.debug(`Backup directory does not exist. Creating: ${backupDir}`);
|
|
63
69
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
70
|
+
}
|
|
64
71
|
const realPath = path.resolve(rootDir, filePath);
|
|
72
|
+
log.debug(`Resolved real path for backup: ${realPath}`);
|
|
65
73
|
if (fs.existsSync(realPath)) {
|
|
66
74
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
67
75
|
const filename = path.basename(realPath);
|
|
68
76
|
const backupPath = path.join(backupDir, `${timestamp}_${filename}.bak`);
|
|
77
|
+
log.debug(`Attempting to copy ${realPath} to ${backupPath}`);
|
|
69
78
|
fs.copyFileSync(realPath, backupPath);
|
|
79
|
+
log.sys(`Backup created for ${filePath} at ${backupPath}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
log.debug(`File does not exist, no backup needed: ${realPath}`);
|
|
70
83
|
}
|
|
71
84
|
};
|
|
85
|
+
/**
|
|
86
|
+
* Tool for safely writing content to a file on the real disk.
|
|
87
|
+
* It creates a backup before writing and then triggers a project re-indexing.
|
|
88
|
+
* @param {object} params - The parameters for the tool.
|
|
89
|
+
* @param {string} params.filePath - The relative path where the file should be saved.
|
|
90
|
+
* @param {string} params.content - The content to write to the file.
|
|
91
|
+
* @returns {Promise<string>} A message indicating success or failure.
|
|
92
|
+
*/
|
|
72
93
|
exports.safeWriteFileTool = (0, tools_1.tool)(async ({ filePath, content }) => {
|
|
94
|
+
log.debug(`safe_write_file called with filePath: ${filePath}`);
|
|
73
95
|
try {
|
|
74
96
|
const rootDir = process.cwd();
|
|
97
|
+
log.debug(`Current working directory: ${rootDir}`);
|
|
75
98
|
const targetPath = path.resolve(rootDir, filePath);
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
log.debug(`Resolved target path: ${targetPath}`);
|
|
100
|
+
// Security check: Ensure the path is within the project root
|
|
101
|
+
if (!targetPath.startsWith(rootDir)) {
|
|
102
|
+
log.error(`Attempted write outside root directory: ${filePath}. Resolved path: ${targetPath}`);
|
|
103
|
+
return '❌ Error: Access denied. Cannot write outside the project root.';
|
|
104
|
+
}
|
|
78
105
|
const dir = path.dirname(targetPath);
|
|
79
|
-
|
|
106
|
+
log.debug(`Directory for target path: ${dir}`);
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
log.debug(`Directory does not exist. Creating: ${dir}`);
|
|
80
109
|
fs.mkdirSync(dir, { recursive: true });
|
|
81
|
-
|
|
110
|
+
log.sys(`Created directory: ${dir}`);
|
|
111
|
+
}
|
|
112
|
+
createBackup(filePath); // Create backup before writing
|
|
113
|
+
log.debug(`Writing content to file: ${targetPath}`);
|
|
82
114
|
fs.writeFileSync(targetPath, content, 'utf-8');
|
|
83
|
-
log.sys(`
|
|
115
|
+
log.sys(`File saved to REAL DISK: ${filePath}`);
|
|
116
|
+
// Trigger re-indexing after a successful write
|
|
117
|
+
log.sys(`Initiating re-index for: ${filePath}`);
|
|
84
118
|
const indexer = new indexer_1.IndexerService();
|
|
85
|
-
|
|
119
|
+
// Run indexing asynchronously, log errors but don't block the write confirmation
|
|
120
|
+
indexer.indexProject().catch((err) => {
|
|
121
|
+
log.error(`Failed to re-index after write for ${filePath}: ${err.message}`);
|
|
122
|
+
});
|
|
86
123
|
return `✅ File saved to REAL DISK: ${filePath}`;
|
|
87
124
|
}
|
|
88
125
|
catch (error) {
|
|
89
|
-
|
|
126
|
+
log.error(`Failed to write file ${filePath}: ${error.message}`);
|
|
127
|
+
return `❌ Error writing file: ${error.message}`;
|
|
90
128
|
}
|
|
91
129
|
}, {
|
|
92
130
|
name: 'safe_write_file',
|
|
@@ -96,17 +134,36 @@ exports.safeWriteFileTool = (0, tools_1.tool)(async ({ filePath, content }) => {
|
|
|
96
134
|
content: zod_1.z.string().describe('Full file content'),
|
|
97
135
|
}),
|
|
98
136
|
});
|
|
99
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Tool for safely reading the content of a file from the real disk.
|
|
139
|
+
* @param {object} params - The parameters for the tool.
|
|
140
|
+
* @param {string} params.filePath - The relative path of the file to read.
|
|
141
|
+
* @returns {Promise<string>} The content of the file, or an error message if reading fails.
|
|
142
|
+
*/
|
|
100
143
|
exports.safeReadFileTool = (0, tools_1.tool)(async ({ filePath }) => {
|
|
144
|
+
log.debug(`safe_read_file called with filePath: ${filePath}`);
|
|
101
145
|
try {
|
|
102
146
|
const rootDir = process.cwd();
|
|
147
|
+
log.debug(`Current working directory: ${rootDir}`);
|
|
103
148
|
const targetPath = path.resolve(rootDir, filePath);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
149
|
+
log.debug(`Resolved target path: ${targetPath}`);
|
|
150
|
+
// Security check: Ensure the path is within the project root
|
|
151
|
+
if (!fs.existsSync(targetPath)) {
|
|
152
|
+
log.error(`File not found for reading: ${filePath}. Resolved path: ${targetPath}`);
|
|
153
|
+
return `❌ File not found: ${filePath}`;
|
|
154
|
+
}
|
|
155
|
+
if (!targetPath.startsWith(rootDir)) {
|
|
156
|
+
log.error(`Attempted read outside root directory: ${filePath}. Resolved path: ${targetPath}`);
|
|
157
|
+
return '❌ Error: Access denied. Cannot read outside the project root.';
|
|
158
|
+
}
|
|
159
|
+
log.debug(`Reading file content from: ${targetPath}`);
|
|
160
|
+
const content = fs.readFileSync(targetPath, 'utf-8');
|
|
161
|
+
log.sys(`File read successfully: ${filePath}`);
|
|
162
|
+
return content;
|
|
107
163
|
}
|
|
108
164
|
catch (e) {
|
|
109
|
-
|
|
165
|
+
log.error(`Failed to read file ${filePath}: ${e.message}`);
|
|
166
|
+
return `❌ Error reading file: ${e.message}`;
|
|
110
167
|
}
|
|
111
168
|
}, {
|
|
112
169
|
name: 'safe_read_file',
|
|
@@ -114,18 +171,27 @@ exports.safeReadFileTool = (0, tools_1.tool)(async ({ filePath }) => {
|
|
|
114
171
|
schema: zod_1.z.object({ filePath: zod_1.z.string() }),
|
|
115
172
|
});
|
|
116
173
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
174
|
+
* Tool to query the codebase using semantic search and dependency graph analysis.
|
|
175
|
+
* It's the primary way for the agent to explore and understand the project structure and logic.
|
|
176
|
+
* @param {object} params - The parameters for the tool.
|
|
177
|
+
* @param {string} params.query - A natural language query describing the code or functionality to find.
|
|
178
|
+
* @returns {Promise<string>} A report containing relevant code snippets, file paths, and dependencies.
|
|
119
179
|
*/
|
|
120
180
|
exports.askCodebaseTool = (0, tools_1.tool)(async ({ query }) => {
|
|
181
|
+
log.debug(`ask_codebase called with query: "${query}"`);
|
|
121
182
|
try {
|
|
122
|
-
|
|
183
|
+
log.tool(`Querying codebase: "${query}"`);
|
|
123
184
|
const retriever = new retriever_1.RetrieverService();
|
|
185
|
+
log.debug('RetrieverService instantiated.');
|
|
124
186
|
const context = await retriever.getContextForLLM(query);
|
|
187
|
+
log.tool(`Codebase query complete for: "${query}"`);
|
|
188
|
+
log.debug(`Context retrieved for query "${query}".`);
|
|
125
189
|
return context;
|
|
126
190
|
}
|
|
127
191
|
catch (error) {
|
|
128
|
-
|
|
192
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
193
|
+
log.error(`Error during codebase query "${query}": ${errorMessage}`);
|
|
194
|
+
return `❌ Error querying codebase: ${errorMessage}`;
|
|
129
195
|
}
|
|
130
196
|
}, {
|
|
131
197
|
name: 'ask_codebase',
|
|
@@ -141,20 +207,32 @@ exports.askCodebaseTool = (0, tools_1.tool)(async ({ query }) => {
|
|
|
141
207
|
}),
|
|
142
208
|
});
|
|
143
209
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
210
|
+
* Tool to run the TypeScript compiler (tsc) for type checking.
|
|
211
|
+
* This is crucial for maintaining code quality and catching errors early.
|
|
212
|
+
* @returns {Promise<string>} A message indicating whether the integrity check passed or failed, including compiler output on failure.
|
|
146
213
|
*/
|
|
147
214
|
exports.integrityCheckTool = (0, tools_1.tool)(async () => {
|
|
215
|
+
const rootDir = process.cwd();
|
|
216
|
+
log.tool('Running TypeScript integrity check...');
|
|
217
|
+
log.debug(`Integrity check running in directory: ${rootDir}`);
|
|
148
218
|
try {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const { stdout } = await execAsync('npx tsc --noEmit', { cwd: rootDir });
|
|
152
|
-
|
|
219
|
+
// 'tsc --noEmit' checks types without generating JS files. It's fast and safe.
|
|
220
|
+
log.debug('Executing command: npx tsc --noEmit');
|
|
221
|
+
const { stdout, stderr } = await execAsync('npx tsc --noEmit', { cwd: rootDir });
|
|
222
|
+
if (stderr) {
|
|
223
|
+
// Log stderr as an error even if stdout indicates success, as tsc might output warnings here
|
|
224
|
+
log.error(`TypeScript integrity check produced stderr output:\n${stderr}`);
|
|
225
|
+
}
|
|
226
|
+
log.tool('TypeScript integrity check PASSED.');
|
|
227
|
+
log.debug(`Integrity check stdout:\n${stdout}`);
|
|
228
|
+
// Include stdout in the success message for completeness, though it's usually empty on success.
|
|
153
229
|
return `✅ INTEGRITY CHECK PASSED. The codebase is strictly typed and compiles correctly.\n${stdout}`;
|
|
154
230
|
}
|
|
155
231
|
catch (error) {
|
|
156
|
-
// Return the exact compiler error so the agent can fix it
|
|
157
|
-
|
|
232
|
+
// Return the exact compiler error output so the agent can attempt to fix it
|
|
233
|
+
const errorMessage = error.stdout || error.stderr || error.message || 'Unknown error';
|
|
234
|
+
log.error(`TypeScript integrity check FAILED.\n${errorMessage}`);
|
|
235
|
+
return `❌ INTEGRITY CHECK FAILED. You must fix these TypeScript errors before finishing:\n${errorMessage}`;
|
|
158
236
|
}
|
|
159
237
|
}, {
|
|
160
238
|
name: 'run_integrity_check',
|
|
@@ -163,30 +241,31 @@ exports.integrityCheckTool = (0, tools_1.tool)(async () => {
|
|
|
163
241
|
schema: zod_1.z.object({}),
|
|
164
242
|
});
|
|
165
243
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* * @returns {Promise<string>} A confirmation message or error details.
|
|
244
|
+
* Tool to refresh the project's index, forcing a re-scan and re-vectorization of all files.
|
|
245
|
+
* This is useful when the agent needs to be absolutely sure it's working with the latest code,
|
|
246
|
+
* especially after significant changes or if the automatic indexing seems to be lagging.
|
|
247
|
+
* @returns {Promise<string>} A confirmation message or details about any errors encountered during indexing.
|
|
171
248
|
*/
|
|
172
249
|
exports.refreshIndexTool = (0, tools_1.tool)(async () => {
|
|
173
250
|
log.sys('🔄 Starting full project re-indexing...');
|
|
174
251
|
try {
|
|
175
|
-
// Start the expensive operation
|
|
176
252
|
const indexer = new indexer_1.IndexerService();
|
|
177
|
-
|
|
253
|
+
log.debug('IndexerService instantiated.');
|
|
254
|
+
// Execute the indexing process.
|
|
255
|
+
await indexer.indexProject(); // Await the completion of the indexing process
|
|
178
256
|
log.sys('✅ Re-indexing completed successfully.');
|
|
257
|
+
log.debug('Project re-indexing process finished.');
|
|
179
258
|
return '✅ Index successfully updated. I now have access to the latest code version.';
|
|
180
259
|
}
|
|
181
260
|
catch (error) {
|
|
182
|
-
//
|
|
261
|
+
// Provide a detailed error message if indexing fails.
|
|
183
262
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
184
263
|
log.error(`❌ Indexing failed: ${errorMessage}`);
|
|
264
|
+
log.debug(`Error details during indexing: ${errorMessage}`);
|
|
185
265
|
return `❌ Critical error while attempting to index the project: ${errorMessage}. Please try again or check the logs.`;
|
|
186
266
|
}
|
|
187
267
|
}, {
|
|
188
268
|
name: 'refresh_project_index',
|
|
189
|
-
// CRITICAL IMPROVEMENT: Instruction-oriented description for the LLM
|
|
190
269
|
description: 'Triggers a forced, full re-indexing of the project codebase. That fucntion is optimazed only index changes comparing hash' +
|
|
191
270
|
'USE THIS TOOL ONLY WHEN: ' +
|
|
192
271
|
'1) The user explicitly states that files have changed. ' +
|
package/package.json
CHANGED