wave-agent-sdk 0.8.0 → 0.8.2
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/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +0 -21
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +0 -36
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +2 -1
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +8 -2
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +2 -14
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +5 -3
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +0 -4
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +2 -10
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +1 -3
- package/dist/services/taskManager.d.ts +2 -0
- package/dist/services/taskManager.d.ts.map +1 -1
- package/dist/services/taskManager.js +48 -0
- package/dist/tools/globTool.d.ts.map +1 -1
- package/dist/tools/globTool.js +12 -2
- package/dist/tools/taskManagementTools.d.ts.map +1 -1
- package/dist/tools/taskManagementTools.js +58 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +60 -50
- package/dist/utils/commandArgumentParser.d.ts.map +1 -1
- package/dist/utils/commandArgumentParser.js +7 -0
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +3 -0
- package/dist/utils/messageOperations.d.ts +1 -0
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +6 -2
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +3 -1
- package/package.json +1 -1
- package/src/managers/hookManager.ts +0 -52
- package/src/managers/liveConfigManager.ts +0 -75
- package/src/managers/messageManager.ts +2 -0
- package/src/managers/skillManager.ts +8 -2
- package/src/managers/slashCommandManager.ts +8 -18
- package/src/services/aiService.ts +5 -3
- package/src/services/fileWatcher.ts +0 -8
- package/src/services/initializationService.ts +2 -19
- package/src/services/pluginLoader.ts +1 -3
- package/src/services/taskManager.ts +51 -0
- package/src/tools/globTool.ts +15 -2
- package/src/tools/taskManagementTools.ts +77 -0
- package/src/tools/taskTool.ts +70 -61
- package/src/utils/commandArgumentParser.ts +8 -0
- package/src/utils/containerSetup.ts +3 -0
- package/src/utils/messageOperations.ts +7 -2
- package/src/utils/openaiClient.ts +3 -1
|
@@ -83,8 +83,6 @@ export class LiveConfigManager {
|
|
|
83
83
|
projectPaths?: string[],
|
|
84
84
|
): Promise<void> {
|
|
85
85
|
try {
|
|
86
|
-
logger?.debug("Live Config: Initializing configuration watching...");
|
|
87
|
-
|
|
88
86
|
this.userConfigPaths = userPaths;
|
|
89
87
|
this.projectConfigPaths = projectPaths;
|
|
90
88
|
|
|
@@ -94,16 +92,9 @@ export class LiveConfigManager {
|
|
|
94
92
|
// Start watching user configs that exist
|
|
95
93
|
for (const userPath of userPaths) {
|
|
96
94
|
if (existsSync(userPath)) {
|
|
97
|
-
logger?.debug(
|
|
98
|
-
`Live Config: Starting to watch user config: ${userPath}`,
|
|
99
|
-
);
|
|
100
95
|
await this.fileWatcher.watchFile(userPath, (event) =>
|
|
101
96
|
this.handleFileChange(event, "user"),
|
|
102
97
|
);
|
|
103
|
-
} else {
|
|
104
|
-
logger?.debug(
|
|
105
|
-
`Live Config: User config file does not exist: ${userPath}`,
|
|
106
|
-
);
|
|
107
98
|
}
|
|
108
99
|
}
|
|
109
100
|
|
|
@@ -114,24 +105,14 @@ export class LiveConfigManager {
|
|
|
114
105
|
if (projectPath.endsWith("settings.local.json")) {
|
|
115
106
|
await ensureGlobalGitIgnore("**/.wave/settings.local.json");
|
|
116
107
|
}
|
|
117
|
-
logger?.debug(
|
|
118
|
-
`Live Config: Starting to watch project config: ${projectPath}`,
|
|
119
|
-
);
|
|
120
108
|
await this.fileWatcher.watchFile(projectPath, (event) =>
|
|
121
109
|
this.handleFileChange(event, "project"),
|
|
122
110
|
);
|
|
123
|
-
} else {
|
|
124
|
-
logger?.debug(
|
|
125
|
-
`Live Config: Project config file does not exist: ${projectPath}`,
|
|
126
|
-
);
|
|
127
111
|
}
|
|
128
112
|
}
|
|
129
113
|
}
|
|
130
114
|
|
|
131
115
|
this.isWatching = true;
|
|
132
|
-
logger?.debug(
|
|
133
|
-
"Live Config: Configuration watching initialized successfully",
|
|
134
|
-
);
|
|
135
116
|
} catch (error) {
|
|
136
117
|
const errorMessage = `Failed to initialize configuration watching: ${(error as Error).message}`;
|
|
137
118
|
logger?.error(`Live Config: ${errorMessage}`);
|
|
@@ -151,7 +132,6 @@ export class LiveConfigManager {
|
|
|
151
132
|
*/
|
|
152
133
|
async initialize(): Promise<void> {
|
|
153
134
|
if (this.isInitialized) {
|
|
154
|
-
logger?.debug("Already initialized");
|
|
155
135
|
return;
|
|
156
136
|
}
|
|
157
137
|
|
|
@@ -163,9 +143,6 @@ export class LiveConfigManager {
|
|
|
163
143
|
await this.initializeWatching(userPaths, projectPaths);
|
|
164
144
|
|
|
165
145
|
this.isInitialized = true;
|
|
166
|
-
logger?.debug(
|
|
167
|
-
"Live configuration management initialized with file watching",
|
|
168
|
-
);
|
|
169
146
|
} catch (error) {
|
|
170
147
|
logger?.error(`Failed to initialize: ${(error as Error).message}`);
|
|
171
148
|
throw error;
|
|
@@ -181,8 +158,6 @@ export class LiveConfigManager {
|
|
|
181
158
|
}
|
|
182
159
|
|
|
183
160
|
try {
|
|
184
|
-
logger?.debug("Live Config: Shutting down configuration manager...");
|
|
185
|
-
|
|
186
161
|
this.isWatching = false;
|
|
187
162
|
|
|
188
163
|
// Cleanup file watcher
|
|
@@ -193,7 +168,6 @@ export class LiveConfigManager {
|
|
|
193
168
|
this.lastValidConfiguration = null;
|
|
194
169
|
|
|
195
170
|
this.isInitialized = false;
|
|
196
|
-
logger?.debug("Live configuration management shutdown completed");
|
|
197
171
|
} catch (error) {
|
|
198
172
|
logger?.error(`Error during shutdown: ${(error as Error).message}`);
|
|
199
173
|
throw error;
|
|
@@ -206,15 +180,12 @@ export class LiveConfigManager {
|
|
|
206
180
|
*/
|
|
207
181
|
private async reloadConfiguration(): Promise<WaveConfiguration> {
|
|
208
182
|
if (this.reloadInProgress) {
|
|
209
|
-
logger?.debug("Live Config: Reload already in progress, skipping");
|
|
210
183
|
return this.currentConfiguration || {};
|
|
211
184
|
}
|
|
212
185
|
|
|
213
186
|
this.reloadInProgress = true;
|
|
214
187
|
|
|
215
188
|
try {
|
|
216
|
-
logger?.debug("Live Config: Reloading configuration from files...");
|
|
217
|
-
|
|
218
189
|
// Load merged configuration using ConfigurationService
|
|
219
190
|
const loadResult: ConfigurationLoadResult =
|
|
220
191
|
await this.configurationService.loadMergedConfiguration(this.workdir);
|
|
@@ -237,9 +208,6 @@ export class LiveConfigManager {
|
|
|
237
208
|
|
|
238
209
|
// Use fallback configuration if available
|
|
239
210
|
if (this.lastValidConfiguration) {
|
|
240
|
-
logger?.debug(
|
|
241
|
-
"Live Config: Using previous valid configuration due to loading errors",
|
|
242
|
-
);
|
|
243
211
|
this.currentConfiguration = this.lastValidConfiguration;
|
|
244
212
|
|
|
245
213
|
// Apply environment variables to configuration service if configured
|
|
@@ -266,24 +234,6 @@ export class LiveConfigManager {
|
|
|
266
234
|
}
|
|
267
235
|
}
|
|
268
236
|
|
|
269
|
-
// Log success with detailed information
|
|
270
|
-
if (newConfig) {
|
|
271
|
-
logger?.debug(
|
|
272
|
-
`Live Config: Configuration loaded successfully from ${loadResult.sourcePath || "merged sources"}`,
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// Log detailed configuration info
|
|
276
|
-
const hookCount = Object.keys(newConfig.hooks || {}).length;
|
|
277
|
-
const envCount = Object.keys(newConfig.env || {}).length;
|
|
278
|
-
logger?.debug(
|
|
279
|
-
`Live Config: Loaded ${hookCount} hook events and ${envCount} environment variables`,
|
|
280
|
-
);
|
|
281
|
-
} else {
|
|
282
|
-
logger?.debug(
|
|
283
|
-
"Live Config: No configuration found (using empty configuration)",
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
237
|
// Log warnings from successful loading
|
|
288
238
|
if (loadResult.warnings && loadResult.warnings.length > 0) {
|
|
289
239
|
logger?.warn(
|
|
@@ -300,9 +250,6 @@ export class LiveConfigManager {
|
|
|
300
250
|
|
|
301
251
|
// Use previous valid configuration for error recovery
|
|
302
252
|
if (this.lastValidConfiguration) {
|
|
303
|
-
logger?.debug(
|
|
304
|
-
"Live Config: Using previous valid configuration due to validation errors",
|
|
305
|
-
);
|
|
306
253
|
this.currentConfiguration = this.lastValidConfiguration;
|
|
307
254
|
|
|
308
255
|
// Apply environment variables to configuration service if configured
|
|
@@ -339,9 +286,6 @@ export class LiveConfigManager {
|
|
|
339
286
|
// Save as last valid configuration if it's valid and not empty
|
|
340
287
|
if (newConfig && (newConfig.hooks || newConfig.env)) {
|
|
341
288
|
this.lastValidConfiguration = { ...newConfig };
|
|
342
|
-
logger?.debug(
|
|
343
|
-
"Live Config: Saved current configuration as last valid backup",
|
|
344
|
-
);
|
|
345
289
|
}
|
|
346
290
|
|
|
347
291
|
// Note: Environment variables are already applied by loadMergedConfiguration()
|
|
@@ -378,10 +322,6 @@ export class LiveConfigManager {
|
|
|
378
322
|
}
|
|
379
323
|
}
|
|
380
324
|
|
|
381
|
-
logger?.debug(
|
|
382
|
-
`Live Config: Configuration reload completed successfully with ${Object.keys(newConfig?.hooks || {}).length} event types and ${Object.keys(newConfig?.env || {}).length} environment variables`,
|
|
383
|
-
);
|
|
384
|
-
|
|
385
325
|
return this.currentConfiguration;
|
|
386
326
|
} catch (error) {
|
|
387
327
|
const errorMessage = `Configuration reload failed with exception: ${(error as Error).message}`;
|
|
@@ -389,9 +329,6 @@ export class LiveConfigManager {
|
|
|
389
329
|
|
|
390
330
|
// Use previous valid configuration for error recovery
|
|
391
331
|
if (this.lastValidConfiguration) {
|
|
392
|
-
logger?.debug(
|
|
393
|
-
"Live Config: Using previous valid configuration due to reload exception",
|
|
394
|
-
);
|
|
395
332
|
this.currentConfiguration = this.lastValidConfiguration;
|
|
396
333
|
|
|
397
334
|
// Apply environment variables to configuration service if configured
|
|
@@ -424,7 +361,6 @@ export class LiveConfigManager {
|
|
|
424
361
|
* Reload configuration from files (public method)
|
|
425
362
|
*/
|
|
426
363
|
async reload(): Promise<WaveConfiguration> {
|
|
427
|
-
logger?.debug("Manually reloading configuration...");
|
|
428
364
|
return await this.reloadConfiguration();
|
|
429
365
|
}
|
|
430
366
|
|
|
@@ -464,16 +400,9 @@ export class LiveConfigManager {
|
|
|
464
400
|
event: FileWatchEvent,
|
|
465
401
|
source: Scope,
|
|
466
402
|
): Promise<void> {
|
|
467
|
-
logger?.debug(
|
|
468
|
-
`Live Config: File ${event.type} detected for ${source} config: ${event.path}`,
|
|
469
|
-
);
|
|
470
|
-
|
|
471
403
|
try {
|
|
472
404
|
// Handle file deletion
|
|
473
405
|
if (event.type === "delete") {
|
|
474
|
-
logger?.debug(
|
|
475
|
-
`Live Config: ${source} config file deleted: ${event.path}`,
|
|
476
|
-
);
|
|
477
406
|
// Reload configuration without the deleted file
|
|
478
407
|
await this.reloadConfiguration();
|
|
479
408
|
return;
|
|
@@ -481,10 +410,6 @@ export class LiveConfigManager {
|
|
|
481
410
|
|
|
482
411
|
// Handle file creation or modification
|
|
483
412
|
if (event.type === "change" || event.type === "create") {
|
|
484
|
-
logger?.debug(
|
|
485
|
-
`Live Config: ${source} config file ${event.type}: ${event.path}`,
|
|
486
|
-
);
|
|
487
|
-
|
|
488
413
|
if (
|
|
489
414
|
source === "project" &&
|
|
490
415
|
event.path.endsWith("settings.local.json") &&
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
removeLastUserMessage,
|
|
10
10
|
UserMessageParams,
|
|
11
11
|
type AgentToolBlockUpdateParams,
|
|
12
|
+
generateMessageId,
|
|
12
13
|
} from "../utils/messageOperations.js";
|
|
13
14
|
import type { Message, Usage, SlashCommand } from "../types/index.js";
|
|
14
15
|
import { join } from "path";
|
|
@@ -422,6 +423,7 @@ export class MessageManager {
|
|
|
422
423
|
|
|
423
424
|
// Create compressed message
|
|
424
425
|
const compressMessage: Message = {
|
|
426
|
+
id: generateMessageId(),
|
|
425
427
|
role: "assistant",
|
|
426
428
|
blocks: [
|
|
427
429
|
{
|
|
@@ -355,13 +355,19 @@ export class SkillManager {
|
|
|
355
355
|
*/
|
|
356
356
|
registerPluginSkills(skills: Skill[]): void {
|
|
357
357
|
for (const skill of skills) {
|
|
358
|
-
|
|
358
|
+
const metadata: SkillMetadata = {
|
|
359
359
|
name: skill.name,
|
|
360
360
|
description: skill.description,
|
|
361
361
|
type: skill.type,
|
|
362
362
|
skillPath: skill.skillPath,
|
|
363
363
|
allowedTools: skill.allowedTools,
|
|
364
|
-
|
|
364
|
+
context: skill.context,
|
|
365
|
+
agent: skill.agent,
|
|
366
|
+
model: skill.model,
|
|
367
|
+
disableModelInvocation: skill.disableModelInvocation,
|
|
368
|
+
userInvocable: skill.userInvocable,
|
|
369
|
+
};
|
|
370
|
+
this.skillMetadata.set(skill.name, metadata);
|
|
365
371
|
this.skillContent.set(skill.name, skill);
|
|
366
372
|
}
|
|
367
373
|
logger?.debug(
|
|
@@ -117,15 +117,10 @@ export class SlashCommandManager {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (args) {
|
|
120
|
-
|
|
121
|
-
processedContent
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
} else {
|
|
126
|
-
// If no placeholders, append arguments to the content
|
|
127
|
-
processedContent = `${processedContent.trim()} ${args}`;
|
|
128
|
-
}
|
|
120
|
+
processedContent = substituteCommandParameters(
|
|
121
|
+
processedContent,
|
|
122
|
+
args,
|
|
123
|
+
);
|
|
129
124
|
}
|
|
130
125
|
|
|
131
126
|
await this.executeCustomCommandInMainAgent(
|
|
@@ -240,15 +235,10 @@ export class SlashCommandManager {
|
|
|
240
235
|
}
|
|
241
236
|
|
|
242
237
|
if (args) {
|
|
243
|
-
|
|
244
|
-
processedContent
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
);
|
|
248
|
-
} else {
|
|
249
|
-
// If no placeholders, append arguments to the content
|
|
250
|
-
processedContent = `${processedContent.trim()} ${args}`;
|
|
251
|
-
}
|
|
238
|
+
processedContent = substituteCommandParameters(
|
|
239
|
+
processedContent,
|
|
240
|
+
args,
|
|
241
|
+
);
|
|
252
242
|
}
|
|
253
243
|
|
|
254
244
|
await this.executeCustomCommandInMainAgent(
|
|
@@ -132,9 +132,9 @@ function getModelConfig(
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
// Configuration rules for specific models
|
|
135
|
-
if (modelName.includes("gpt-5
|
|
136
|
-
// gpt-5
|
|
137
|
-
config.temperature
|
|
135
|
+
if (modelName.includes("gpt-5")) {
|
|
136
|
+
// gpt-5 models should not have temperature field
|
|
137
|
+
delete config.temperature;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
return config;
|
|
@@ -399,6 +399,7 @@ export async function callAgent(
|
|
|
399
399
|
}
|
|
400
400
|
} catch (error) {
|
|
401
401
|
if ((error as Error).name === "AbortError") {
|
|
402
|
+
logger.info("OpenAI request aborted");
|
|
402
403
|
throw new Error("Request was aborted");
|
|
403
404
|
}
|
|
404
405
|
|
|
@@ -818,6 +819,7 @@ export async function compressMessages(
|
|
|
818
819
|
};
|
|
819
820
|
} catch (error) {
|
|
820
821
|
if ((error as Error).name === "AbortError") {
|
|
822
|
+
logger.info("Compression request was aborted");
|
|
821
823
|
throw new Error("Compression request was aborted");
|
|
822
824
|
}
|
|
823
825
|
logger.error("Failed to compress messages:", error);
|
|
@@ -115,7 +115,6 @@ export class FileWatcherService extends EventEmitter {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
this.watchers.delete(path);
|
|
118
|
-
this.logger?.debug(`Live Config: Stopped watching file: ${path}`);
|
|
119
118
|
} catch (error) {
|
|
120
119
|
this.logger?.warn(
|
|
121
120
|
`Live Config: Error unwatching file ${path}: ${(error as Error).message}`,
|
|
@@ -199,8 +198,6 @@ export class FileWatcherService extends EventEmitter {
|
|
|
199
198
|
entry.watcher = this.globalWatcher;
|
|
200
199
|
entry.isActive = true;
|
|
201
200
|
entry.errorCount = 0;
|
|
202
|
-
|
|
203
|
-
this.logger?.debug(`Live Config: Started watching file: ${entry.path}`);
|
|
204
201
|
} catch (error) {
|
|
205
202
|
entry.errorCount++;
|
|
206
203
|
entry.isActive = false;
|
|
@@ -215,9 +212,6 @@ export class FileWatcherService extends EventEmitter {
|
|
|
215
212
|
!entry.config.fallbackPolling &&
|
|
216
213
|
entry.errorCount < entry.config.maxRetries
|
|
217
214
|
) {
|
|
218
|
-
this.logger?.debug(
|
|
219
|
-
`Live Config: Attempting polling fallback for ${entry.path}`,
|
|
220
|
-
);
|
|
221
215
|
entry.config.fallbackPolling = true;
|
|
222
216
|
await this.initializeWatcher(entry);
|
|
223
217
|
} else {
|
|
@@ -281,7 +275,5 @@ export class FileWatcherService extends EventEmitter {
|
|
|
281
275
|
);
|
|
282
276
|
}
|
|
283
277
|
}
|
|
284
|
-
|
|
285
|
-
this.logger?.debug(`Live Config: File ${type} event for ${filePath}`);
|
|
286
278
|
}
|
|
287
279
|
}
|
|
@@ -121,7 +121,6 @@ export class InitializationService {
|
|
|
121
121
|
// Initialize hooks configuration
|
|
122
122
|
try {
|
|
123
123
|
// Load hooks configuration using ConfigurationService
|
|
124
|
-
logger?.debug("Loading hooks configuration...");
|
|
125
124
|
const configResult =
|
|
126
125
|
await configurationService.loadMergedConfiguration(workdir);
|
|
127
126
|
|
|
@@ -156,8 +155,6 @@ export class InitializationService {
|
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
}
|
|
159
|
-
|
|
160
|
-
logger?.debug("Hooks system initialized successfully");
|
|
161
158
|
} catch (error) {
|
|
162
159
|
logger?.error("Failed to initialize hooks system:", error);
|
|
163
160
|
// Don't throw error to prevent app startup failure
|
|
@@ -206,9 +203,7 @@ export class InitializationService {
|
|
|
206
203
|
|
|
207
204
|
// Initialize live configuration reload
|
|
208
205
|
try {
|
|
209
|
-
logger?.debug("Initializing live configuration reload...");
|
|
210
206
|
await liveConfigManager.initialize();
|
|
211
|
-
logger?.debug("Live configuration reload initialized successfully");
|
|
212
207
|
} catch (error) {
|
|
213
208
|
logger?.error("Failed to initialize live configuration reload:", error);
|
|
214
209
|
// Don't throw error to prevent app startup failure - continue without live reload
|
|
@@ -216,8 +211,6 @@ export class InitializationService {
|
|
|
216
211
|
|
|
217
212
|
// Load memory files during initialization
|
|
218
213
|
try {
|
|
219
|
-
logger?.debug("Loading memory files...");
|
|
220
|
-
|
|
221
214
|
// Load project memory from AGENTS.md (bypass memory store for direct file access)
|
|
222
215
|
try {
|
|
223
216
|
const projectMemoryPath = path.join(workdir, "AGENTS.md");
|
|
@@ -226,13 +219,9 @@ export class InitializationService {
|
|
|
226
219
|
"utf-8",
|
|
227
220
|
);
|
|
228
221
|
setProjectMemory(projectMemoryContent);
|
|
229
|
-
logger?.debug("Project memory loaded successfully");
|
|
230
222
|
} catch (error) {
|
|
223
|
+
logger?.warn("Failed to load project memory file:", error);
|
|
231
224
|
setProjectMemory("");
|
|
232
|
-
logger?.debug(
|
|
233
|
-
"Project memory file not found or unreadable, using empty content:",
|
|
234
|
-
error instanceof Error ? error.message : String(error),
|
|
235
|
-
);
|
|
236
225
|
}
|
|
237
226
|
|
|
238
227
|
// Load user memory (bypass memory store for direct file access)
|
|
@@ -240,16 +229,10 @@ export class InitializationService {
|
|
|
240
229
|
const userMemoryPath = path.join(os.homedir(), ".wave", "AGENTS.md");
|
|
241
230
|
const userMemoryContent = await fs.readFile(userMemoryPath, "utf-8");
|
|
242
231
|
setUserMemory(userMemoryContent);
|
|
243
|
-
logger?.debug("User memory loaded successfully");
|
|
244
232
|
} catch (error) {
|
|
233
|
+
logger?.warn("Failed to load user memory file:", error);
|
|
245
234
|
setUserMemory("");
|
|
246
|
-
logger?.debug(
|
|
247
|
-
"User memory file not found or unreadable, using empty content:",
|
|
248
|
-
error instanceof Error ? error.message : String(error),
|
|
249
|
-
);
|
|
250
235
|
}
|
|
251
|
-
|
|
252
|
-
logger?.debug("Memory initialization completed");
|
|
253
236
|
} catch (error) {
|
|
254
237
|
// Ensure memory is always initialized even if loading fails
|
|
255
238
|
setProjectMemory("");
|
|
@@ -98,10 +98,8 @@ export class PluginLoader {
|
|
|
98
98
|
});
|
|
99
99
|
if (parsed.isValid) {
|
|
100
100
|
skills.push({
|
|
101
|
-
|
|
102
|
-
description: parsed.skillMetadata.description,
|
|
101
|
+
...parsed.skillMetadata,
|
|
103
102
|
type: "project", // Plugin skills are treated as project skills
|
|
104
|
-
skillPath: parsed.skillMetadata.skillPath,
|
|
105
103
|
content: parsed.content,
|
|
106
104
|
frontmatter: parsed.frontmatter,
|
|
107
105
|
isValid: parsed.isValid,
|
|
@@ -161,6 +161,57 @@ export class TaskManager extends EventEmitter {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
async deleteTask(taskId: string): Promise<void> {
|
|
165
|
+
await this.withLock(async () => {
|
|
166
|
+
const taskPath = this.getTaskPath(taskId);
|
|
167
|
+
try {
|
|
168
|
+
await fs.unlink(taskPath);
|
|
169
|
+
this.emit("tasksChange", this.taskListId);
|
|
170
|
+
logger.debug(
|
|
171
|
+
`Task ${taskId} deleted from task list ${this.taskListId}`,
|
|
172
|
+
);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async cleanupOldTaskLists(days: number = 30): Promise<void> {
|
|
182
|
+
const threshold = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
183
|
+
try {
|
|
184
|
+
const dirs = await fs.readdir(this.baseDir);
|
|
185
|
+
for (const dir of dirs) {
|
|
186
|
+
if (dir === this.taskListId) continue;
|
|
187
|
+
|
|
188
|
+
const dirPath = join(this.baseDir, dir);
|
|
189
|
+
const stats = await fs.stat(dirPath);
|
|
190
|
+
if (!stats.isDirectory()) continue;
|
|
191
|
+
|
|
192
|
+
// Check mtime of the directory and its contents
|
|
193
|
+
let latestMtime = stats.mtimeMs;
|
|
194
|
+
const files = await fs.readdir(dirPath);
|
|
195
|
+
for (const file of files) {
|
|
196
|
+
const filePath = join(dirPath, file);
|
|
197
|
+
const fileStats = await fs.stat(filePath);
|
|
198
|
+
if (fileStats.mtimeMs > latestMtime) {
|
|
199
|
+
latestMtime = fileStats.mtimeMs;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (latestMtime < threshold) {
|
|
204
|
+
logger.info(`Cleaning up old task list directory: ${dirPath}`);
|
|
205
|
+
await fs.rm(dirPath, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
210
|
+
logger.error("Failed to cleanup old task lists:", error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
164
215
|
async listTasks(): Promise<Task[]> {
|
|
165
216
|
const sessionDir = this.getSessionDir();
|
|
166
217
|
try {
|
package/src/tools/globTool.ts
CHANGED
|
@@ -5,6 +5,11 @@ import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
|
5
5
|
import { getGlobIgnorePatterns } from "../utils/fileFilter.js";
|
|
6
6
|
import { GLOB_TOOL_NAME } from "../constants/tools.js";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Maximum number of files returned by glob tool
|
|
10
|
+
*/
|
|
11
|
+
const MAX_GLOB_RESULTS = 1000;
|
|
12
|
+
|
|
8
13
|
/**
|
|
9
14
|
* Glob Tool Plugin - Fast file pattern matching
|
|
10
15
|
*/
|
|
@@ -109,15 +114,23 @@ export const globTool: ToolPlugin = {
|
|
|
109
114
|
.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()) // Most recently modified files first
|
|
110
115
|
.map((item) => item.path);
|
|
111
116
|
|
|
117
|
+
const totalCount = sortedFiles.length;
|
|
118
|
+
const finalFiles = sortedFiles.slice(0, MAX_GLOB_RESULTS);
|
|
119
|
+
|
|
112
120
|
// Format output
|
|
113
|
-
const output =
|
|
121
|
+
const output = finalFiles
|
|
114
122
|
.map((file, index) => `${index + 1}. ${file}`)
|
|
115
123
|
.join("\n");
|
|
116
124
|
|
|
125
|
+
const isTruncated = totalCount > MAX_GLOB_RESULTS;
|
|
126
|
+
const shortResult = isTruncated
|
|
127
|
+
? `Found ${totalCount} files (showing first ${MAX_GLOB_RESULTS})`
|
|
128
|
+
: `Found ${totalCount} file${totalCount === 1 ? "" : "s"}`;
|
|
129
|
+
|
|
117
130
|
return {
|
|
118
131
|
success: true,
|
|
119
132
|
content: output,
|
|
120
|
-
shortResult
|
|
133
|
+
shortResult,
|
|
121
134
|
};
|
|
122
135
|
} catch (error) {
|
|
123
136
|
return {
|
|
@@ -101,6 +101,15 @@ NOTE that you should not use this tool if there is only one trivial task to do.
|
|
|
101
101
|
- Check TaskList first to avoid creating duplicate tasks`,
|
|
102
102
|
execute: async (args, context: ToolContext): Promise<ToolResult> => {
|
|
103
103
|
const taskManager = context.taskManager;
|
|
104
|
+
|
|
105
|
+
if (args.status === "deleted") {
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
content: `Task creation skipped because status was set to 'deleted'.`,
|
|
109
|
+
shortResult: `Skipped deleted task`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
104
113
|
const task: Omit<Task, "id"> = {
|
|
105
114
|
subject: args.subject as string,
|
|
106
115
|
description: args.description as string,
|
|
@@ -334,6 +343,74 @@ Set up task dependencies:
|
|
|
334
343
|
};
|
|
335
344
|
}
|
|
336
345
|
|
|
346
|
+
if (args.status === "deleted") {
|
|
347
|
+
// Reciprocal Dependency Cleanup
|
|
348
|
+
// For each task in the deleted task's blocks list, remove the deleted task's ID from their blockedBy list.
|
|
349
|
+
for (const targetId of existingTask.blocks) {
|
|
350
|
+
const targetTask = await taskManager.getTask(targetId);
|
|
351
|
+
if (targetTask && targetTask.blockedBy.includes(taskId)) {
|
|
352
|
+
let targetSnapshotId: string | undefined;
|
|
353
|
+
if (context.reversionManager && context.messageId) {
|
|
354
|
+
const targetPath = taskManager.getTaskPath(targetId);
|
|
355
|
+
targetSnapshotId = await context.reversionManager.recordSnapshot(
|
|
356
|
+
context.messageId,
|
|
357
|
+
targetPath,
|
|
358
|
+
"modify",
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
await taskManager.updateTask({
|
|
362
|
+
...targetTask,
|
|
363
|
+
blockedBy: targetTask.blockedBy.filter((id) => id !== taskId),
|
|
364
|
+
});
|
|
365
|
+
if (context.reversionManager && targetSnapshotId) {
|
|
366
|
+
await context.reversionManager.commitSnapshot(targetSnapshotId);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// For each task in the deleted task's blockedBy list, remove the deleted task's ID from their blocks list.
|
|
372
|
+
for (const targetId of existingTask.blockedBy) {
|
|
373
|
+
const targetTask = await taskManager.getTask(targetId);
|
|
374
|
+
if (targetTask && targetTask.blocks.includes(taskId)) {
|
|
375
|
+
let targetSnapshotId: string | undefined;
|
|
376
|
+
if (context.reversionManager && context.messageId) {
|
|
377
|
+
const targetPath = taskManager.getTaskPath(targetId);
|
|
378
|
+
targetSnapshotId = await context.reversionManager.recordSnapshot(
|
|
379
|
+
context.messageId,
|
|
380
|
+
targetPath,
|
|
381
|
+
"modify",
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
await taskManager.updateTask({
|
|
385
|
+
...targetTask,
|
|
386
|
+
blocks: targetTask.blocks.filter((id) => id !== taskId),
|
|
387
|
+
});
|
|
388
|
+
if (context.reversionManager && targetSnapshotId) {
|
|
389
|
+
await context.reversionManager.commitSnapshot(targetSnapshotId);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Record delete snapshot for the task itself
|
|
395
|
+
if (context.reversionManager && context.messageId) {
|
|
396
|
+
const taskPath = taskManager.getTaskPath(taskId);
|
|
397
|
+
const deleteSnapshotId = await context.reversionManager.recordSnapshot(
|
|
398
|
+
context.messageId,
|
|
399
|
+
taskPath,
|
|
400
|
+
"delete",
|
|
401
|
+
);
|
|
402
|
+
await context.reversionManager.commitSnapshot(deleteSnapshotId);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
await taskManager.deleteTask(taskId);
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
success: true,
|
|
409
|
+
content: `Task #${taskId} deleted and removed from disk.`,
|
|
410
|
+
shortResult: `Deleted task ${taskId}`,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
337
414
|
let snapshotId: string | undefined;
|
|
338
415
|
if (context.reversionManager && context.messageId) {
|
|
339
416
|
const taskPath = taskManager.getTaskPath(taskId);
|