task-o-matic 0.0.9 → 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.
Files changed (110) hide show
  1. package/dist/cli/display/progress.d.ts +1 -1
  2. package/dist/cli/display/progress.d.ts.map +1 -1
  3. package/dist/cli/display/progress.js +16 -13
  4. package/dist/commands/benchmark.js +70 -2
  5. package/dist/commands/init.js +48 -27
  6. package/dist/commands/prd.d.ts.map +1 -1
  7. package/dist/commands/prd.js +201 -0
  8. package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
  9. package/dist/commands/tasks/execute-loop.js +10 -0
  10. package/dist/commands/tasks/execute.d.ts.map +1 -1
  11. package/dist/commands/tasks/execute.js +4 -0
  12. package/dist/commands/workflow.d.ts.map +1 -1
  13. package/dist/commands/workflow.js +56 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -0
  16. package/dist/lib/ai-service/ai-operations.d.ts +13 -10
  17. package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
  18. package/dist/lib/ai-service/ai-operations.js +35 -995
  19. package/dist/lib/ai-service/base-operations.d.ts +13 -0
  20. package/dist/lib/ai-service/base-operations.d.ts.map +1 -0
  21. package/dist/lib/ai-service/base-operations.js +79 -0
  22. package/dist/lib/ai-service/documentation-operations.d.ts +18 -0
  23. package/dist/lib/ai-service/documentation-operations.d.ts.map +1 -0
  24. package/dist/lib/ai-service/documentation-operations.js +291 -0
  25. package/dist/lib/ai-service/prd-operations.d.ts +14 -0
  26. package/dist/lib/ai-service/prd-operations.d.ts.map +1 -0
  27. package/dist/lib/ai-service/prd-operations.js +405 -0
  28. package/dist/lib/ai-service/task-operations.d.ts +12 -0
  29. package/dist/lib/ai-service/task-operations.d.ts.map +1 -0
  30. package/dist/lib/ai-service/task-operations.js +225 -0
  31. package/dist/lib/benchmark/registry.d.ts.map +1 -1
  32. package/dist/lib/benchmark/registry.js +127 -0
  33. package/dist/lib/better-t-stack-cli.d.ts +4 -1
  34. package/dist/lib/better-t-stack-cli.d.ts.map +1 -1
  35. package/dist/lib/better-t-stack-cli.js +126 -5
  36. package/dist/lib/config.d.ts +13 -6
  37. package/dist/lib/config.d.ts.map +1 -1
  38. package/dist/lib/config.js +90 -48
  39. package/dist/lib/context-builder.d.ts +13 -1
  40. package/dist/lib/context-builder.d.ts.map +1 -1
  41. package/dist/lib/context-builder.js +68 -36
  42. package/dist/lib/executors/claude-code-executor.d.ts +5 -2
  43. package/dist/lib/executors/claude-code-executor.d.ts.map +1 -1
  44. package/dist/lib/executors/claude-code-executor.js +30 -3
  45. package/dist/lib/executors/codex-executor.d.ts +5 -2
  46. package/dist/lib/executors/codex-executor.d.ts.map +1 -1
  47. package/dist/lib/executors/codex-executor.js +30 -3
  48. package/dist/lib/executors/executor-factory.d.ts +2 -2
  49. package/dist/lib/executors/executor-factory.d.ts.map +1 -1
  50. package/dist/lib/executors/executor-factory.js +5 -5
  51. package/dist/lib/executors/gemini-executor.d.ts +5 -2
  52. package/dist/lib/executors/gemini-executor.d.ts.map +1 -1
  53. package/dist/lib/executors/gemini-executor.js +30 -3
  54. package/dist/lib/executors/opencode-executor.d.ts +5 -2
  55. package/dist/lib/executors/opencode-executor.d.ts.map +1 -1
  56. package/dist/lib/executors/opencode-executor.js +30 -7
  57. package/dist/lib/index.d.ts +5 -0
  58. package/dist/lib/index.d.ts.map +1 -1
  59. package/dist/lib/index.js +7 -1
  60. package/dist/lib/prompt-builder.d.ts.map +1 -1
  61. package/dist/lib/prompt-builder.js +1 -0
  62. package/dist/lib/storage/file-system.d.ts +3 -7
  63. package/dist/lib/storage/file-system.d.ts.map +1 -1
  64. package/dist/lib/storage/file-system.js +50 -230
  65. package/dist/lib/storage/storage-callbacks.d.ts +17 -0
  66. package/dist/lib/storage/storage-callbacks.d.ts.map +1 -0
  67. package/dist/lib/storage/storage-callbacks.js +94 -0
  68. package/dist/lib/task-execution.d.ts.map +1 -1
  69. package/dist/lib/task-execution.js +16 -9
  70. package/dist/lib/task-loop-execution.d.ts.map +1 -1
  71. package/dist/lib/task-loop-execution.js +207 -8
  72. package/dist/prompts/index.d.ts +2 -0
  73. package/dist/prompts/index.d.ts.map +1 -1
  74. package/dist/prompts/index.js +2 -0
  75. package/dist/prompts/prd-combination.d.ts +2 -0
  76. package/dist/prompts/prd-combination.d.ts.map +1 -0
  77. package/dist/prompts/prd-combination.js +35 -0
  78. package/dist/prompts/prd-generation.d.ts +2 -0
  79. package/dist/prompts/prd-generation.d.ts.map +1 -0
  80. package/dist/prompts/prd-generation.js +49 -0
  81. package/dist/services/prd.d.ts +43 -0
  82. package/dist/services/prd.d.ts.map +1 -1
  83. package/dist/services/prd.js +121 -0
  84. package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
  85. package/dist/services/workflow-ai-assistant.js +1 -40
  86. package/dist/services/workflow.d.ts +10 -0
  87. package/dist/services/workflow.d.ts.map +1 -1
  88. package/dist/services/workflow.js +118 -40
  89. package/dist/test/hooks.test.js +19 -10
  90. package/dist/test/integration/callbacks.test.d.ts +2 -0
  91. package/dist/test/integration/callbacks.test.d.ts.map +1 -0
  92. package/dist/test/integration/callbacks.test.js +64 -0
  93. package/dist/test/task-loop-git.test.js +33 -0
  94. package/dist/test/validation.test.d.ts +2 -0
  95. package/dist/test/validation.test.d.ts.map +1 -0
  96. package/dist/test/validation.test.js +22 -0
  97. package/dist/types/callbacks.d.ts +9 -6
  98. package/dist/types/callbacks.d.ts.map +1 -1
  99. package/dist/types/index.d.ts +17 -2
  100. package/dist/types/index.d.ts.map +1 -1
  101. package/dist/types/workflow-options.d.ts +7 -0
  102. package/dist/types/workflow-options.d.ts.map +1 -1
  103. package/dist/utils/ai-service-factory.d.ts +15 -1
  104. package/dist/utils/ai-service-factory.d.ts.map +1 -1
  105. package/dist/utils/ai-service-factory.js +29 -1
  106. package/dist/utils/streaming-options.d.ts +1 -1
  107. package/dist/utils/streaming-options.d.ts.map +1 -1
  108. package/dist/utils/streaming-options.js +31 -18
  109. package/docs/agents/cli.md +191 -0
  110. 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
