@vibetasks/cli 0.1.0 → 0.3.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/dist/bin/vibetasks.js +2654 -0
- package/dist/chunk-2KRLRG4G.js +161 -0
- package/dist/chunk-PZF4VRDG.js +430 -0
- package/dist/daemon-config-EUSBQA4E.js +10 -0
- package/dist/src/daemon-worker.js +198 -0
- package/hooks/sync-todos.js +333 -0
- package/package.json +10 -6
- package/dist/vibetasks.js +0 -1126
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// src/utils/daemon-config.ts
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
var DEFAULT_DAEMON_CONFIG = {
|
|
6
|
+
enabled: true,
|
|
7
|
+
keyboard_shortcut: "Ctrl+Alt+E",
|
|
8
|
+
auto_start: false,
|
|
9
|
+
notification_duration: 5e3,
|
|
10
|
+
auto_create_task: false,
|
|
11
|
+
capture_clipboard: true,
|
|
12
|
+
capture_screen: false,
|
|
13
|
+
// Screen capture requires additional setup
|
|
14
|
+
error_patterns: [
|
|
15
|
+
"Error:",
|
|
16
|
+
"Exception:",
|
|
17
|
+
"TypeError:",
|
|
18
|
+
"ReferenceError:",
|
|
19
|
+
"SyntaxError:",
|
|
20
|
+
"FATAL:",
|
|
21
|
+
"FAILED:",
|
|
22
|
+
"error\\[",
|
|
23
|
+
"panic:",
|
|
24
|
+
"Traceback",
|
|
25
|
+
"at Object\\.",
|
|
26
|
+
"at Module\\.",
|
|
27
|
+
"at async",
|
|
28
|
+
"npm ERR!",
|
|
29
|
+
"yarn error",
|
|
30
|
+
"pnpm ERR!"
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
var DaemonConfigManager = class {
|
|
34
|
+
configDir;
|
|
35
|
+
configPath;
|
|
36
|
+
pidFilePath;
|
|
37
|
+
constructor() {
|
|
38
|
+
const configHome = process.env.XDG_CONFIG_HOME || (process.platform === "win32" ? process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming") : path.join(os.homedir(), ".config"));
|
|
39
|
+
this.configDir = path.join(configHome, "taskflow");
|
|
40
|
+
this.configPath = path.join(this.configDir, "daemon.json");
|
|
41
|
+
this.pidFilePath = path.join(this.configDir, "daemon.pid");
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Ensure the config directory exists
|
|
45
|
+
*/
|
|
46
|
+
async ensureConfigDir() {
|
|
47
|
+
try {
|
|
48
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error.code !== "EEXIST") {
|
|
51
|
+
throw new Error(`Failed to create config directory: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get daemon configuration
|
|
57
|
+
*/
|
|
58
|
+
async getConfig() {
|
|
59
|
+
try {
|
|
60
|
+
const data = await fs.readFile(this.configPath, "utf-8");
|
|
61
|
+
const config = JSON.parse(data);
|
|
62
|
+
return { ...DEFAULT_DAEMON_CONFIG, ...config };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (error.code === "ENOENT") {
|
|
65
|
+
return { ...DEFAULT_DAEMON_CONFIG };
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Failed to read daemon config: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Save daemon configuration
|
|
72
|
+
*/
|
|
73
|
+
async saveConfig(config) {
|
|
74
|
+
await this.ensureConfigDir();
|
|
75
|
+
const currentConfig = await this.getConfig();
|
|
76
|
+
const newConfig = { ...currentConfig, ...config };
|
|
77
|
+
try {
|
|
78
|
+
await fs.writeFile(this.configPath, JSON.stringify(newConfig, null, 2), "utf-8");
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(`Failed to write daemon config: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get PID file path
|
|
85
|
+
*/
|
|
86
|
+
getPidFilePath() {
|
|
87
|
+
return this.pidFilePath;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get config directory path
|
|
91
|
+
*/
|
|
92
|
+
getConfigDir() {
|
|
93
|
+
return this.configDir;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Save daemon PID
|
|
97
|
+
*/
|
|
98
|
+
async savePid(pid) {
|
|
99
|
+
await this.ensureConfigDir();
|
|
100
|
+
await fs.writeFile(this.pidFilePath, pid.toString(), "utf-8");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get daemon PID
|
|
104
|
+
*/
|
|
105
|
+
async getPid() {
|
|
106
|
+
try {
|
|
107
|
+
const data = await fs.readFile(this.pidFilePath, "utf-8");
|
|
108
|
+
return parseInt(data.trim(), 10);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (error.code === "ENOENT") {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Remove PID file
|
|
118
|
+
*/
|
|
119
|
+
async removePid() {
|
|
120
|
+
try {
|
|
121
|
+
await fs.unlink(this.pidFilePath);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error.code !== "ENOENT") {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if daemon is running
|
|
130
|
+
*/
|
|
131
|
+
async isDaemonRunning() {
|
|
132
|
+
const pid = await this.getPid();
|
|
133
|
+
if (!pid) return false;
|
|
134
|
+
try {
|
|
135
|
+
process.kill(pid, 0);
|
|
136
|
+
return true;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
await this.removePid();
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Parse keyboard shortcut string to key combination
|
|
144
|
+
*/
|
|
145
|
+
parseShortcut(shortcut) {
|
|
146
|
+
const parts = shortcut.toLowerCase().split("+").map((p) => p.trim());
|
|
147
|
+
return {
|
|
148
|
+
ctrl: parts.includes("ctrl") || parts.includes("control"),
|
|
149
|
+
alt: parts.includes("alt"),
|
|
150
|
+
shift: parts.includes("shift"),
|
|
151
|
+
key: parts.find((p) => !["ctrl", "control", "alt", "shift"].includes(p)) || ""
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var daemonConfigManager = new DaemonConfigManager();
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
DEFAULT_DAEMON_CONFIG,
|
|
159
|
+
DaemonConfigManager,
|
|
160
|
+
daemonConfigManager
|
|
161
|
+
};
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
// src/utils/clipboard-monitor.ts
|
|
2
|
+
var clipboardy = null;
|
|
3
|
+
async function getClipboardy() {
|
|
4
|
+
if (!clipboardy) {
|
|
5
|
+
clipboardy = await import("clipboardy");
|
|
6
|
+
}
|
|
7
|
+
return clipboardy;
|
|
8
|
+
}
|
|
9
|
+
var nodeJsPatterns = {
|
|
10
|
+
name: "Node.js Errors",
|
|
11
|
+
category: "nodejs",
|
|
12
|
+
patterns: [
|
|
13
|
+
/^(Error|TypeError|ReferenceError|SyntaxError|RangeError|EvalError|URIError):\s*.+/m,
|
|
14
|
+
/^\s+at\s+.+\(.+:\d+:\d+\)/m,
|
|
15
|
+
/^Uncaught\s+(Error|TypeError|ReferenceError):/m,
|
|
16
|
+
/^UnhandledPromiseRejectionWarning:/m,
|
|
17
|
+
/^node:internal\//m
|
|
18
|
+
],
|
|
19
|
+
extractInfo: (text) => {
|
|
20
|
+
const errorMatch = text.match(
|
|
21
|
+
/^(Error|TypeError|ReferenceError|SyntaxError|RangeError|EvalError|URIError):\s*(.+)/m
|
|
22
|
+
);
|
|
23
|
+
if (!errorMatch) return null;
|
|
24
|
+
const stackMatch = text.match(/at\s+(?:.+\s+)?\((.+):(\d+):(\d+)\)/);
|
|
25
|
+
const file = stackMatch?.[1];
|
|
26
|
+
const line = stackMatch ? parseInt(stackMatch[2], 10) : void 0;
|
|
27
|
+
const column = stackMatch ? parseInt(stackMatch[3], 10) : void 0;
|
|
28
|
+
const stackLines = text.split("\n").filter((l) => l.trim().startsWith("at "));
|
|
29
|
+
const stack = stackLines.length > 0 ? stackLines.join("\n") : void 0;
|
|
30
|
+
return {
|
|
31
|
+
type: errorMatch[1],
|
|
32
|
+
category: "nodejs",
|
|
33
|
+
message: errorMatch[2].trim(),
|
|
34
|
+
stack,
|
|
35
|
+
file,
|
|
36
|
+
line,
|
|
37
|
+
column,
|
|
38
|
+
rawText: text
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var npmPatterns = {
|
|
43
|
+
name: "npm Errors",
|
|
44
|
+
category: "npm",
|
|
45
|
+
patterns: [
|
|
46
|
+
/npm ERR!/,
|
|
47
|
+
/npm WARN/,
|
|
48
|
+
/ERESOLVE/,
|
|
49
|
+
/E404/,
|
|
50
|
+
/ENOENT/,
|
|
51
|
+
/EPERM/,
|
|
52
|
+
/EACCES/,
|
|
53
|
+
/peer dep missing/i,
|
|
54
|
+
/Could not resolve dependency/
|
|
55
|
+
],
|
|
56
|
+
extractInfo: (text) => {
|
|
57
|
+
const codeMatch = text.match(/npm ERR! code\s+(\w+)/);
|
|
58
|
+
const code = codeMatch?.[1];
|
|
59
|
+
const summaryMatch = text.match(/npm ERR!\s+(.+)/);
|
|
60
|
+
const message = summaryMatch?.[1] || "npm error detected";
|
|
61
|
+
if (text.includes("ERESOLVE")) {
|
|
62
|
+
const conflictMatch = text.match(/Conflicting peer dependency:\s*(.+)/);
|
|
63
|
+
return {
|
|
64
|
+
type: "ERESOLVE",
|
|
65
|
+
category: "npm",
|
|
66
|
+
message: conflictMatch?.[1] || "Dependency resolution conflict",
|
|
67
|
+
code: "ERESOLVE",
|
|
68
|
+
suggestion: "Try: npm install --legacy-peer-deps or npm install --force",
|
|
69
|
+
rawText: text
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (text.includes("E404") || text.includes("404 Not Found")) {
|
|
73
|
+
const packageMatch = text.match(/Not Found.*?:\s*(.+)/);
|
|
74
|
+
return {
|
|
75
|
+
type: "E404",
|
|
76
|
+
category: "npm",
|
|
77
|
+
message: `Package not found: ${packageMatch?.[1] || "unknown"}`,
|
|
78
|
+
code: "E404",
|
|
79
|
+
suggestion: "Check package name spelling or registry configuration",
|
|
80
|
+
rawText: text
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
type: "npm error",
|
|
85
|
+
category: "npm",
|
|
86
|
+
message,
|
|
87
|
+
code,
|
|
88
|
+
rawText: text
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var expoPatterns = {
|
|
93
|
+
name: "Expo/React Native Errors",
|
|
94
|
+
category: "expo",
|
|
95
|
+
patterns: [
|
|
96
|
+
/Error:\s*Unable to resolve module/,
|
|
97
|
+
/Metro Bundler/i,
|
|
98
|
+
/BUNDLE\s+.*failed/i,
|
|
99
|
+
/Red Box/i,
|
|
100
|
+
/Invariant Violation/,
|
|
101
|
+
/Native module.+cannot be null/,
|
|
102
|
+
/Cannot read property.+of undefined/,
|
|
103
|
+
/expo-.*error/i,
|
|
104
|
+
/RCTFatalException/,
|
|
105
|
+
/ExceptionsManager/
|
|
106
|
+
],
|
|
107
|
+
extractInfo: (text) => {
|
|
108
|
+
const moduleMatch = text.match(/Unable to resolve module\s+['"]?([^'"]+)['"]?/);
|
|
109
|
+
if (moduleMatch) {
|
|
110
|
+
return {
|
|
111
|
+
type: "ModuleResolutionError",
|
|
112
|
+
category: "expo",
|
|
113
|
+
message: `Cannot find module: ${moduleMatch[1]}`,
|
|
114
|
+
suggestion: `Try: npx expo install ${moduleMatch[1]}`,
|
|
115
|
+
rawText: text
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const invariantMatch = text.match(/Invariant Violation:\s*(.+)/);
|
|
119
|
+
if (invariantMatch) {
|
|
120
|
+
return {
|
|
121
|
+
type: "InvariantViolation",
|
|
122
|
+
category: "expo",
|
|
123
|
+
message: invariantMatch[1].trim(),
|
|
124
|
+
rawText: text
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const nativeMatch = text.match(/Native module\s+['"]?([^'"]+)['"]?\s+cannot be null/);
|
|
128
|
+
if (nativeMatch) {
|
|
129
|
+
return {
|
|
130
|
+
type: "NativeModuleError",
|
|
131
|
+
category: "expo",
|
|
132
|
+
message: `Native module missing: ${nativeMatch[1]}`,
|
|
133
|
+
suggestion: "Try: npx expo prebuild --clean && npx expo run:ios/android",
|
|
134
|
+
rawText: text
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (text.includes("BUNDLE") && text.includes("failed")) {
|
|
138
|
+
return {
|
|
139
|
+
type: "BundleError",
|
|
140
|
+
category: "expo",
|
|
141
|
+
message: "Metro bundler failed to build",
|
|
142
|
+
suggestion: "Try: npx expo start -c (clear cache)",
|
|
143
|
+
rawText: text
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
type: "ExpoError",
|
|
148
|
+
category: "expo",
|
|
149
|
+
message: "Expo/React Native error detected",
|
|
150
|
+
rawText: text
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
var webpackPatterns = {
|
|
155
|
+
name: "Webpack/Build Errors",
|
|
156
|
+
category: "webpack",
|
|
157
|
+
patterns: [
|
|
158
|
+
/Module not found:\s*Error/,
|
|
159
|
+
/ModuleNotFoundError/,
|
|
160
|
+
/ERROR in\s+/,
|
|
161
|
+
/webpack.*error/i,
|
|
162
|
+
/Failed to compile/i,
|
|
163
|
+
/Module build failed/,
|
|
164
|
+
/Syntax error in/i,
|
|
165
|
+
/Can't resolve/,
|
|
166
|
+
/You may need an appropriate loader/
|
|
167
|
+
],
|
|
168
|
+
extractInfo: (text) => {
|
|
169
|
+
const moduleMatch = text.match(/Can't resolve\s+['"]?([^'"]+)['"]?/);
|
|
170
|
+
if (moduleMatch) {
|
|
171
|
+
return {
|
|
172
|
+
type: "ModuleNotFound",
|
|
173
|
+
category: "webpack",
|
|
174
|
+
message: `Cannot resolve: ${moduleMatch[1]}`,
|
|
175
|
+
suggestion: `Try: npm install ${moduleMatch[1]}`,
|
|
176
|
+
rawText: text
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const fileMatch = text.match(/ERROR in\s+(.+):(\d+):(\d+)/);
|
|
180
|
+
if (fileMatch) {
|
|
181
|
+
return {
|
|
182
|
+
type: "WebpackError",
|
|
183
|
+
category: "webpack",
|
|
184
|
+
message: "Build error",
|
|
185
|
+
file: fileMatch[1],
|
|
186
|
+
line: parseInt(fileMatch[2], 10),
|
|
187
|
+
column: parseInt(fileMatch[3], 10),
|
|
188
|
+
rawText: text
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (text.includes("appropriate loader")) {
|
|
192
|
+
const extensionMatch = text.match(/\.(\w+)(?:\?|$)/);
|
|
193
|
+
return {
|
|
194
|
+
type: "LoaderError",
|
|
195
|
+
category: "webpack",
|
|
196
|
+
message: `Missing loader for .${extensionMatch?.[1] || "unknown"} files`,
|
|
197
|
+
suggestion: "Check webpack.config.js for missing loaders",
|
|
198
|
+
rawText: text
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
type: "WebpackError",
|
|
203
|
+
category: "webpack",
|
|
204
|
+
message: "Webpack build error detected",
|
|
205
|
+
rawText: text
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
var typescriptPatterns = {
|
|
210
|
+
name: "TypeScript Errors",
|
|
211
|
+
category: "typescript",
|
|
212
|
+
patterns: [
|
|
213
|
+
/TS\d+:/,
|
|
214
|
+
/error TS\d+/,
|
|
215
|
+
/Type '.+' is not assignable to type/,
|
|
216
|
+
/Property '.+' does not exist on type/,
|
|
217
|
+
/Cannot find module '.+' or its corresponding type declarations/,
|
|
218
|
+
/Argument of type '.+' is not assignable/,
|
|
219
|
+
/Object is possibly 'undefined'/,
|
|
220
|
+
/Object is possibly 'null'/
|
|
221
|
+
],
|
|
222
|
+
extractInfo: (text) => {
|
|
223
|
+
const codeMatch = text.match(/TS(\d+)/);
|
|
224
|
+
const code = codeMatch ? `TS${codeMatch[1]}` : void 0;
|
|
225
|
+
const fileMatch = text.match(/(.+\.tsx?)\((\d+),(\d+)\)/);
|
|
226
|
+
const file = fileMatch?.[1];
|
|
227
|
+
const line = fileMatch ? parseInt(fileMatch[2], 10) : void 0;
|
|
228
|
+
const column = fileMatch ? parseInt(fileMatch[3], 10) : void 0;
|
|
229
|
+
const messageMatch = text.match(/error TS\d+:\s*(.+)/);
|
|
230
|
+
const message = messageMatch?.[1] || "TypeScript error detected";
|
|
231
|
+
return {
|
|
232
|
+
type: "TypeScriptError",
|
|
233
|
+
category: "typescript",
|
|
234
|
+
message,
|
|
235
|
+
code,
|
|
236
|
+
file,
|
|
237
|
+
line,
|
|
238
|
+
column,
|
|
239
|
+
rawText: text
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var pythonPatterns = {
|
|
244
|
+
name: "Python Errors",
|
|
245
|
+
category: "python",
|
|
246
|
+
patterns: [
|
|
247
|
+
/Traceback \(most recent call last\)/,
|
|
248
|
+
/^\s+File ".+", line \d+/m,
|
|
249
|
+
/^(NameError|TypeError|ValueError|KeyError|AttributeError|ImportError|ModuleNotFoundError|SyntaxError|IndentationError|IndexError|ZeroDivisionError|FileNotFoundError):/m,
|
|
250
|
+
/raise\s+\w+Error/
|
|
251
|
+
],
|
|
252
|
+
extractInfo: (text) => {
|
|
253
|
+
if (!text.includes("Traceback")) {
|
|
254
|
+
const errorMatch2 = text.match(
|
|
255
|
+
/^(NameError|TypeError|ValueError|KeyError|AttributeError|ImportError|ModuleNotFoundError|SyntaxError|IndentationError|IndexError|ZeroDivisionError|FileNotFoundError):\s*(.+)/m
|
|
256
|
+
);
|
|
257
|
+
if (errorMatch2) {
|
|
258
|
+
return {
|
|
259
|
+
type: errorMatch2[1],
|
|
260
|
+
category: "python",
|
|
261
|
+
message: errorMatch2[2].trim(),
|
|
262
|
+
rawText: text
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const lines = text.split("\n").filter((l) => l.trim());
|
|
268
|
+
const lastLine = lines[lines.length - 1];
|
|
269
|
+
const errorMatch = lastLine.match(/^(\w+Error):\s*(.+)/);
|
|
270
|
+
const fileMatch = text.match(/File "(.+)", line (\d+)/);
|
|
271
|
+
const file = fileMatch?.[1];
|
|
272
|
+
const line = fileMatch ? parseInt(fileMatch[2], 10) : void 0;
|
|
273
|
+
const stackLines = text.split("\n").filter((l) => l.trim().startsWith("File "));
|
|
274
|
+
const stack = stackLines.join("\n");
|
|
275
|
+
return {
|
|
276
|
+
type: errorMatch?.[1] || "PythonError",
|
|
277
|
+
category: "python",
|
|
278
|
+
message: errorMatch?.[2] || "Python error detected",
|
|
279
|
+
stack,
|
|
280
|
+
file,
|
|
281
|
+
line,
|
|
282
|
+
rawText: text
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
var ALL_PATTERNS = [
|
|
287
|
+
nodeJsPatterns,
|
|
288
|
+
npmPatterns,
|
|
289
|
+
expoPatterns,
|
|
290
|
+
webpackPatterns,
|
|
291
|
+
typescriptPatterns,
|
|
292
|
+
pythonPatterns
|
|
293
|
+
];
|
|
294
|
+
function detectError(text) {
|
|
295
|
+
if (!text || text.length < 10) return null;
|
|
296
|
+
for (const pattern of ALL_PATTERNS) {
|
|
297
|
+
const hasMatch = pattern.patterns.some((p) => p.test(text));
|
|
298
|
+
if (hasMatch) {
|
|
299
|
+
const info = pattern.extractInfo(text);
|
|
300
|
+
if (info) return info;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
function getErrorSummary(error) {
|
|
306
|
+
const parts = [];
|
|
307
|
+
parts.push(`[${error.category.toUpperCase()}]`);
|
|
308
|
+
parts.push(error.type);
|
|
309
|
+
if (error.message.length > 80) {
|
|
310
|
+
parts.push(error.message.substring(0, 77) + "...");
|
|
311
|
+
} else {
|
|
312
|
+
parts.push(error.message);
|
|
313
|
+
}
|
|
314
|
+
if (error.file) {
|
|
315
|
+
const shortFile = error.file.split(/[/\\]/).slice(-2).join("/");
|
|
316
|
+
parts.push(`@ ${shortFile}`);
|
|
317
|
+
if (error.line) {
|
|
318
|
+
parts[parts.length - 1] += `:${error.line}`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return parts.join(" ");
|
|
322
|
+
}
|
|
323
|
+
function formatErrorForNotes(error) {
|
|
324
|
+
const lines = [];
|
|
325
|
+
lines.push(`## Error Details`);
|
|
326
|
+
lines.push("");
|
|
327
|
+
lines.push(`**Type:** ${error.type}`);
|
|
328
|
+
lines.push(`**Category:** ${error.category}`);
|
|
329
|
+
lines.push(`**Message:** ${error.message}`);
|
|
330
|
+
if (error.code) {
|
|
331
|
+
lines.push(`**Code:** ${error.code}`);
|
|
332
|
+
}
|
|
333
|
+
if (error.file) {
|
|
334
|
+
lines.push(`**File:** \`${error.file}\``);
|
|
335
|
+
if (error.line) {
|
|
336
|
+
lines.push(`**Line:** ${error.line}${error.column ? `:${error.column}` : ""}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (error.suggestion) {
|
|
340
|
+
lines.push("");
|
|
341
|
+
lines.push(`### Suggestion`);
|
|
342
|
+
lines.push(error.suggestion);
|
|
343
|
+
}
|
|
344
|
+
if (error.stack) {
|
|
345
|
+
lines.push("");
|
|
346
|
+
lines.push(`### Stack Trace`);
|
|
347
|
+
lines.push("```");
|
|
348
|
+
lines.push(error.stack);
|
|
349
|
+
lines.push("```");
|
|
350
|
+
}
|
|
351
|
+
lines.push("");
|
|
352
|
+
lines.push(`### Raw Error`);
|
|
353
|
+
lines.push("```");
|
|
354
|
+
const rawLines = error.rawText.split("\n");
|
|
355
|
+
if (rawLines.length > 30) {
|
|
356
|
+
lines.push(rawLines.slice(0, 30).join("\n"));
|
|
357
|
+
lines.push(`... (${rawLines.length - 30} more lines)`);
|
|
358
|
+
} else {
|
|
359
|
+
lines.push(error.rawText);
|
|
360
|
+
}
|
|
361
|
+
lines.push("```");
|
|
362
|
+
return lines.join("\n");
|
|
363
|
+
}
|
|
364
|
+
function createClipboardMonitor(options) {
|
|
365
|
+
const interval = options.interval || 1e3;
|
|
366
|
+
const minLength = options.minLength || 20;
|
|
367
|
+
let running = false;
|
|
368
|
+
let lastClipboardContent = "";
|
|
369
|
+
let intervalId = null;
|
|
370
|
+
const checkClipboard = async () => {
|
|
371
|
+
try {
|
|
372
|
+
const clipboard = await getClipboardy();
|
|
373
|
+
const content = await clipboard.default.read();
|
|
374
|
+
if (content === lastClipboardContent || content.length < minLength) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
lastClipboardContent = content;
|
|
378
|
+
const error = detectError(content);
|
|
379
|
+
if (error) {
|
|
380
|
+
options.onError(error);
|
|
381
|
+
}
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
return {
|
|
386
|
+
start: async () => {
|
|
387
|
+
if (running) return;
|
|
388
|
+
await getClipboardy();
|
|
389
|
+
try {
|
|
390
|
+
const clipboard = await getClipboardy();
|
|
391
|
+
lastClipboardContent = await clipboard.default.read();
|
|
392
|
+
} catch {
|
|
393
|
+
lastClipboardContent = "";
|
|
394
|
+
}
|
|
395
|
+
running = true;
|
|
396
|
+
options.onStart?.();
|
|
397
|
+
intervalId = setInterval(checkClipboard, interval);
|
|
398
|
+
},
|
|
399
|
+
stop: () => {
|
|
400
|
+
if (!running) return;
|
|
401
|
+
running = false;
|
|
402
|
+
if (intervalId) {
|
|
403
|
+
clearInterval(intervalId);
|
|
404
|
+
intervalId = null;
|
|
405
|
+
}
|
|
406
|
+
options.onStop?.();
|
|
407
|
+
},
|
|
408
|
+
isRunning: () => running
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
async function readClipboard() {
|
|
412
|
+
const clipboard = await getClipboardy();
|
|
413
|
+
return clipboard.default.read();
|
|
414
|
+
}
|
|
415
|
+
async function checkClipboardForError() {
|
|
416
|
+
try {
|
|
417
|
+
const content = await readClipboard();
|
|
418
|
+
return detectError(content);
|
|
419
|
+
} catch {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export {
|
|
425
|
+
detectError,
|
|
426
|
+
getErrorSummary,
|
|
427
|
+
formatErrorForNotes,
|
|
428
|
+
createClipboardMonitor,
|
|
429
|
+
checkClipboardForError
|
|
430
|
+
};
|