task-o-matic 0.0.10 → 0.0.11
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/cli/display/progress.d.ts +1 -1
- package/dist/cli/display/progress.d.ts.map +1 -1
- package/dist/cli/display/progress.js +16 -13
- package/dist/commands/benchmark.js +70 -2
- package/dist/commands/init.js +48 -27
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +201 -0
- package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
- package/dist/commands/tasks/execute-loop.js +10 -0
- package/dist/commands/tasks/execute.d.ts.map +1 -1
- package/dist/commands/tasks/execute.js +4 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +56 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/ai-service/ai-operations.d.ts +13 -10
- package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/ai-operations.js +35 -995
- package/dist/lib/ai-service/base-operations.d.ts +13 -0
- package/dist/lib/ai-service/base-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/base-operations.js +79 -0
- package/dist/lib/ai-service/documentation-operations.d.ts +18 -0
- package/dist/lib/ai-service/documentation-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/documentation-operations.js +291 -0
- package/dist/lib/ai-service/prd-operations.d.ts +14 -0
- package/dist/lib/ai-service/prd-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/prd-operations.js +405 -0
- package/dist/lib/ai-service/task-operations.d.ts +12 -0
- package/dist/lib/ai-service/task-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/task-operations.js +225 -0
- package/dist/lib/benchmark/registry.d.ts.map +1 -1
- package/dist/lib/benchmark/registry.js +127 -0
- package/dist/lib/better-t-stack-cli.d.ts +4 -1
- package/dist/lib/better-t-stack-cli.d.ts.map +1 -1
- package/dist/lib/better-t-stack-cli.js +126 -5
- package/dist/lib/config.d.ts +13 -6
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +90 -48
- package/dist/lib/context-builder.d.ts +13 -1
- package/dist/lib/context-builder.d.ts.map +1 -1
- package/dist/lib/context-builder.js +68 -36
- package/dist/lib/executors/claude-code-executor.d.ts +5 -2
- package/dist/lib/executors/claude-code-executor.d.ts.map +1 -1
- package/dist/lib/executors/claude-code-executor.js +30 -3
- package/dist/lib/executors/codex-executor.d.ts +5 -2
- package/dist/lib/executors/codex-executor.d.ts.map +1 -1
- package/dist/lib/executors/codex-executor.js +30 -3
- package/dist/lib/executors/executor-factory.d.ts +2 -2
- package/dist/lib/executors/executor-factory.d.ts.map +1 -1
- package/dist/lib/executors/executor-factory.js +5 -5
- package/dist/lib/executors/gemini-executor.d.ts +5 -2
- package/dist/lib/executors/gemini-executor.d.ts.map +1 -1
- package/dist/lib/executors/gemini-executor.js +30 -3
- package/dist/lib/executors/opencode-executor.d.ts +5 -2
- package/dist/lib/executors/opencode-executor.d.ts.map +1 -1
- package/dist/lib/executors/opencode-executor.js +30 -7
- package/dist/lib/prompt-builder.d.ts.map +1 -1
- package/dist/lib/prompt-builder.js +1 -0
- package/dist/lib/storage/file-system.d.ts +3 -7
- package/dist/lib/storage/file-system.d.ts.map +1 -1
- package/dist/lib/storage/file-system.js +50 -230
- package/dist/lib/storage/storage-callbacks.d.ts +17 -0
- package/dist/lib/storage/storage-callbacks.d.ts.map +1 -0
- package/dist/lib/storage/storage-callbacks.js +94 -0
- package/dist/lib/task-execution.d.ts.map +1 -1
- package/dist/lib/task-execution.js +16 -9
- package/dist/lib/task-loop-execution.d.ts.map +1 -1
- package/dist/lib/task-loop-execution.js +207 -8
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +2 -0
- package/dist/prompts/prd-combination.d.ts +2 -0
- package/dist/prompts/prd-combination.d.ts.map +1 -0
- package/dist/prompts/prd-combination.js +35 -0
- package/dist/prompts/prd-generation.d.ts +2 -0
- package/dist/prompts/prd-generation.d.ts.map +1 -0
- package/dist/prompts/prd-generation.js +49 -0
- package/dist/services/prd.d.ts +43 -0
- package/dist/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +121 -0
- package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
- package/dist/services/workflow-ai-assistant.js +1 -40
- package/dist/services/workflow.d.ts +10 -0
- package/dist/services/workflow.d.ts.map +1 -1
- package/dist/services/workflow.js +118 -40
- package/dist/test/integration/callbacks.test.d.ts +2 -0
- package/dist/test/integration/callbacks.test.d.ts.map +1 -0
- package/dist/test/integration/callbacks.test.js +64 -0
- package/dist/types/callbacks.d.ts +9 -6
- package/dist/types/callbacks.d.ts.map +1 -1
- package/dist/types/index.d.ts +17 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/workflow-options.d.ts +7 -0
- package/dist/types/workflow-options.d.ts.map +1 -1
- package/dist/utils/ai-service-factory.d.ts +15 -1
- package/dist/utils/ai-service-factory.d.ts.map +1 -1
- package/dist/utils/ai-service-factory.js +29 -1
- package/dist/utils/streaming-options.d.ts +1 -1
- package/dist/utils/streaming-options.d.ts.map +1 -1
- package/dist/utils/streaming-options.js +31 -18
- package/docs/agents/cli.md +191 -0
- package/package.json +3 -2
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FileSystemStorage = void 0;
|
|
4
|
-
const promises_1 = require("fs/promises");
|
|
5
|
-
const path_1 = require("path");
|
|
6
4
|
const config_1 = require("../config");
|
|
5
|
+
const storage_callbacks_1 = require("./storage-callbacks");
|
|
7
6
|
class FileSystemStorage {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
callbacks;
|
|
8
|
+
constructor(callbacks) {
|
|
9
|
+
// If no callbacks provided, use default file system callbacks
|
|
10
|
+
// We use configManager to get the base directory for the default implementation
|
|
11
|
+
this.callbacks =
|
|
12
|
+
callbacks || (0, storage_callbacks_1.createFileSystemCallbacks)(config_1.configManager.getTaskOMaticDir());
|
|
13
13
|
}
|
|
14
14
|
sanitizeForFilename(name) {
|
|
15
15
|
return name.replace(/[\/\?%*:|"<>]/g, "-");
|
|
@@ -42,53 +42,12 @@ class FileSystemStorage {
|
|
|
42
42
|
throw new Error("Tags must be an array if provided");
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
ensureInitialized() {
|
|
46
|
-
if (this.initialized) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
this.taskOMatic = config_1.configManager.getTaskOMaticDir();
|
|
50
|
-
this.tasksFile = (0, path_1.join)(this.taskOMatic, "tasks.json");
|
|
51
|
-
this.initialized = true;
|
|
52
|
-
}
|
|
53
|
-
async ensureDirectories() {
|
|
54
|
-
this.ensureInitialized();
|
|
55
|
-
if (!this.taskOMatic) {
|
|
56
|
-
throw new Error("FileSystemStorage not initialized");
|
|
57
|
-
}
|
|
58
|
-
const dirs = [
|
|
59
|
-
"prd",
|
|
60
|
-
"logs",
|
|
61
|
-
"tasks",
|
|
62
|
-
"tasks/enhanced",
|
|
63
|
-
"docs",
|
|
64
|
-
"docs/tasks",
|
|
65
|
-
"plans",
|
|
66
|
-
];
|
|
67
|
-
for (const dir of dirs) {
|
|
68
|
-
const fullPath = (0, path_1.join)(this.taskOMatic, dir);
|
|
69
|
-
try {
|
|
70
|
-
await (0, promises_1.mkdir)(fullPath, { recursive: true });
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
if (error.code !== "EEXIST")
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
45
|
async loadTasksData() {
|
|
79
|
-
this.ensureInitialized();
|
|
80
|
-
if (!this.tasksFile) {
|
|
81
|
-
return { tasks: [], nextId: 1 };
|
|
82
|
-
}
|
|
83
46
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return { tasks: [], nextId: 1 };
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
const content = await (0, promises_1.readFile)(this.tasksFile, "utf-8");
|
|
47
|
+
const content = await this.callbacks.read("tasks.json");
|
|
48
|
+
if (!content) {
|
|
49
|
+
return { tasks: [], nextId: 1 };
|
|
50
|
+
}
|
|
92
51
|
return JSON.parse(content);
|
|
93
52
|
}
|
|
94
53
|
catch (error) {
|
|
@@ -97,12 +56,8 @@ class FileSystemStorage {
|
|
|
97
56
|
}
|
|
98
57
|
}
|
|
99
58
|
async saveTasksData(data) {
|
|
100
|
-
this.ensureInitialized();
|
|
101
|
-
if (!this.tasksFile) {
|
|
102
|
-
throw new Error("FileSystemStorage not initialized");
|
|
103
|
-
}
|
|
104
59
|
try {
|
|
105
|
-
await
|
|
60
|
+
await this.callbacks.write("tasks.json", JSON.stringify(data, null, 2));
|
|
106
61
|
}
|
|
107
62
|
catch (error) {
|
|
108
63
|
throw new Error(`Failed to save tasks data: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -151,7 +106,6 @@ class FileSystemStorage {
|
|
|
151
106
|
}
|
|
152
107
|
async createTask(task, aiMetadata) {
|
|
153
108
|
this.validateTaskRequest(task);
|
|
154
|
-
await this.ensureDirectories();
|
|
155
109
|
const data = await this.loadTasksData();
|
|
156
110
|
let id;
|
|
157
111
|
if (task.parentId) {
|
|
@@ -322,23 +276,11 @@ class FileSystemStorage {
|
|
|
322
276
|
}
|
|
323
277
|
return false;
|
|
324
278
|
}
|
|
325
|
-
getAIMetadataFile() {
|
|
326
|
-
this.ensureInitialized();
|
|
327
|
-
if (!this.taskOMatic) {
|
|
328
|
-
throw new Error("FileSystemStorage not initialized");
|
|
329
|
-
}
|
|
330
|
-
return (0, path_1.join)(this.taskOMatic, "ai-metadata.json");
|
|
331
|
-
}
|
|
332
279
|
async loadAIMetadata() {
|
|
333
|
-
const metadataFile = this.getAIMetadataFile();
|
|
334
|
-
try {
|
|
335
|
-
await (0, promises_1.stat)(metadataFile);
|
|
336
|
-
}
|
|
337
|
-
catch {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
280
|
try {
|
|
341
|
-
const content = await
|
|
281
|
+
const content = await this.callbacks.read("ai-metadata.json");
|
|
282
|
+
if (!content)
|
|
283
|
+
return [];
|
|
342
284
|
return JSON.parse(content);
|
|
343
285
|
}
|
|
344
286
|
catch (error) {
|
|
@@ -347,8 +289,7 @@ class FileSystemStorage {
|
|
|
347
289
|
}
|
|
348
290
|
}
|
|
349
291
|
async saveAIMetadata(metadata) {
|
|
350
|
-
|
|
351
|
-
await (0, promises_1.writeFile)(metadataFile, JSON.stringify(metadata, null, 2));
|
|
292
|
+
await this.callbacks.write("ai-metadata.json", JSON.stringify(metadata, null, 2));
|
|
352
293
|
}
|
|
353
294
|
async getTaskAIMetadata(taskId) {
|
|
354
295
|
const metadata = await this.loadAIMetadata();
|
|
@@ -375,14 +316,9 @@ class FileSystemStorage {
|
|
|
375
316
|
if (typeof content !== "string") {
|
|
376
317
|
throw new Error("Content must be a string");
|
|
377
318
|
}
|
|
378
|
-
await this.ensureDirectories();
|
|
379
|
-
if (!this.taskOMatic) {
|
|
380
|
-
throw new Error("FileSystemStorage not initialized");
|
|
381
|
-
}
|
|
382
319
|
const contentFileName = `tasks/${taskId}.md`;
|
|
383
|
-
const contentFilePath = (0, path_1.join)(this.taskOMatic, contentFileName);
|
|
384
320
|
try {
|
|
385
|
-
await
|
|
321
|
+
await this.callbacks.write(contentFileName, content);
|
|
386
322
|
}
|
|
387
323
|
catch (error) {
|
|
388
324
|
throw new Error(`Failed to save task content: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -394,14 +330,9 @@ class FileSystemStorage {
|
|
|
394
330
|
if (typeof content !== "string") {
|
|
395
331
|
throw new Error("Content must be a string");
|
|
396
332
|
}
|
|
397
|
-
await this.ensureDirectories();
|
|
398
|
-
if (!this.taskOMatic) {
|
|
399
|
-
throw new Error("FileSystemStorage not initialized");
|
|
400
|
-
}
|
|
401
333
|
const contentFileName = `tasks/enhanced/${taskId}.md`;
|
|
402
|
-
const contentFilePath = (0, path_1.join)(this.taskOMatic, contentFileName);
|
|
403
334
|
try {
|
|
404
|
-
await
|
|
335
|
+
await this.callbacks.write(contentFileName, content);
|
|
405
336
|
}
|
|
406
337
|
catch (error) {
|
|
407
338
|
throw new Error(`Failed to save enhanced task content: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -410,19 +341,9 @@ class FileSystemStorage {
|
|
|
410
341
|
}
|
|
411
342
|
async getTaskContent(taskId) {
|
|
412
343
|
this.validateTaskId(taskId);
|
|
413
|
-
if (!this.taskOMatic) {
|
|
414
|
-
throw new Error("FileSystemStorage not initialized");
|
|
415
|
-
}
|
|
416
344
|
const contentFileName = `tasks/${taskId}.md`;
|
|
417
|
-
const contentFilePath = (0, path_1.join)(this.taskOMatic, contentFileName);
|
|
418
|
-
try {
|
|
419
|
-
await (0, promises_1.stat)(contentFilePath);
|
|
420
|
-
}
|
|
421
|
-
catch {
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
345
|
try {
|
|
425
|
-
return await
|
|
346
|
+
return await this.callbacks.read(contentFileName);
|
|
426
347
|
}
|
|
427
348
|
catch (error) {
|
|
428
349
|
console.error(`Failed to read task content for ${taskId}:`, error);
|
|
@@ -431,51 +352,20 @@ class FileSystemStorage {
|
|
|
431
352
|
}
|
|
432
353
|
async deleteTaskContent(taskId) {
|
|
433
354
|
this.validateTaskId(taskId);
|
|
434
|
-
if (!this.taskOMatic) {
|
|
435
|
-
throw new Error("FileSystemStorage not initialized");
|
|
436
|
-
}
|
|
437
355
|
const contentFileName = `tasks/${taskId}.md`;
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
await (0, promises_1.stat)(contentFilePath);
|
|
441
|
-
await (0, promises_1.unlink)(contentFilePath);
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
// Ignore if file doesn't exist
|
|
445
|
-
}
|
|
356
|
+
await this.callbacks.delete(contentFileName);
|
|
446
357
|
}
|
|
447
358
|
async saveContext7Documentation(library, query, content) {
|
|
448
|
-
await this.ensureDirectories();
|
|
449
|
-
if (!this.taskOMatic) {
|
|
450
|
-
throw new Error("FileSystemStorage not initialized");
|
|
451
|
-
}
|
|
452
359
|
const sanitizedLibrary = this.sanitizeForFilename(library);
|
|
453
360
|
const sanitizedQuery = this.sanitizeForFilename(query);
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
catch (error) {
|
|
459
|
-
if (error.code !== "EEXIST")
|
|
460
|
-
throw error;
|
|
461
|
-
}
|
|
462
|
-
const filePath = (0, path_1.join)(libraryDir, `${sanitizedQuery}.md`);
|
|
463
|
-
await (0, promises_1.writeFile)(filePath, content, "utf-8");
|
|
464
|
-
return `docs/${sanitizedLibrary}/${sanitizedQuery}.md`;
|
|
361
|
+
const filePath = `docs/${sanitizedLibrary}/${sanitizedQuery}.md`;
|
|
362
|
+
await this.callbacks.write(filePath, content);
|
|
363
|
+
return filePath;
|
|
465
364
|
}
|
|
466
365
|
async getDocumentationFile(fileName) {
|
|
467
|
-
|
|
468
|
-
throw new Error("FileSystemStorage not initialized");
|
|
469
|
-
}
|
|
470
|
-
const filePath = (0, path_1.join)(this.taskOMatic, "docs", fileName);
|
|
366
|
+
const filePath = `docs/${fileName}`;
|
|
471
367
|
try {
|
|
472
|
-
await
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
return null;
|
|
476
|
-
}
|
|
477
|
-
try {
|
|
478
|
-
return await (0, promises_1.readFile)(filePath, "utf-8");
|
|
368
|
+
return await this.callbacks.read(filePath);
|
|
479
369
|
}
|
|
480
370
|
catch (error) {
|
|
481
371
|
console.error(`Failed to read documentation file ${fileName}:`, error);
|
|
@@ -483,32 +373,12 @@ class FileSystemStorage {
|
|
|
483
373
|
}
|
|
484
374
|
}
|
|
485
375
|
async listDocumentationFiles() {
|
|
486
|
-
this.ensureInitialized();
|
|
487
376
|
try {
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return [];
|
|
494
|
-
}
|
|
495
|
-
const files = [];
|
|
496
|
-
const scanDirectory = async (dir, basePath = "") => {
|
|
497
|
-
const items = await (0, promises_1.readdir)(dir);
|
|
498
|
-
for (const item of items) {
|
|
499
|
-
const fullPath = (0, path_1.join)(dir, item);
|
|
500
|
-
const relativePath = basePath ? (0, path_1.join)(basePath, item) : item;
|
|
501
|
-
const stats = await (0, promises_1.stat)(fullPath);
|
|
502
|
-
if (stats.isDirectory() && item !== "_cache") {
|
|
503
|
-
await scanDirectory(fullPath, relativePath);
|
|
504
|
-
}
|
|
505
|
-
else if (item.endsWith(".md") || item.endsWith(".txt")) {
|
|
506
|
-
files.push(relativePath);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
};
|
|
510
|
-
await scanDirectory(docsDir);
|
|
511
|
-
return files;
|
|
377
|
+
const files = await this.callbacks.list("docs/");
|
|
378
|
+
// Filter for markdown/text files and remove "docs/" prefix for compatibility
|
|
379
|
+
return files
|
|
380
|
+
.filter((f) => f.endsWith(".md") || f.endsWith(".txt"))
|
|
381
|
+
.map((f) => (f.startsWith("docs/") ? f.substring(5) : f));
|
|
512
382
|
}
|
|
513
383
|
catch (error) {
|
|
514
384
|
console.error("Failed to list documentation files:", error);
|
|
@@ -523,12 +393,8 @@ class FileSystemStorage {
|
|
|
523
393
|
if ("content" in task && task.content && !task.contentFile) {
|
|
524
394
|
const oldContent = task.content;
|
|
525
395
|
if (oldContent.length > 200) {
|
|
526
|
-
if (!this.taskOMatic) {
|
|
527
|
-
throw new Error("FileSystemStorage not initialized");
|
|
528
|
-
}
|
|
529
396
|
const contentFile = `tasks/${task.id}.md`;
|
|
530
|
-
|
|
531
|
-
await (0, promises_1.writeFile)(contentFilePath, oldContent, "utf-8");
|
|
397
|
+
await this.callbacks.write(contentFile, oldContent);
|
|
532
398
|
updatedTask.contentFile = contentFile;
|
|
533
399
|
updatedTask.description =
|
|
534
400
|
task.description || oldContent.substring(0, 200) + "...";
|
|
@@ -557,20 +423,17 @@ class FileSystemStorage {
|
|
|
557
423
|
const validContentFiles = new Set(allTasks
|
|
558
424
|
.filter((task) => task.contentFile)
|
|
559
425
|
.map((task) => task.contentFile));
|
|
560
|
-
if (!this.taskOMatic) {
|
|
561
|
-
throw new Error("FileSystemStorage not initialized");
|
|
562
|
-
}
|
|
563
|
-
const tasksDir = (0, path_1.join)(this.taskOMatic, "tasks");
|
|
564
426
|
let cleanedCount = 0;
|
|
565
427
|
try {
|
|
566
|
-
const files = await
|
|
428
|
+
const files = await this.callbacks.list("tasks/");
|
|
567
429
|
for (const file of files) {
|
|
430
|
+
// file is a full key like "tasks/123.md"
|
|
431
|
+
// validContentFiles contains relative paths like "tasks/123.md"
|
|
432
|
+
// So we can check directly
|
|
568
433
|
if (file.endsWith(".md")) {
|
|
569
|
-
|
|
570
|
-
if (!validContentFiles.has(contentFile)) {
|
|
571
|
-
const filePath = (0, path_1.join)(tasksDir, file);
|
|
434
|
+
if (!validContentFiles.has(file)) {
|
|
572
435
|
try {
|
|
573
|
-
await
|
|
436
|
+
await this.callbacks.delete(file);
|
|
574
437
|
cleanedCount++;
|
|
575
438
|
console.log(`Cleaned up orphaned content file: ${file}`);
|
|
576
439
|
}
|
|
@@ -589,7 +452,6 @@ class FileSystemStorage {
|
|
|
589
452
|
async validateStorageIntegrity() {
|
|
590
453
|
const issues = [];
|
|
591
454
|
try {
|
|
592
|
-
await this.ensureDirectories();
|
|
593
455
|
const data = await this.loadTasksData();
|
|
594
456
|
if (!Array.isArray(data.tasks)) {
|
|
595
457
|
issues.push("Tasks data is not an array");
|
|
@@ -623,11 +485,7 @@ class FileSystemStorage {
|
|
|
623
485
|
}
|
|
624
486
|
async savePlan(taskId, plan) {
|
|
625
487
|
this.validateTaskId(taskId);
|
|
626
|
-
|
|
627
|
-
if (!this.taskOMatic) {
|
|
628
|
-
throw new Error("FileSystemStorage not initialized");
|
|
629
|
-
}
|
|
630
|
-
const planFile = (0, path_1.join)(this.taskOMatic, "plans", `${taskId}.json`);
|
|
488
|
+
const planFile = `plans/${taskId}.json`;
|
|
631
489
|
try {
|
|
632
490
|
const planData = {
|
|
633
491
|
taskId,
|
|
@@ -635,7 +493,7 @@ class FileSystemStorage {
|
|
|
635
493
|
createdAt: Date.now(),
|
|
636
494
|
updatedAt: Date.now(),
|
|
637
495
|
};
|
|
638
|
-
await
|
|
496
|
+
await this.callbacks.write(planFile, JSON.stringify(planData, null, 2));
|
|
639
497
|
}
|
|
640
498
|
catch (error) {
|
|
641
499
|
throw new Error(`Failed to save plan for task ${taskId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -643,19 +501,11 @@ class FileSystemStorage {
|
|
|
643
501
|
}
|
|
644
502
|
async getPlan(taskId) {
|
|
645
503
|
this.validateTaskId(taskId);
|
|
646
|
-
|
|
647
|
-
if (!this.taskOMatic) {
|
|
648
|
-
throw new Error("FileSystemStorage not initialized");
|
|
649
|
-
}
|
|
650
|
-
const planFile = (0, path_1.join)(this.taskOMatic, "plans", `${taskId}.json`);
|
|
651
|
-
try {
|
|
652
|
-
await (0, promises_1.stat)(planFile);
|
|
653
|
-
}
|
|
654
|
-
catch {
|
|
655
|
-
return null;
|
|
656
|
-
}
|
|
504
|
+
const planFile = `plans/${taskId}.json`;
|
|
657
505
|
try {
|
|
658
|
-
const content = await
|
|
506
|
+
const content = await this.callbacks.read(planFile);
|
|
507
|
+
if (!content)
|
|
508
|
+
return null;
|
|
659
509
|
return JSON.parse(content);
|
|
660
510
|
}
|
|
661
511
|
catch (error) {
|
|
@@ -663,23 +513,13 @@ class FileSystemStorage {
|
|
|
663
513
|
}
|
|
664
514
|
}
|
|
665
515
|
async listPlans() {
|
|
666
|
-
this.ensureInitialized();
|
|
667
|
-
if (!this.taskOMatic) {
|
|
668
|
-
throw new Error("FileSystemStorage not initialized");
|
|
669
|
-
}
|
|
670
|
-
const plansDir = (0, path_1.join)(this.taskOMatic, "plans");
|
|
671
|
-
try {
|
|
672
|
-
await (0, promises_1.stat)(plansDir);
|
|
673
|
-
}
|
|
674
|
-
catch {
|
|
675
|
-
return [];
|
|
676
|
-
}
|
|
677
516
|
try {
|
|
678
517
|
const plans = [];
|
|
679
|
-
const files = await
|
|
518
|
+
const files = await this.callbacks.list("plans/");
|
|
680
519
|
for (const file of files) {
|
|
681
520
|
if (file.endsWith(".json")) {
|
|
682
|
-
|
|
521
|
+
// file is "plans/123.json"
|
|
522
|
+
const taskId = file.replace("plans/", "").replace(".json", "");
|
|
683
523
|
const planData = await this.getPlan(taskId);
|
|
684
524
|
if (planData) {
|
|
685
525
|
plans.push({
|
|
@@ -699,14 +539,9 @@ class FileSystemStorage {
|
|
|
699
539
|
}
|
|
700
540
|
async deletePlan(taskId) {
|
|
701
541
|
this.validateTaskId(taskId);
|
|
702
|
-
|
|
703
|
-
if (!this.taskOMatic) {
|
|
704
|
-
throw new Error("FileSystemStorage not initialized");
|
|
705
|
-
}
|
|
706
|
-
const planFile = (0, path_1.join)(this.taskOMatic, "plans", `${taskId}.json`);
|
|
542
|
+
const planFile = `plans/${taskId}.json`;
|
|
707
543
|
try {
|
|
708
|
-
await
|
|
709
|
-
await (0, promises_1.unlink)(planFile);
|
|
544
|
+
await this.callbacks.delete(planFile);
|
|
710
545
|
return true;
|
|
711
546
|
}
|
|
712
547
|
catch (error) {
|
|
@@ -718,14 +553,9 @@ class FileSystemStorage {
|
|
|
718
553
|
if (typeof documentation !== "string") {
|
|
719
554
|
throw new Error("Documentation must be a string");
|
|
720
555
|
}
|
|
721
|
-
await this.ensureDirectories();
|
|
722
|
-
if (!this.taskOMatic) {
|
|
723
|
-
throw new Error("FileSystemStorage not initialized");
|
|
724
|
-
}
|
|
725
556
|
const documentationFileName = `docs/tasks/${taskId}.md`;
|
|
726
|
-
const documentationFilePath = (0, path_1.join)(this.taskOMatic, documentationFileName);
|
|
727
557
|
try {
|
|
728
|
-
await
|
|
558
|
+
await this.callbacks.write(documentationFileName, documentation);
|
|
729
559
|
}
|
|
730
560
|
catch (error) {
|
|
731
561
|
throw new Error(`Failed to save task documentation: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -734,19 +564,9 @@ class FileSystemStorage {
|
|
|
734
564
|
}
|
|
735
565
|
async getTaskDocumentation(taskId) {
|
|
736
566
|
this.validateTaskId(taskId);
|
|
737
|
-
if (!this.taskOMatic) {
|
|
738
|
-
throw new Error("FileSystemStorage not initialized");
|
|
739
|
-
}
|
|
740
567
|
const documentationFileName = `docs/tasks/${taskId}.md`;
|
|
741
|
-
const documentationFilePath = (0, path_1.join)(this.taskOMatic, documentationFileName);
|
|
742
|
-
try {
|
|
743
|
-
await (0, promises_1.stat)(documentationFilePath);
|
|
744
|
-
}
|
|
745
|
-
catch {
|
|
746
|
-
return null;
|
|
747
|
-
}
|
|
748
568
|
try {
|
|
749
|
-
return await
|
|
569
|
+
return await this.callbacks.read(documentationFileName);
|
|
750
570
|
}
|
|
751
571
|
catch (error) {
|
|
752
572
|
console.error(`Failed to read task documentation for ${taskId}:`, error);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple storage callbacks - just read/write key-value pairs
|
|
3
|
+
* This allows the library to be used with ANY storage backend (DB, Redis, S3, etc.)
|
|
4
|
+
*/
|
|
5
|
+
export interface StorageCallbacks {
|
|
6
|
+
read: (key: string) => Promise<string | null>;
|
|
7
|
+
write: (key: string, value: string) => Promise<void>;
|
|
8
|
+
delete: (key: string) => Promise<void>;
|
|
9
|
+
list: (prefix?: string) => Promise<string[]>;
|
|
10
|
+
exists: (key: string) => Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Default file system callbacks (Node.js environment)
|
|
14
|
+
* Uses fs/promises to implement the storage interface
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFileSystemCallbacks(baseDir?: string): StorageCallbacks;
|
|
17
|
+
//# sourceMappingURL=storage-callbacks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-callbacks.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/storage-callbacks.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAE/B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAG9C,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGrD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAG7C,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,MAAsB,GAC9B,gBAAgB,CAwFlB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFileSystemCallbacks = createFileSystemCallbacks;
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
/**
|
|
8
|
+
* Default file system callbacks (Node.js environment)
|
|
9
|
+
* Uses fs/promises to implement the storage interface
|
|
10
|
+
*/
|
|
11
|
+
function createFileSystemCallbacks(baseDir = process.cwd()) {
|
|
12
|
+
const resolvePath = (key) => (0, path_1.join)(baseDir, key);
|
|
13
|
+
const ensureDir = (filePath) => {
|
|
14
|
+
const dir = (0, path_1.dirname)(filePath);
|
|
15
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
16
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
read: async (key) => {
|
|
21
|
+
try {
|
|
22
|
+
const path = resolvePath(key);
|
|
23
|
+
if (!(0, fs_1.existsSync)(path))
|
|
24
|
+
return null;
|
|
25
|
+
return await (0, promises_1.readFile)(path, "utf-8");
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code === "ENOENT")
|
|
29
|
+
return null;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
write: async (key, value) => {
|
|
34
|
+
const path = resolvePath(key);
|
|
35
|
+
ensureDir(path);
|
|
36
|
+
await (0, promises_1.writeFile)(path, value, "utf-8");
|
|
37
|
+
},
|
|
38
|
+
delete: async (key) => {
|
|
39
|
+
try {
|
|
40
|
+
const path = resolvePath(key);
|
|
41
|
+
if ((0, fs_1.existsSync)(path)) {
|
|
42
|
+
await (0, promises_1.unlink)(path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Ignore if already gone
|
|
47
|
+
if (error.code !== "ENOENT")
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
list: async (prefix) => {
|
|
52
|
+
const searchPath = prefix ? resolvePath(prefix) : baseDir;
|
|
53
|
+
try {
|
|
54
|
+
// If it's a directory, recursively list all files in it
|
|
55
|
+
if ((0, fs_1.existsSync)(searchPath) && (await (0, promises_1.stat)(searchPath)).isDirectory()) {
|
|
56
|
+
const files = [];
|
|
57
|
+
const scan = async (dir, currentPrefix) => {
|
|
58
|
+
const items = await (0, promises_1.readdir)(dir);
|
|
59
|
+
for (const item of items) {
|
|
60
|
+
const fullPath = (0, path_1.join)(dir, item);
|
|
61
|
+
const itemPrefix = currentPrefix
|
|
62
|
+
? (0, path_1.join)(currentPrefix, item)
|
|
63
|
+
: item;
|
|
64
|
+
if ((await (0, promises_1.stat)(fullPath)).isDirectory()) {
|
|
65
|
+
await scan(fullPath, itemPrefix);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
files.push(itemPrefix);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
await scan(searchPath, prefix || "");
|
|
73
|
+
return files;
|
|
74
|
+
}
|
|
75
|
+
// If it's a file prefix (e.g. "tasks/task-")
|
|
76
|
+
const dir = (0, path_1.dirname)(searchPath);
|
|
77
|
+
if ((0, fs_1.existsSync)(dir)) {
|
|
78
|
+
const files = await (0, promises_1.readdir)(dir);
|
|
79
|
+
const namePrefix = prefix ? prefix.split("/").pop() || "" : "";
|
|
80
|
+
return files
|
|
81
|
+
.filter((f) => f.startsWith(namePrefix))
|
|
82
|
+
.map((f) => (0, path_1.join)((0, path_1.dirname)(prefix || ""), f));
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
exists: async (key) => {
|
|
91
|
+
return (0, fs_1.existsSync)(resolvePath(key));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAgC,MAAM,UAAU,CAAC;AAgM5E,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgF5E"}
|
|
@@ -10,7 +10,7 @@ const validation_1 = require("./validation");
|
|
|
10
10
|
const ai_service_factory_1 = require("../utils/ai-service-factory");
|
|
11
11
|
const hooks_1 = require("./hooks");
|
|
12
12
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
-
async function executeSingleTask(taskId, tool, dry) {
|
|
13
|
+
async function executeSingleTask(taskId, tool, dry, executorConfig) {
|
|
14
14
|
// Load task
|
|
15
15
|
const task = await tasks_1.taskService.getTask(taskId);
|
|
16
16
|
if (!task) {
|
|
@@ -73,8 +73,8 @@ async function executeSingleTask(taskId, tool, dry) {
|
|
|
73
73
|
await hooks_1.hooks.emit("execution:start", { taskId, tool });
|
|
74
74
|
try {
|
|
75
75
|
// Create executor and run
|
|
76
|
-
const executor = executor_factory_1.ExecutorFactory.create(tool);
|
|
77
|
-
await executor.execute(executionMessage, dry);
|
|
76
|
+
const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
|
|
77
|
+
await executor.execute(executionMessage, dry, executorConfig);
|
|
78
78
|
if (!dry) {
|
|
79
79
|
// Update task status to completed
|
|
80
80
|
await tasks_1.taskService.setTaskStatus(taskId, "completed");
|
|
@@ -97,7 +97,7 @@ async function executeSingleTask(taskId, tool, dry) {
|
|
|
97
97
|
throw error;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
async function executeTaskWithSubtasks(taskId, tool, dry) {
|
|
100
|
+
async function executeTaskWithSubtasks(taskId, tool, dry, executorConfig) {
|
|
101
101
|
const task = await tasks_1.taskService.getTask(taskId);
|
|
102
102
|
if (!task) {
|
|
103
103
|
throw new Error(`Task with ID ${taskId} not found`);
|
|
@@ -115,7 +115,7 @@ async function executeTaskWithSubtasks(taskId, tool, dry) {
|
|
|
115
115
|
const subtask = subtasks[i];
|
|
116
116
|
console.log(chalk_1.default.cyan(`\n[${i + 1}/${subtasks.length}] Executing subtask: ${subtask.title} (${subtask.id})`));
|
|
117
117
|
try {
|
|
118
|
-
await executeTaskWithSubtasks(subtask.id, tool, dry);
|
|
118
|
+
await executeTaskWithSubtasks(subtask.id, tool, dry, executorConfig);
|
|
119
119
|
}
|
|
120
120
|
catch (error) {
|
|
121
121
|
console.error(chalk_1.default.red(`❌ Failed to execute subtask ${subtask.id}: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
@@ -132,7 +132,14 @@ async function executeTaskWithSubtasks(taskId, tool, dry) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
async function executeTask(options) {
|
|
135
|
-
const { taskId, tool = "opencode", message, dry = false, validate = [], } = options;
|
|
135
|
+
const { taskId, tool = "opencode", message, model, continueSession, dry = false, validate = [], } = options;
|
|
136
|
+
// Build executor config from options
|
|
137
|
+
const executorConfig = model || continueSession
|
|
138
|
+
? {
|
|
139
|
+
model,
|
|
140
|
+
continueLastSession: continueSession,
|
|
141
|
+
}
|
|
142
|
+
: undefined;
|
|
136
143
|
// If custom message is provided, execute just this task (ignore subtasks)
|
|
137
144
|
if (message) {
|
|
138
145
|
const task = await tasks_1.taskService.getTask(taskId);
|
|
@@ -147,8 +154,8 @@ async function executeTask(options) {
|
|
|
147
154
|
// Emit execution:start event
|
|
148
155
|
await hooks_1.hooks.emit("execution:start", { taskId, tool });
|
|
149
156
|
try {
|
|
150
|
-
const executor = executor_factory_1.ExecutorFactory.create(tool);
|
|
151
|
-
await executor.execute(message, dry);
|
|
157
|
+
const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
|
|
158
|
+
await executor.execute(message, dry, executorConfig);
|
|
152
159
|
if (!dry) {
|
|
153
160
|
await tasks_1.taskService.setTaskStatus(taskId, "completed");
|
|
154
161
|
console.log(chalk_1.default.green("✅ Task execution completed successfully"));
|
|
@@ -173,7 +180,7 @@ async function executeTask(options) {
|
|
|
173
180
|
return;
|
|
174
181
|
}
|
|
175
182
|
// No custom message - execute recursively with subtasks
|
|
176
|
-
await executeTaskWithSubtasks(taskId, tool, dry);
|
|
183
|
+
await executeTaskWithSubtasks(taskId, tool, dry, executorConfig);
|
|
177
184
|
// Run validations after all subtasks complete
|
|
178
185
|
await (0, validation_1.runValidations)(validate, dry);
|
|
179
186
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAMlB,MAAM,UAAU,CAAC;AAelB;;;GAGG;AACH;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE;IACR,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,OAAO,CAAC;CAChC,EACD,MAAM,GAAE,CACN,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAa,EAC5D,KAAK,GAAE,GAAuB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA+G/C;AA4pBD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAmK5B"}
|