- taskOMatic = null;
9
- tasksFile = null;
10
- initialized = false;
11
- constructor() {
12
- // Pure constructor - NO side effects
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
- // Check if file exists using access or stat, or just try reading
85
- await (0, promises_1.stat)(this.tasksFile);
86
- }
87
- catch {
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 (0, promises_1.writeFile)(this.tasksFile, JSON.stringify(data, null, 2));
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 (0, promises_1.readFile)(metadataFile, "utf-8");
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
- const metadataFile = this.getAIMetadataFile();
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 (0, promises_1.writeFile)(contentFilePath, content, "utf-8");
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 (0, promises_1.writeFile)(contentFilePath, content, "utf-8");
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 (0, promises_1.readFile)(contentFilePath, "utf-8");
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
- const contentFilePath = (0, path_1.join)(this.taskOMatic, contentFileName);
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 libraryDir = (0, path_1.join)(this.taskOMatic, "docs", sanitizedLibrary);
455
- try {
456
- await (0, promises_1.mkdir)(libraryDir, { recursive: true });
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
- if (!this.taskOMatic) {
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 (0, promises_1.stat)(filePath);
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 docsDir = (0, path_1.join)(this.taskOMatic, "docs");
489
- try {
490
- await (0, promises_1.stat)(docsDir);
491
- }
492
- catch {
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
- const contentFilePath = (0, path_1.join)(this.taskOMatic, contentFile);
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 (0, promises_1.readdir)(tasksDir);
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
- const contentFile = `tasks/${file}`;
570
- if (!validContentFiles.has(contentFile)) {
571
- const filePath = (0, path_1.join)(tasksDir, file);
434
+ if (!validContentFiles.has(file)) {
572
435
  try {
573
- await (0, promises_1.unlink)(filePath);
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
- this.ensureInitialized();
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 (0, promises_1.writeFile)(planFile, JSON.stringify(planData, null, 2));
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
- this.ensureInitialized();
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 (0, promises_1.readFile)(planFile, "utf-8");
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 (0, promises_1.readdir)(plansDir);
518
+ const files = await this.callbacks.list("plans/");
680
519
  for (const file of files) {
681
520
  if (file.endsWith(".json")) {
682
- const taskId = file.replace(".json", "");
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
- this.ensureInitialized();
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 (0, promises_1.stat)(planFile);
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 (0, promises_1.writeFile)(documentationFilePath, documentation, "utf-8");
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 (0, promises_1.readFile)(documentationFilePath, "utf-8");
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,EAAgB,MAAM,UAAU,CAAC;AA8L5D,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE5E"}
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,EAIlB,MAAM,UAAU,CAAC;AAYlB;;;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;AAsXD;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CA0K5B"}
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"}