wave-agent-sdk 1.0.4 → 1.0.6
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/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
package/src/utils/pathEncoder.ts
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path encoding utility for converting working directory paths to filesystem-safe names
|
|
3
|
-
* Handles cross-platform directory name encoding for project-based session organization
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { resolve, join } from "path";
|
|
7
|
-
import { createHash } from "crypto";
|
|
8
|
-
import { realpath, mkdir } from "fs/promises";
|
|
9
|
-
import { homedir, platform } from "os";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Project directory information
|
|
13
|
-
*/
|
|
14
|
-
export interface ProjectDirectory {
|
|
15
|
-
readonly originalPath: string;
|
|
16
|
-
readonly encodedName: string;
|
|
17
|
-
readonly encodedPath: string;
|
|
18
|
-
readonly pathHash?: string; // For collision resolution
|
|
19
|
-
readonly isSymbolicLink: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Path encoding configuration options
|
|
24
|
-
*/
|
|
25
|
-
export interface PathEncodingOptions {
|
|
26
|
-
maxLength?: number; // Default: 200 characters
|
|
27
|
-
pathSeparatorReplacement?: string; // Default: '-'
|
|
28
|
-
spaceReplacement?: string; // Default: '_'
|
|
29
|
-
invalidCharReplacement?: string; // Default: '_'
|
|
30
|
-
hashLength?: number; // Default: 8 characters
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Platform-specific filesystem constraints
|
|
35
|
-
*/
|
|
36
|
-
export interface FilesystemConstraints {
|
|
37
|
-
readonly maxDirectoryNameLength: number;
|
|
38
|
-
readonly maxPathLength: number;
|
|
39
|
-
readonly invalidCharacters: string[];
|
|
40
|
-
readonly reservedNames: string[];
|
|
41
|
-
readonly caseSensitive: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Path validation result
|
|
46
|
-
*/
|
|
47
|
-
export interface PathValidationResult {
|
|
48
|
-
readonly isValid: boolean;
|
|
49
|
-
readonly errors: string[];
|
|
50
|
-
readonly warnings: string[];
|
|
51
|
-
readonly suggestedFix?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* PathEncoder class for converting working directory paths to filesystem-safe names
|
|
56
|
-
*/
|
|
57
|
-
export class PathEncoder {
|
|
58
|
-
private readonly options: Required<PathEncodingOptions>;
|
|
59
|
-
private readonly constraints: FilesystemConstraints;
|
|
60
|
-
|
|
61
|
-
constructor(options: PathEncodingOptions = {}) {
|
|
62
|
-
this.options = {
|
|
63
|
-
maxLength: options.maxLength ?? 200,
|
|
64
|
-
pathSeparatorReplacement: options.pathSeparatorReplacement ?? "-",
|
|
65
|
-
spaceReplacement: options.spaceReplacement ?? "_",
|
|
66
|
-
invalidCharReplacement: options.invalidCharReplacement ?? "_",
|
|
67
|
-
hashLength: options.hashLength ?? 8,
|
|
68
|
-
};
|
|
69
|
-
this.constraints = this.getFilesystemConstraints();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Encode a working directory path to a filesystem-safe directory name
|
|
74
|
-
*/
|
|
75
|
-
async encode(originalPath: string): Promise<string> {
|
|
76
|
-
// Resolve symbolic links and normalize path
|
|
77
|
-
const resolvedPath = await this.resolvePath(originalPath);
|
|
78
|
-
return this.encodeSync(resolvedPath);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Synchronously encode a path to a filesystem-safe directory name
|
|
83
|
-
* Note: Does not resolve symbolic links - use encode() for full path resolution
|
|
84
|
-
*/
|
|
85
|
-
encodeSync(pathToEncode: string): string {
|
|
86
|
-
// Convert to safe directory name
|
|
87
|
-
let encoded = pathToEncode;
|
|
88
|
-
|
|
89
|
-
// Remove Windows drive letter prefix (e.g. "D:") so encoded names are
|
|
90
|
-
// stable across platforms — path.resolve("/home/user/project") yields
|
|
91
|
-
// "D:\home\user\project" on Windows and we want the same encoded name
|
|
92
|
-
// as on Linux ("home-user-project").
|
|
93
|
-
encoded = encoded.replace(/^[a-zA-Z]:/, "");
|
|
94
|
-
|
|
95
|
-
// Remove leading slash/backslash to avoid empty directory names
|
|
96
|
-
if (encoded.startsWith("/") || encoded.startsWith("\\")) {
|
|
97
|
-
encoded = encoded.substring(1);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Replace path separators with hyphens
|
|
101
|
-
encoded = encoded.replace(/[/\\]/g, this.options.pathSeparatorReplacement);
|
|
102
|
-
|
|
103
|
-
// Replace spaces with underscores
|
|
104
|
-
encoded = encoded.replace(/\s+/g, this.options.spaceReplacement);
|
|
105
|
-
|
|
106
|
-
// Replace invalid characters with underscores
|
|
107
|
-
const escapedChars = this.constraints.invalidCharacters
|
|
108
|
-
.map((c) => `\\${c}`)
|
|
109
|
-
.join("");
|
|
110
|
-
const invalidChars = new RegExp(`[${escapedChars}]`, "g");
|
|
111
|
-
encoded = encoded.replace(
|
|
112
|
-
invalidChars,
|
|
113
|
-
this.options.invalidCharReplacement,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// Case is preserved
|
|
117
|
-
|
|
118
|
-
// Handle length limit with hash
|
|
119
|
-
if (encoded.length > this.options.maxLength) {
|
|
120
|
-
const hash = this.generateHash(pathToEncode, this.options.hashLength);
|
|
121
|
-
const maxBaseLength =
|
|
122
|
-
this.options.maxLength - this.options.hashLength - 1; // -1 for separator
|
|
123
|
-
encoded = `${encoded.substring(0, maxBaseLength)}-${hash}`;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return encoded;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Decode an encoded directory name back to original path (limited functionality)
|
|
131
|
-
* Note: This is best-effort as encoding is lossy
|
|
132
|
-
*/
|
|
133
|
-
async decode(encodedName: string): Promise<string | null> {
|
|
134
|
-
return this.decodeSync(encodedName);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Synchronously decode an encoded directory name back to original path (limited functionality)
|
|
139
|
-
* Note: This is best-effort as encoding is lossy
|
|
140
|
-
*/
|
|
141
|
-
decodeSync(encodedName: string): string | null {
|
|
142
|
-
// This is a simplified version - full reversal is not always possible
|
|
143
|
-
// due to lossy encoding (case changes, character replacements, hashing)
|
|
144
|
-
|
|
145
|
-
// Check if this has a hash suffix
|
|
146
|
-
const hashPattern = new RegExp(`-[a-f0-9]{${this.options.hashLength}}$`);
|
|
147
|
-
if (hashPattern.test(encodedName)) {
|
|
148
|
-
// Cannot reliably decode hashed paths
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Attempt basic reversal
|
|
153
|
-
let decoded = encodedName;
|
|
154
|
-
|
|
155
|
-
// Reverse path separator replacement
|
|
156
|
-
decoded = decoded.replace(
|
|
157
|
-
new RegExp(this.options.pathSeparatorReplacement, "g"),
|
|
158
|
-
"/",
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
// Reverse space replacement
|
|
162
|
-
decoded = decoded.replace(
|
|
163
|
-
new RegExp(this.options.spaceReplacement, "g"),
|
|
164
|
-
" ",
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
// Add leading slash
|
|
168
|
-
decoded = `/${decoded}`;
|
|
169
|
-
|
|
170
|
-
return decoded;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Resolve symbolic links and normalize path before encoding
|
|
175
|
-
*/
|
|
176
|
-
async resolvePath(path: string): Promise<string> {
|
|
177
|
-
try {
|
|
178
|
-
// Expand tilde to home directory
|
|
179
|
-
const expandedPath = this.expandTilde(path);
|
|
180
|
-
|
|
181
|
-
// Resolve to absolute path
|
|
182
|
-
const absolutePath = resolve(expandedPath);
|
|
183
|
-
|
|
184
|
-
// Resolve symbolic links
|
|
185
|
-
const resolvedPath = await realpath(absolutePath);
|
|
186
|
-
|
|
187
|
-
return resolvedPath;
|
|
188
|
-
} catch (error) {
|
|
189
|
-
throw new Error(`Failed to resolve path "${path}": ${error}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Get project directory info without creating the directory
|
|
195
|
-
*/
|
|
196
|
-
async getProjectDirectory(
|
|
197
|
-
originalPath: string,
|
|
198
|
-
baseSessionDir: string,
|
|
199
|
-
): Promise<ProjectDirectory> {
|
|
200
|
-
// Resolve the original path and check for symbolic links
|
|
201
|
-
const expandedPath = this.expandTilde(originalPath);
|
|
202
|
-
const absolutePath = resolve(expandedPath);
|
|
203
|
-
|
|
204
|
-
let resolvedPath: string;
|
|
205
|
-
let isSymbolicLink = false;
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
resolvedPath = await realpath(absolutePath);
|
|
209
|
-
isSymbolicLink = resolvedPath !== absolutePath;
|
|
210
|
-
} catch {
|
|
211
|
-
// If realpath fails, use the absolute path
|
|
212
|
-
resolvedPath = absolutePath;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Encode the resolved path. Use encodeSync (pure string transform) rather
|
|
216
|
-
// than encode(): realpath was already attempted above with its own fallback,
|
|
217
|
-
// so re-resolving via encode()->resolvePath() would throw ENOENT again when
|
|
218
|
-
// the workdir no longer exists (e.g. a deleted worktree during agent
|
|
219
|
-
// shutdown). Session files live under <baseSessionDir>/<encoded>/ regardless
|
|
220
|
-
// of the workdir's existence, so encoding must not require it to exist.
|
|
221
|
-
const encodedName = this.encodeSync(resolvedPath);
|
|
222
|
-
const encodedPath = join(baseSessionDir, encodedName);
|
|
223
|
-
|
|
224
|
-
// Generate hash if encoding resulted in truncation
|
|
225
|
-
let pathHash: string | undefined;
|
|
226
|
-
if (resolvedPath.length > this.options.maxLength) {
|
|
227
|
-
pathHash = this.generateHash(resolvedPath, this.options.hashLength);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
originalPath: resolvedPath,
|
|
232
|
-
encodedName,
|
|
233
|
-
encodedPath,
|
|
234
|
-
pathHash,
|
|
235
|
-
isSymbolicLink,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Create project directory entity from original path
|
|
241
|
-
*/
|
|
242
|
-
async createProjectDirectory(
|
|
243
|
-
originalPath: string,
|
|
244
|
-
baseSessionDir: string,
|
|
245
|
-
): Promise<ProjectDirectory> {
|
|
246
|
-
const projectDirectory = await this.getProjectDirectory(
|
|
247
|
-
originalPath,
|
|
248
|
-
baseSessionDir,
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
// Ensure the encoded directory exists
|
|
252
|
-
try {
|
|
253
|
-
await mkdir(projectDirectory.encodedPath, { recursive: true });
|
|
254
|
-
} catch {
|
|
255
|
-
// Ignore errors if directory already exists
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return projectDirectory;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Validate that an encoded name is filesystem-safe
|
|
263
|
-
*/
|
|
264
|
-
validateEncodedName(encodedName: string): boolean {
|
|
265
|
-
// Check length
|
|
266
|
-
if (encodedName.length > this.constraints.maxDirectoryNameLength) {
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Check for invalid characters
|
|
271
|
-
for (const char of this.constraints.invalidCharacters) {
|
|
272
|
-
if (encodedName.includes(char)) {
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Check for reserved names
|
|
278
|
-
const lowerName = encodedName.toLowerCase();
|
|
279
|
-
if (
|
|
280
|
-
this.constraints.reservedNames.some(
|
|
281
|
-
(reserved) => reserved.toLowerCase() === lowerName,
|
|
282
|
-
)
|
|
283
|
-
) {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Check for empty or dots-only names
|
|
288
|
-
if (!encodedName.trim() || /^\.+$/.test(encodedName)) {
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Handle encoding collisions by generating unique names
|
|
297
|
-
*/
|
|
298
|
-
resolveCollision(baseName: string, existingNames: Set<string>): string {
|
|
299
|
-
if (!existingNames.has(baseName)) {
|
|
300
|
-
return baseName;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Try numbered suffixes first
|
|
304
|
-
for (let i = 1; i <= 999; i++) {
|
|
305
|
-
const candidate = `${baseName}-${i}`;
|
|
306
|
-
if (!existingNames.has(candidate)) {
|
|
307
|
-
return candidate;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// If all numbered suffixes are taken, use hash
|
|
312
|
-
const hash = this.generateHash(
|
|
313
|
-
baseName + Date.now(),
|
|
314
|
-
this.options.hashLength,
|
|
315
|
-
);
|
|
316
|
-
return `${baseName}-${hash}`;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Get platform-specific filesystem constraints
|
|
321
|
-
*/
|
|
322
|
-
private getFilesystemConstraints(): FilesystemConstraints {
|
|
323
|
-
const currentPlatform = platform();
|
|
324
|
-
|
|
325
|
-
switch (currentPlatform) {
|
|
326
|
-
case "win32":
|
|
327
|
-
return {
|
|
328
|
-
maxDirectoryNameLength: 255,
|
|
329
|
-
maxPathLength: 260,
|
|
330
|
-
invalidCharacters: ["<", ">", ":", '"', "|", "?", "*"],
|
|
331
|
-
reservedNames: [
|
|
332
|
-
"CON",
|
|
333
|
-
"PRN",
|
|
334
|
-
"AUX",
|
|
335
|
-
"NUL",
|
|
336
|
-
"COM1",
|
|
337
|
-
"COM2",
|
|
338
|
-
"COM3",
|
|
339
|
-
"COM4",
|
|
340
|
-
"COM5",
|
|
341
|
-
"COM6",
|
|
342
|
-
"COM7",
|
|
343
|
-
"COM8",
|
|
344
|
-
"COM9",
|
|
345
|
-
"LPT1",
|
|
346
|
-
"LPT2",
|
|
347
|
-
"LPT3",
|
|
348
|
-
"LPT4",
|
|
349
|
-
"LPT5",
|
|
350
|
-
"LPT6",
|
|
351
|
-
"LPT7",
|
|
352
|
-
"LPT8",
|
|
353
|
-
"LPT9",
|
|
354
|
-
],
|
|
355
|
-
caseSensitive: false,
|
|
356
|
-
};
|
|
357
|
-
case "darwin":
|
|
358
|
-
return {
|
|
359
|
-
maxDirectoryNameLength: 255,
|
|
360
|
-
maxPathLength: 1024,
|
|
361
|
-
invalidCharacters: [":"],
|
|
362
|
-
reservedNames: [],
|
|
363
|
-
caseSensitive: false, // HFS+ is case-insensitive by default
|
|
364
|
-
};
|
|
365
|
-
default: // Linux and other Unix-like systems
|
|
366
|
-
return {
|
|
367
|
-
maxDirectoryNameLength: 255,
|
|
368
|
-
maxPathLength: 4096,
|
|
369
|
-
invalidCharacters: ["\0"],
|
|
370
|
-
reservedNames: [],
|
|
371
|
-
caseSensitive: true,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Generate hash for collision resolution
|
|
378
|
-
*/
|
|
379
|
-
private generateHash(input: string, length: number): string {
|
|
380
|
-
return createHash("sha256")
|
|
381
|
-
.update(input)
|
|
382
|
-
.digest("hex")
|
|
383
|
-
.substring(0, length);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Expand tilde (~) to home directory
|
|
388
|
-
*/
|
|
389
|
-
private expandTilde(path: string): string {
|
|
390
|
-
if (path.startsWith("~/") || path === "~") {
|
|
391
|
-
return path.replace(/^~/, homedir());
|
|
392
|
-
}
|
|
393
|
-
return path;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Default PathEncoder instance
|
|
399
|
-
*/
|
|
400
|
-
export const pathEncoder = new PathEncoder();
|
package/src/utils/pathSafety.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Get the real path of a file or directory, even if it doesn't exist.
|
|
6
|
-
* For non-existent paths, it resolves the real path of the nearest existing parent.
|
|
7
|
-
*/
|
|
8
|
-
function getSafeRealPath(p: string): string {
|
|
9
|
-
try {
|
|
10
|
-
return fs.realpathSync(p);
|
|
11
|
-
} catch {
|
|
12
|
-
const parent = path.dirname(p);
|
|
13
|
-
if (parent === p) return p; // Root reached
|
|
14
|
-
return path.join(getSafeRealPath(parent), path.basename(p));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Check if a target path is inside a parent directory.
|
|
20
|
-
* Resolves symlinks and handles absolute paths.
|
|
21
|
-
* Returns true if target is the same as parent or is a subdirectory of parent.
|
|
22
|
-
* @param target The path to check
|
|
23
|
-
* @param parent The parent directory path
|
|
24
|
-
* @returns boolean
|
|
25
|
-
*/
|
|
26
|
-
export function isPathInside(target: string, parent: string): boolean {
|
|
27
|
-
try {
|
|
28
|
-
const absoluteTarget = path.resolve(target);
|
|
29
|
-
const absoluteParent = path.resolve(parent);
|
|
30
|
-
|
|
31
|
-
const realTarget = getSafeRealPath(absoluteTarget);
|
|
32
|
-
const realParent = getSafeRealPath(absoluteParent);
|
|
33
|
-
|
|
34
|
-
const relative = path.relative(realParent, realTarget);
|
|
35
|
-
|
|
36
|
-
return !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
37
|
-
} catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import { PROMPT_HISTORY_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
3
|
-
import { logger } from "./globalLogger.js";
|
|
4
|
-
import { PromptEntry } from "../types/history.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Ensure data directory exists
|
|
8
|
-
*/
|
|
9
|
-
const ensureDataDirectory = (): void => {
|
|
10
|
-
try {
|
|
11
|
-
if (!fs.existsSync(DATA_DIRECTORY)) {
|
|
12
|
-
fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
|
|
13
|
-
}
|
|
14
|
-
} catch (error) {
|
|
15
|
-
logger.debug("Failed to create data directory:", error);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const MAX_HISTORY_ENTRIES = 1000;
|
|
20
|
-
|
|
21
|
-
export class PromptHistoryManager {
|
|
22
|
-
/**
|
|
23
|
-
* Add a new prompt to history
|
|
24
|
-
*/
|
|
25
|
-
static async addEntry(
|
|
26
|
-
prompt: string,
|
|
27
|
-
sessionId?: string,
|
|
28
|
-
longTextMap?: Record<string, string>,
|
|
29
|
-
workdir?: string,
|
|
30
|
-
): Promise<void> {
|
|
31
|
-
try {
|
|
32
|
-
if (!prompt.trim()) return;
|
|
33
|
-
|
|
34
|
-
ensureDataDirectory();
|
|
35
|
-
const entry: PromptEntry = {
|
|
36
|
-
prompt,
|
|
37
|
-
timestamp: Date.now(),
|
|
38
|
-
sessionId,
|
|
39
|
-
longTextMap,
|
|
40
|
-
workdir,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const line = JSON.stringify(entry) + "\n";
|
|
44
|
-
await fs.promises.appendFile(PROMPT_HISTORY_FILE, line, "utf-8");
|
|
45
|
-
|
|
46
|
-
// Periodically trim history file to prevent it from growing too large
|
|
47
|
-
// We do this randomly (1 in 50 chance) to avoid performance hit on every entry
|
|
48
|
-
if (Math.random() < 0.02) {
|
|
49
|
-
await this.trimHistory();
|
|
50
|
-
}
|
|
51
|
-
} catch (error) {
|
|
52
|
-
logger.debug("Failed to add prompt to history:", error);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Trim history file to MAX_HISTORY_ENTRIES
|
|
58
|
-
*/
|
|
59
|
-
private static async trimHistory(): Promise<void> {
|
|
60
|
-
try {
|
|
61
|
-
if (!fs.existsSync(PROMPT_HISTORY_FILE)) return;
|
|
62
|
-
|
|
63
|
-
const data = await fs.promises.readFile(PROMPT_HISTORY_FILE, "utf-8");
|
|
64
|
-
const lines = data.split("\n").filter((line) => line.trim());
|
|
65
|
-
|
|
66
|
-
if (lines.length > MAX_HISTORY_ENTRIES * 1.2) {
|
|
67
|
-
const trimmedLines = lines.slice(-MAX_HISTORY_ENTRIES);
|
|
68
|
-
await fs.promises.writeFile(
|
|
69
|
-
PROMPT_HISTORY_FILE,
|
|
70
|
-
trimmedLines.join("\n") + "\n",
|
|
71
|
-
"utf-8",
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
logger.debug("Failed to trim prompt history:", error);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get all history entries
|
|
81
|
-
*/
|
|
82
|
-
static async getHistory(options?: {
|
|
83
|
-
sessionId?: string | string[];
|
|
84
|
-
workdir?: string;
|
|
85
|
-
}): Promise<PromptEntry[]> {
|
|
86
|
-
try {
|
|
87
|
-
if (!fs.existsSync(PROMPT_HISTORY_FILE)) {
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const data = await fs.promises.readFile(PROMPT_HISTORY_FILE, "utf-8");
|
|
92
|
-
const lines = data.split("\n").filter((line) => line.trim());
|
|
93
|
-
|
|
94
|
-
const entries: PromptEntry[] = lines
|
|
95
|
-
.map((line) => {
|
|
96
|
-
try {
|
|
97
|
-
return JSON.parse(line) as PromptEntry;
|
|
98
|
-
} catch {
|
|
99
|
-
logger.debug("Failed to parse history line:", line);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
.filter((entry): entry is PromptEntry => entry !== null);
|
|
104
|
-
|
|
105
|
-
// Filter by sessionId if provided
|
|
106
|
-
let filteredEntries = entries;
|
|
107
|
-
if (options?.sessionId) {
|
|
108
|
-
const sessionIds = Array.isArray(options.sessionId)
|
|
109
|
-
? options.sessionId
|
|
110
|
-
: [options.sessionId];
|
|
111
|
-
filteredEntries = filteredEntries.filter(
|
|
112
|
-
(entry) => entry.sessionId && sessionIds.includes(entry.sessionId),
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Filter by workdir if provided
|
|
117
|
-
if (options?.workdir) {
|
|
118
|
-
filteredEntries = filteredEntries.filter(
|
|
119
|
-
(entry) => entry.workdir === options.workdir,
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Deduplicate by prompt, keeping the most recent one
|
|
124
|
-
const uniqueEntries: PromptEntry[] = [];
|
|
125
|
-
const seenPrompts = new Set<string>();
|
|
126
|
-
|
|
127
|
-
// Process from newest to oldest
|
|
128
|
-
for (let i = filteredEntries.length - 1; i >= 0; i--) {
|
|
129
|
-
const entry = filteredEntries[i];
|
|
130
|
-
if (!seenPrompts.has(entry.prompt)) {
|
|
131
|
-
uniqueEntries.push(entry);
|
|
132
|
-
seenPrompts.add(entry.prompt);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return uniqueEntries;
|
|
137
|
-
} catch (error) {
|
|
138
|
-
logger.debug("Failed to load prompt history:", error);
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Search history by query
|
|
145
|
-
*/
|
|
146
|
-
static async searchHistory(
|
|
147
|
-
query: string,
|
|
148
|
-
options?: {
|
|
149
|
-
sessionId?: string | string[];
|
|
150
|
-
workdir?: string;
|
|
151
|
-
},
|
|
152
|
-
): Promise<PromptEntry[]> {
|
|
153
|
-
try {
|
|
154
|
-
const history = await this.getHistory(options);
|
|
155
|
-
if (!query.trim()) {
|
|
156
|
-
return history;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const normalizedQuery = query.toLowerCase();
|
|
160
|
-
return history.filter((entry) =>
|
|
161
|
-
entry.prompt.toLowerCase().includes(normalizedQuery),
|
|
162
|
-
);
|
|
163
|
-
} catch (error) {
|
|
164
|
-
logger.debug("Failed to search prompt history:", error);
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
package/src/utils/ripgrep.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import process from "process";
|
|
3
|
-
import { getPackageRoot } from "./configPaths.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Detect the current platform and architecture to find the correct bundled ripgrep binary.
|
|
7
|
-
*/
|
|
8
|
-
function getPlatformKey(): string {
|
|
9
|
-
const platform = process.platform;
|
|
10
|
-
const arch = process.arch;
|
|
11
|
-
|
|
12
|
-
if (platform === "darwin") {
|
|
13
|
-
return arch === "arm64" ? "macos-aarch64" : "macos-x86_64";
|
|
14
|
-
} else if (platform === "linux") {
|
|
15
|
-
return arch === "arm64" ? "linux-aarch64" : "linux-x86_64";
|
|
16
|
-
} else if (platform === "win32") {
|
|
17
|
-
return arch === "arm64" ? "windows-aarch64" : "windows-x86_64";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
throw new Error(`Unsupported platform: ${platform}-${arch}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const platformKey = getPlatformKey();
|
|
24
|
-
const isWindows = platformKey.startsWith("windows");
|
|
25
|
-
const binaryName = isWindows ? "rg.exe" : "rg";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Path to the ripgrep binary bundled in the vendor directory.
|
|
29
|
-
* Uses findUpSync to locate the package root, working correctly even
|
|
30
|
-
* when bundled (e.g. esbuild into a VS Code extension).
|
|
31
|
-
*/
|
|
32
|
-
export const rgPath = path.join(
|
|
33
|
-
getPackageRoot(),
|
|
34
|
-
`vendor/ripgrep/${platformKey}/${binaryName}`,
|
|
35
|
-
);
|