errase 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 +21 -0
- package/dist/index.js +185 -0
- package/package.json +47 -0
- package/src/index.ts +191 -0
- package/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ERRase CLI
|
|
2
|
+
|
|
3
|
+
AI-powered browser debugging. Detects runtime errors, reads your source files, and applies fixes automatically.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx errase /path/to/your/project
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
1. Install the [ERRase Chrome Extension](#)
|
|
14
|
+
2. Get a free [Groq API key](https://console.groq.com)
|
|
15
|
+
3. Enter your key in the extension popup
|
|
16
|
+
4. Run `npx errase` in your project directory
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js 18+
|
|
21
|
+
- Next.js project (more frameworks coming soon)
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const ws_1 = require("ws");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const source_map_js_1 = require("source-map-js");
|
|
12
|
+
const projectRoot = process.argv[2]
|
|
13
|
+
? path_1.default.resolve(process.argv[2])
|
|
14
|
+
: process.cwd();
|
|
15
|
+
const app = (0, express_1.default)();
|
|
16
|
+
const server = app.listen(7891, () => [
|
|
17
|
+
console.log("[ERRase CLI] Running on port 7891"),
|
|
18
|
+
console.log("[ERRase CLI] Project root:", projectRoot)
|
|
19
|
+
]);
|
|
20
|
+
const wss = new ws_1.WebSocketServer({ server });
|
|
21
|
+
// Extracts all bundled JS URLs + line/col from a raw browser stack trace
|
|
22
|
+
function parseBundledStack(stack) {
|
|
23
|
+
const frames = [];
|
|
24
|
+
for (const line of stack.split(/\r?\n/)) {
|
|
25
|
+
const match = line.match(/\(?(https?:\/\/.+?\.js):(\d+):(\d+)\)?/);
|
|
26
|
+
if (match)
|
|
27
|
+
frames.push({ bundledUrl: match[1], line: parseInt(match[2]), column: parseInt(match[3]) });
|
|
28
|
+
}
|
|
29
|
+
return frames;
|
|
30
|
+
}
|
|
31
|
+
// Iterates all stack frames, resolves source maps, and returns the first frame
|
|
32
|
+
// that maps to a project file (skipping node_modules frames).
|
|
33
|
+
async function resolveToProjectFile(stack) {
|
|
34
|
+
const frames = parseBundledStack(stack);
|
|
35
|
+
for (const frame of frames) {
|
|
36
|
+
let rawMap;
|
|
37
|
+
try {
|
|
38
|
+
rawMap = await fetchSourceMap(frame.bundledUrl);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const consumer = new source_map_js_1.SourceMapConsumer(rawMap);
|
|
44
|
+
const original = consumer.originalPositionFor({ line: frame.line, column: frame.column });
|
|
45
|
+
if (!original.source || original.line == null)
|
|
46
|
+
continue;
|
|
47
|
+
if (original.source.includes("node_modules"))
|
|
48
|
+
continue;
|
|
49
|
+
const filename = stripSourcePrefix(original.source);
|
|
50
|
+
if (filename.includes("node_modules"))
|
|
51
|
+
continue;
|
|
52
|
+
return { filename, lineNumber: original.line };
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
async function fetchSourceMap(bundledUrl) {
|
|
57
|
+
const res = await fetch(bundledUrl + ".map");
|
|
58
|
+
if (!res.ok)
|
|
59
|
+
throw new Error(`Source map not found: ${bundledUrl}.map (${res.status})`);
|
|
60
|
+
return res.json();
|
|
61
|
+
}
|
|
62
|
+
// Strips webpack:// or file:// prefixes from source map source paths, returning
|
|
63
|
+
// a path relative to projectRoot suitable for disk reads.
|
|
64
|
+
// e.g. webpack://_N_E/./app/page.tsx → app/page.tsx
|
|
65
|
+
// file:///C:/path/to/project/app/page.tsx → app/page.tsx
|
|
66
|
+
function stripSourcePrefix(source) {
|
|
67
|
+
const webpackMatch = source.match(/^webpack:\/\/[^/]*\/\.\/(.+)$/) ?? source.match(/^webpack:\/\/[^/]*\/(.+)$/);
|
|
68
|
+
if (webpackMatch)
|
|
69
|
+
return webpackMatch[1];
|
|
70
|
+
if (source.startsWith("file://")) {
|
|
71
|
+
const decoded = decodeURIComponent(source);
|
|
72
|
+
// file:///C:/... on Windows, file:///... on Unix
|
|
73
|
+
const absolute = decoded.replace(/^file:\/\/\//, "").replace(/^file:\/\//, "");
|
|
74
|
+
return path_1.default.relative(projectRoot, absolute);
|
|
75
|
+
}
|
|
76
|
+
return source;
|
|
77
|
+
}
|
|
78
|
+
function readContextLines(filename, lineNumber, context = 15) {
|
|
79
|
+
const fullPath = path_1.default.join(projectRoot, filename);
|
|
80
|
+
if (!fs_1.default.existsSync(fullPath))
|
|
81
|
+
throw new Error(`File not found: ${fullPath}`);
|
|
82
|
+
const lines = fs_1.default.readFileSync(fullPath, "utf-8").split("\n");
|
|
83
|
+
const start = Math.max(0, lineNumber - 1 - context);
|
|
84
|
+
const end = Math.min(lines.length, lineNumber + context);
|
|
85
|
+
return lines.slice(start, end).map((l, i) => `${start + i + 1}: ${l}`).join("\n");
|
|
86
|
+
}
|
|
87
|
+
wss.on("connection", (socket) => {
|
|
88
|
+
console.log("[ERRase CLI] Extension connected");
|
|
89
|
+
socket.on("message", async (data) => {
|
|
90
|
+
let message;
|
|
91
|
+
try {
|
|
92
|
+
message = JSON.parse(data.toString());
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
console.log("[ERRase CLI] Could not parse message, skipping");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (message.type === "ERROR_DETECTED") {
|
|
99
|
+
console.log("[ERRase CLI] Error received:");
|
|
100
|
+
console.log(" Message:", message.message);
|
|
101
|
+
console.log(" URL:", message.url);
|
|
102
|
+
let resolved = null;
|
|
103
|
+
try {
|
|
104
|
+
resolved = await resolveToProjectFile(message.stack);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
console.log("[ERRase CLI] Source map resolution failed:", err);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!resolved) {
|
|
111
|
+
console.log("[ERRase CLI] Could not resolve stack to a project file (all frames are node_modules or unmapped)");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const { filename, lineNumber } = resolved;
|
|
115
|
+
console.log(`[ERRase CLI] Mapped to: ${filename}:${lineNumber}`);
|
|
116
|
+
try {
|
|
117
|
+
const snippet = readContextLines(filename, lineNumber);
|
|
118
|
+
console.log(`[ERRase CLI] Context (±30 lines around line ${lineNumber}):\n${snippet}`);
|
|
119
|
+
socket.send(JSON.stringify({
|
|
120
|
+
type: "ERROR_CONTEXT",
|
|
121
|
+
filename,
|
|
122
|
+
lineNumber,
|
|
123
|
+
message: message.message,
|
|
124
|
+
stack: message.stack,
|
|
125
|
+
fileContent: snippet
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.log("[ERRase CLI] Could not read source file:", err);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (message.type === "ANALYZE_REQUEST") {
|
|
133
|
+
console.log("[ERRase CLI] Analyze request received:");
|
|
134
|
+
console.log(" File:", message.filename);
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetch("https://errase-production.up.railway.app/analyze", {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: { "Content-Type": "application/json" },
|
|
139
|
+
body: JSON.stringify({
|
|
140
|
+
message: message.message,
|
|
141
|
+
filename: message.filename,
|
|
142
|
+
lineNumber: message.lineNumber,
|
|
143
|
+
fileContents: message.fileContents,
|
|
144
|
+
groqApiKey: message.groqApiKey
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
const fix = await response.json();
|
|
148
|
+
console.log("[ERRase Backend response]:", JSON.stringify(fix, null, 2));
|
|
149
|
+
socket.send(JSON.stringify({
|
|
150
|
+
type: "FIX_READY",
|
|
151
|
+
explanation: fix.explanation,
|
|
152
|
+
confidence: fix.confidence,
|
|
153
|
+
originalLine: fix.originalLine,
|
|
154
|
+
fixedLine: fix.fixedLine,
|
|
155
|
+
diff: fix.diff,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("[ERRase CLI] Failed to reach backend:", error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (message.type === "APPLY_FIX") {
|
|
163
|
+
console.log("[ERRase CLI] Applying fix to:", message.filename);
|
|
164
|
+
try {
|
|
165
|
+
const fullPath = path_1.default.join(projectRoot, message.filename);
|
|
166
|
+
const fileContent = fs_1.default.readFileSync(fullPath, "utf-8");
|
|
167
|
+
if (!fileContent.includes(message.originalLine)) {
|
|
168
|
+
console.error("[ERRase CLI] originalLine not found in file");
|
|
169
|
+
console.error("[ERRase CLI] Searched for:", JSON.stringify(message.originalLine));
|
|
170
|
+
console.error("[ERRase CLI] File start:", fileContent.slice(0, 500));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const updated = fileContent.replace(message.originalLine, message.fixedLine);
|
|
174
|
+
fs_1.default.writeFileSync(fullPath, updated, "utf-8");
|
|
175
|
+
console.log("[ERRase CLI] Fix applied to:", fullPath);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error("[ERRase CLI] Failed to apply fix:", error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
socket.on("close", () => {
|
|
183
|
+
console.log("[ERRase CLI] Extension disconnected");
|
|
184
|
+
});
|
|
185
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "errase",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"errase": "dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"dev": "nodemon --exec ts-node src/index.ts",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"debugging",
|
|
16
|
+
"ai",
|
|
17
|
+
"chrome-extension",
|
|
18
|
+
"developer-tools",
|
|
19
|
+
"nextjs"
|
|
20
|
+
],
|
|
21
|
+
"author": "Craig Rosario",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"description": "AI-powered browser debugging assistant. Detects runtime errors, reads your source files, and applies AI-generated fixes without leaving the tab.",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"diff": "^9.0.0",
|
|
26
|
+
"express": "^5.2.1",
|
|
27
|
+
"source-map-js": "^1.2.1",
|
|
28
|
+
"ws": "^8.20.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/diff": "^7.0.2",
|
|
32
|
+
"@types/express": "^5.0.6",
|
|
33
|
+
"@types/node": "^25.9.1",
|
|
34
|
+
"@types/ws": "^8.18.1",
|
|
35
|
+
"nodemon": "^3.1.14",
|
|
36
|
+
"ts-node": "^10.9.2",
|
|
37
|
+
"typescript": "^6.0.3"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://errase.vercel.app",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/Craig-Rosario/ERRase"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/Craig-Rosario/ERRase/issues"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import express from "express"
|
|
3
|
+
import { WebSocketServer } from "ws"
|
|
4
|
+
import fs from "fs"
|
|
5
|
+
import path from "path"
|
|
6
|
+
import { SourceMapConsumer } from "source-map-js"
|
|
7
|
+
|
|
8
|
+
const projectRoot = process.argv[2]
|
|
9
|
+
? path.resolve(process.argv[2])
|
|
10
|
+
: process.cwd()
|
|
11
|
+
|
|
12
|
+
const app = express();
|
|
13
|
+
|
|
14
|
+
const server = app.listen(7891, () => [
|
|
15
|
+
console.log("[ERRase CLI] Running on port 7891"),
|
|
16
|
+
console.log("[ERRase CLI] Project root:", projectRoot)
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
const wss = new WebSocketServer({ server });
|
|
20
|
+
|
|
21
|
+
// Extracts all bundled JS URLs + line/col from a raw browser stack trace
|
|
22
|
+
function parseBundledStack(stack: string): Array<{ bundledUrl: string; line: number; column: number }> {
|
|
23
|
+
const frames: Array<{ bundledUrl: string; line: number; column: number }> = []
|
|
24
|
+
for (const line of stack.split(/\r?\n/)) {
|
|
25
|
+
const match = line.match(/\(?(https?:\/\/.+?\.js):(\d+):(\d+)\)?/)
|
|
26
|
+
if (match) frames.push({ bundledUrl: match[1], line: parseInt(match[2]), column: parseInt(match[3]) })
|
|
27
|
+
}
|
|
28
|
+
return frames
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Iterates all stack frames, resolves source maps, and returns the first frame
|
|
32
|
+
// that maps to a project file (skipping node_modules frames).
|
|
33
|
+
async function resolveToProjectFile(stack: string): Promise<{ filename: string; lineNumber: number } | null> {
|
|
34
|
+
const frames = parseBundledStack(stack)
|
|
35
|
+
for (const frame of frames) {
|
|
36
|
+
let rawMap: any
|
|
37
|
+
try {
|
|
38
|
+
rawMap = await fetchSourceMap(frame.bundledUrl)
|
|
39
|
+
} catch {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
const consumer = new SourceMapConsumer(rawMap)
|
|
43
|
+
const original = consumer.originalPositionFor({ line: frame.line, column: frame.column })
|
|
44
|
+
if (!original.source || original.line == null) continue
|
|
45
|
+
if (original.source.includes("node_modules")) continue
|
|
46
|
+
const filename = stripSourcePrefix(original.source)
|
|
47
|
+
if (filename.includes("node_modules")) continue
|
|
48
|
+
return { filename, lineNumber: original.line }
|
|
49
|
+
}
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function fetchSourceMap(bundledUrl: string): Promise<any> {
|
|
54
|
+
const res = await fetch(bundledUrl + ".map")
|
|
55
|
+
if (!res.ok) throw new Error(`Source map not found: ${bundledUrl}.map (${res.status})`)
|
|
56
|
+
return res.json()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Strips webpack:// or file:// prefixes from source map source paths, returning
|
|
60
|
+
// a path relative to projectRoot suitable for disk reads.
|
|
61
|
+
// e.g. webpack://_N_E/./app/page.tsx → app/page.tsx
|
|
62
|
+
// file:///C:/path/to/project/app/page.tsx → app/page.tsx
|
|
63
|
+
function stripSourcePrefix(source: string): string {
|
|
64
|
+
const webpackMatch = source.match(/^webpack:\/\/[^/]*\/\.\/(.+)$/) ?? source.match(/^webpack:\/\/[^/]*\/(.+)$/)
|
|
65
|
+
if (webpackMatch) return webpackMatch[1]
|
|
66
|
+
|
|
67
|
+
if (source.startsWith("file://")) {
|
|
68
|
+
const decoded = decodeURIComponent(source)
|
|
69
|
+
// file:///C:/... on Windows, file:///... on Unix
|
|
70
|
+
const absolute = decoded.replace(/^file:\/\/\//, "").replace(/^file:\/\//, "")
|
|
71
|
+
return path.relative(projectRoot, absolute)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return source
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readContextLines(filename: string, lineNumber: number, context = 15): string {
|
|
78
|
+
const fullPath = path.join(projectRoot, filename)
|
|
79
|
+
if (!fs.existsSync(fullPath)) throw new Error(`File not found: ${fullPath}`)
|
|
80
|
+
const lines = fs.readFileSync(fullPath, "utf-8").split("\n")
|
|
81
|
+
const start = Math.max(0, lineNumber - 1 - context)
|
|
82
|
+
const end = Math.min(lines.length, lineNumber + context)
|
|
83
|
+
return lines.slice(start, end).map((l, i) => `${start + i + 1}: ${l}`).join("\n")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
wss.on("connection", (socket) => {
|
|
87
|
+
console.log("[ERRase CLI] Extension connected")
|
|
88
|
+
|
|
89
|
+
socket.on("message", async (data) => {
|
|
90
|
+
let message: any
|
|
91
|
+
try {
|
|
92
|
+
message = JSON.parse(data.toString())
|
|
93
|
+
} catch {
|
|
94
|
+
console.log("[ERRase CLI] Could not parse message, skipping")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (message.type === "ERROR_DETECTED") {
|
|
99
|
+
console.log("[ERRase CLI] Error received:")
|
|
100
|
+
console.log(" Message:", message.message)
|
|
101
|
+
console.log(" URL:", message.url)
|
|
102
|
+
|
|
103
|
+
let resolved: { filename: string; lineNumber: number } | null = null
|
|
104
|
+
try {
|
|
105
|
+
resolved = await resolveToProjectFile(message.stack)
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.log("[ERRase CLI] Source map resolution failed:", err)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!resolved) {
|
|
112
|
+
console.log("[ERRase CLI] Could not resolve stack to a project file (all frames are node_modules or unmapped)")
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { filename, lineNumber } = resolved
|
|
117
|
+
console.log(`[ERRase CLI] Mapped to: ${filename}:${lineNumber}`)
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const snippet = readContextLines(filename, lineNumber)
|
|
121
|
+
console.log(`[ERRase CLI] Context (±30 lines around line ${lineNumber}):\n${snippet}`)
|
|
122
|
+
|
|
123
|
+
socket.send(JSON.stringify({
|
|
124
|
+
type: "ERROR_CONTEXT",
|
|
125
|
+
filename,
|
|
126
|
+
lineNumber,
|
|
127
|
+
message: message.message,
|
|
128
|
+
stack: message.stack,
|
|
129
|
+
fileContent: snippet
|
|
130
|
+
}))
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.log("[ERRase CLI] Could not read source file:", err)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (message.type === "ANALYZE_REQUEST") {
|
|
137
|
+
console.log("[ERRase CLI] Analyze request received:")
|
|
138
|
+
console.log(" File:", message.filename)
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetch("https://errase-production.up.railway.app/analyze", {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: { "Content-Type": "application/json" },
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
message: message.message,
|
|
146
|
+
filename: message.filename,
|
|
147
|
+
lineNumber: message.lineNumber,
|
|
148
|
+
fileContents: message.fileContents,
|
|
149
|
+
groqApiKey: message.groqApiKey
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
const fix = await response.json() as any;
|
|
153
|
+
console.log("[ERRase Backend response]:", JSON.stringify(fix, null, 2))
|
|
154
|
+
socket.send(JSON.stringify({
|
|
155
|
+
type: "FIX_READY",
|
|
156
|
+
explanation: fix.explanation,
|
|
157
|
+
confidence: fix.confidence,
|
|
158
|
+
originalLine: fix.originalLine,
|
|
159
|
+
fixedLine: fix.fixedLine,
|
|
160
|
+
diff: fix.diff,
|
|
161
|
+
}))
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error("[ERRase CLI] Failed to reach backend:", error)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (message.type === "APPLY_FIX") {
|
|
169
|
+
console.log("[ERRase CLI] Applying fix to:", message.filename)
|
|
170
|
+
try {
|
|
171
|
+
const fullPath = path.join(projectRoot, message.filename)
|
|
172
|
+
const fileContent = fs.readFileSync(fullPath, "utf-8")
|
|
173
|
+
if (!fileContent.includes(message.originalLine)) {
|
|
174
|
+
console.error("[ERRase CLI] originalLine not found in file")
|
|
175
|
+
console.error("[ERRase CLI] Searched for:", JSON.stringify(message.originalLine))
|
|
176
|
+
console.error("[ERRase CLI] File start:", fileContent.slice(0, 500))
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
const updated = fileContent.replace(message.originalLine, message.fixedLine)
|
|
180
|
+
fs.writeFileSync(fullPath, updated, "utf-8")
|
|
181
|
+
console.log("[ERRase CLI] Fix applied to:", fullPath)
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error("[ERRase CLI] Failed to apply fix:", error)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
socket.on("close", () => {
|
|
189
|
+
console.log("[ERRase CLI] Extension disconnected");
|
|
190
|
+
})
|
|
191
|
+
})
|