@imenam/refactoring-mcp 3.2.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +83 -1
- package/dist/utils/config.js +3 -0
- package/dist/utils/files.js +25 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { setupLogging } from "./logger.js";
|
|
7
|
-
import { executeRefactor } from "./utils/files.js";
|
|
7
|
+
import { executeRefactor, insertTextAtLine } from "./utils/files.js";
|
|
8
8
|
import { getFileOutline } from "./utils/outline.js";
|
|
9
9
|
import { compareCodeSections } from "./utils/compare.js";
|
|
10
10
|
setupLogging("MASTER");
|
|
@@ -57,6 +57,24 @@ const OutlineSchema = z.object({
|
|
|
57
57
|
.optional()
|
|
58
58
|
.describe("Optionnel : dernière ligne à analyser (1-indexé). Utile pour cibler une section du fichier. Par défaut : fin du fichier."),
|
|
59
59
|
});
|
|
60
|
+
const InsertTextSchema = z.object({
|
|
61
|
+
filePath: z
|
|
62
|
+
.string()
|
|
63
|
+
.describe("Chemin absolu complet du fichier cible"),
|
|
64
|
+
insertions: z
|
|
65
|
+
.array(z.object({
|
|
66
|
+
lineNumber: z
|
|
67
|
+
.number()
|
|
68
|
+
.int()
|
|
69
|
+
.positive()
|
|
70
|
+
.describe("Numéro de la ligne devant laquelle le texte sera inséré (1-indexé). Utiliser fileLength + 1 pour insérer en fin de fichier."),
|
|
71
|
+
text: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("Texte à insérer. Peut contenir plusieurs lignes (\\n). Ne pas terminer par \\n : le saut de ligne final est géré automatiquement par l'outil."),
|
|
74
|
+
}))
|
|
75
|
+
.min(1)
|
|
76
|
+
.describe("Liste des insertions à effectuer. Les insertions sont appliquées du bas vers le haut pour éviter les décalages d'offset. Les numéros de lignes doivent référencer le fichier dans son état ORIGINAL (avant toute insertion)."),
|
|
77
|
+
});
|
|
60
78
|
const CompareSchema = z.object({
|
|
61
79
|
fileA: z.string().describe("Chemin absolu complet du premier fichier"),
|
|
62
80
|
startLineA: z
|
|
@@ -83,6 +101,44 @@ const CompareSchema = z.object({
|
|
|
83
101
|
});
|
|
84
102
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
85
103
|
tools: [
|
|
104
|
+
{
|
|
105
|
+
name: "insert_text_at_line",
|
|
106
|
+
description: "Insère du texte (potentiellement multiligne) à une ou plusieurs lignes données dans un fichier. " +
|
|
107
|
+
"Accepte un tableau d'insertions { lineNumber, text }. " +
|
|
108
|
+
"Les insertions sont appliquées du bas vers le haut pour éviter les décalages d'offset. " +
|
|
109
|
+
"Les numéros de lignes doivent référencer le fichier dans son état original (avant toute insertion). " +
|
|
110
|
+
"Retourne le nombre total de lignes insérées. " +
|
|
111
|
+
"Le chemin doit être absolu.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
filePath: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Chemin absolu complet du fichier cible",
|
|
118
|
+
},
|
|
119
|
+
insertions: {
|
|
120
|
+
type: "array",
|
|
121
|
+
description: "Liste des insertions à effectuer. Les numéros de lignes référencent le fichier dans son état original.",
|
|
122
|
+
items: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
lineNumber: {
|
|
126
|
+
type: "number",
|
|
127
|
+
description: "Numéro de la ligne devant laquelle le texte sera inséré (1-indexé). Utiliser fileLength + 1 pour insérer en fin de fichier.",
|
|
128
|
+
},
|
|
129
|
+
text: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Texte à insérer. Peut contenir plusieurs lignes (\\n). Ne pas terminer par \\n : le saut de ligne final est géré automatiquement par l'outil.",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
required: ["lineNumber", "text"],
|
|
135
|
+
},
|
|
136
|
+
minItems: 1,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
required: ["filePath", "insertions"],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
86
142
|
{
|
|
87
143
|
name: "move_or_copy_code",
|
|
88
144
|
description: "Copie ou déplace un bloc de lignes de code d'un fichier source vers un fichier de destination. " +
|
|
@@ -219,6 +275,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
219
275
|
};
|
|
220
276
|
}
|
|
221
277
|
}
|
|
278
|
+
if (name === "insert_text_at_line") {
|
|
279
|
+
try {
|
|
280
|
+
const validatedArgs = InsertTextSchema.parse(args);
|
|
281
|
+
const result = await insertTextAtLine(validatedArgs);
|
|
282
|
+
return {
|
|
283
|
+
content: [
|
|
284
|
+
{
|
|
285
|
+
type: "text",
|
|
286
|
+
text: `Succès : ${result.linesInserted} ligne(s) insérée(s) (${validatedArgs.insertions.length} insertion(s)).\n` +
|
|
287
|
+
`- Fichier : ${result.fileLength} lignes après insertion`,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
return {
|
|
294
|
+
content: [
|
|
295
|
+
{
|
|
296
|
+
type: "text",
|
|
297
|
+
text: `Erreur : ${error.message}`,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
isError: true,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
222
304
|
if (name === "move_or_copy_code") {
|
|
223
305
|
try {
|
|
224
306
|
const validatedArgs = RefactorSchema.parse(args);
|
package/dist/utils/config.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
// TEST INSERT A - début du fichier
|
|
1
2
|
import path from "path";
|
|
2
3
|
import { fileURLToPath } from "url";
|
|
3
4
|
import dotenv from "dotenv";
|
|
4
5
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
5
6
|
const envPath = path.join(rootDir, ".env");
|
|
7
|
+
// TEST INSERT B - avant getConfig
|
|
8
|
+
// ligne 2 du commentaire B
|
|
6
9
|
export function getConfig() {
|
|
7
10
|
dotenv.config({ path: envPath, override: true });
|
|
8
11
|
const rawDir = process.env.REFACTORING_ALLOWED_DIR;
|
package/dist/utils/files.js
CHANGED
|
@@ -94,3 +94,28 @@ export async function executeRefactor(params) {
|
|
|
94
94
|
destLength: destLines.length,
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
+
export async function insertTextAtLine(params) {
|
|
98
|
+
const resolvedPath = await validateAndResolvePath(params.filePath);
|
|
99
|
+
await checkFileSize(resolvedPath);
|
|
100
|
+
const content = await fs.readFile(resolvedPath, "utf-8");
|
|
101
|
+
const fileLines = content.split(/\r?\n/);
|
|
102
|
+
const originalLength = fileLines.length;
|
|
103
|
+
for (const insertion of params.insertions) {
|
|
104
|
+
if (insertion.lineNumber < 1 || insertion.lineNumber > originalLength + 1) {
|
|
105
|
+
throw new Error(`Numéro de ligne invalide (demandé: ${insertion.lineNumber}, fichier de ${originalLength} lignes — valeurs acceptées : 1 à ${originalLength + 1})`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Tri du bas vers le haut pour éviter les décalages d'offset
|
|
109
|
+
const sorted = [...params.insertions].sort((a, b) => b.lineNumber - a.lineNumber);
|
|
110
|
+
let totalLinesInserted = 0;
|
|
111
|
+
for (const insertion of sorted) {
|
|
112
|
+
const newLines = insertion.text.split(/\r?\n/);
|
|
113
|
+
fileLines.splice(insertion.lineNumber - 1, 0, ...newLines);
|
|
114
|
+
totalLinesInserted += newLines.length;
|
|
115
|
+
}
|
|
116
|
+
await fs.writeFile(resolvedPath, fileLines.join("\n"), "utf-8");
|
|
117
|
+
return {
|
|
118
|
+
linesInserted: totalLinesInserted,
|
|
119
|
+
fileLength: fileLines.length,
|
|
120
|
+
};
|
|
121
|
+
}
|
package/package.json
CHANGED