sqlew 3.1.2 → 3.2.3

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 (70) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README.md +58 -3
  3. package/assets/schema.sql +28 -1
  4. package/dist/database.d.ts +65 -0
  5. package/dist/database.d.ts.map +1 -1
  6. package/dist/database.js +190 -0
  7. package/dist/database.js.map +1 -1
  8. package/dist/index.js +47 -1005
  9. package/dist/index.js.map +1 -1
  10. package/dist/migrations/add-decision-context.d.ts +28 -0
  11. package/dist/migrations/add-decision-context.d.ts.map +1 -0
  12. package/dist/migrations/add-decision-context.js +125 -0
  13. package/dist/migrations/add-decision-context.js.map +1 -0
  14. package/dist/migrations/add-task-dependencies.d.ts +26 -0
  15. package/dist/migrations/add-task-dependencies.d.ts.map +1 -0
  16. package/dist/migrations/add-task-dependencies.js +94 -0
  17. package/dist/migrations/add-task-dependencies.js.map +1 -0
  18. package/dist/migrations/index.d.ts +3 -1
  19. package/dist/migrations/index.d.ts.map +1 -1
  20. package/dist/migrations/index.js +32 -2
  21. package/dist/migrations/index.js.map +1 -1
  22. package/dist/schema.js +2 -2
  23. package/dist/schema.js.map +1 -1
  24. package/dist/tests/migrations/test-v3.2-migration.d.ts +6 -0
  25. package/dist/tests/migrations/test-v3.2-migration.d.ts.map +1 -0
  26. package/dist/tests/migrations/test-v3.2-migration.js +191 -0
  27. package/dist/tests/migrations/test-v3.2-migration.js.map +1 -0
  28. package/dist/tests/tasks.dependencies.test.d.ts +7 -0
  29. package/dist/tests/tasks.dependencies.test.d.ts.map +1 -0
  30. package/dist/tests/tasks.dependencies.test.js +613 -0
  31. package/dist/tests/tasks.dependencies.test.js.map +1 -0
  32. package/dist/tools/config.d.ts +10 -0
  33. package/dist/tools/config.d.ts.map +1 -1
  34. package/dist/tools/config.js +105 -0
  35. package/dist/tools/config.js.map +1 -1
  36. package/dist/tools/constraints.d.ts +10 -0
  37. package/dist/tools/constraints.d.ts.map +1 -1
  38. package/dist/tools/constraints.js +167 -0
  39. package/dist/tools/constraints.js.map +1 -1
  40. package/dist/tools/context.d.ts +29 -2
  41. package/dist/tools/context.d.ts.map +1 -1
  42. package/dist/tools/context.js +442 -106
  43. package/dist/tools/context.js.map +1 -1
  44. package/dist/tools/files.d.ts +8 -0
  45. package/dist/tools/files.d.ts.map +1 -1
  46. package/dist/tools/files.js +125 -0
  47. package/dist/tools/files.js.map +1 -1
  48. package/dist/tools/messaging.d.ts +8 -0
  49. package/dist/tools/messaging.d.ts.map +1 -1
  50. package/dist/tools/messaging.js +134 -0
  51. package/dist/tools/messaging.js.map +1 -1
  52. package/dist/tools/tasks.d.ts +32 -0
  53. package/dist/tools/tasks.d.ts.map +1 -1
  54. package/dist/tools/tasks.js +651 -8
  55. package/dist/tools/tasks.js.map +1 -1
  56. package/dist/tools/utils.d.ts +10 -0
  57. package/dist/tools/utils.d.ts.map +1 -1
  58. package/dist/tools/utils.js +179 -21
  59. package/dist/tools/utils.js.map +1 -1
  60. package/dist/types.d.ts +26 -0
  61. package/dist/types.d.ts.map +1 -1
  62. package/docs/AI_AGENT_GUIDE.md +25 -3
  63. package/docs/DECISION_CONTEXT.md +474 -0
  64. package/docs/HELP_PREVIEW_COMPARISON.md +259 -0
  65. package/docs/TASK_ACTIONS.md +311 -10
  66. package/docs/TASK_DEPENDENCIES.md +748 -0
  67. package/docs/TASK_LINKING.md +188 -8
  68. package/docs/TOOL_REFERENCE.md +158 -1
  69. package/docs/WORKFLOWS.md +25 -3
  70. package/package.json +4 -2
@@ -317,6 +317,58 @@ export function updateTask(params) {
317
317
  throw new Error(`Failed to update task: ${message}`);
318
318
  }
319
319
  }
320
+ /**
321
+ * Internal helper: Query task dependencies (used by getTask and getDependencies)
322
+ */
323
+ function queryTaskDependencies(db, taskId, includeDetails = false) {
324
+ // Build query based on include_details flag
325
+ let selectFields;
326
+ if (includeDetails) {
327
+ // Include description from t_task_details
328
+ selectFields = `
329
+ t.id,
330
+ t.title,
331
+ s.name as status,
332
+ t.priority,
333
+ aa.name as assigned_to,
334
+ t.created_ts,
335
+ t.updated_ts,
336
+ td.description
337
+ `;
338
+ }
339
+ else {
340
+ // Metadata only (token-efficient)
341
+ selectFields = `
342
+ t.id,
343
+ t.title,
344
+ s.name as status,
345
+ t.priority
346
+ `;
347
+ }
348
+ // Get blockers (tasks that block this task)
349
+ const blockersQuery = `
350
+ SELECT ${selectFields}
351
+ FROM t_tasks t
352
+ JOIN t_task_dependencies d ON t.id = d.blocker_task_id
353
+ LEFT JOIN m_task_statuses s ON t.status_id = s.id
354
+ LEFT JOIN m_agents aa ON t.assigned_agent_id = aa.id
355
+ ${includeDetails ? 'LEFT JOIN t_task_details td ON t.id = td.task_id' : ''}
356
+ WHERE d.blocked_task_id = ?
357
+ `;
358
+ const blockers = db.prepare(blockersQuery).all(taskId);
359
+ // Get blocking (tasks this task blocks)
360
+ const blockingQuery = `
361
+ SELECT ${selectFields}
362
+ FROM t_tasks t
363
+ JOIN t_task_dependencies d ON t.id = d.blocked_task_id
364
+ LEFT JOIN m_task_statuses s ON t.status_id = s.id
365
+ LEFT JOIN m_agents aa ON t.assigned_agent_id = aa.id
366
+ ${includeDetails ? 'LEFT JOIN t_task_details td ON t.id = td.task_id' : ''}
367
+ WHERE d.blocker_task_id = ?
368
+ `;
369
+ const blocking = db.prepare(blockingQuery).all(taskId);
370
+ return { blockers, blocking };
371
+ }
320
372
  /**
321
373
  * Get full task details
322
374
  */
@@ -389,7 +441,8 @@ export function getTask(params) {
389
441
  WHERE tfl.task_id = ?
390
442
  `);
391
443
  const files = filesStmt.all(params.task_id).map((row) => row.path);
392
- return {
444
+ // Build result
445
+ const result = {
393
446
  found: true,
394
447
  task: {
395
448
  ...task,
@@ -399,6 +452,15 @@ export function getTask(params) {
399
452
  linked_files: files
400
453
  }
401
454
  };
455
+ // Include dependencies if requested (token-efficient, metadata-only)
456
+ if (params.include_dependencies) {
457
+ const deps = queryTaskDependencies(db, params.task_id, false);
458
+ result.task.dependencies = {
459
+ blockers: deps.blockers,
460
+ blocking: deps.blocking
461
+ };
462
+ }
463
+ return result;
402
464
  }
403
465
  catch (error) {
404
466
  const message = error instanceof Error ? error.message : String(error);
@@ -413,36 +475,61 @@ export function listTasks(params = {}) {
413
475
  try {
414
476
  // Run auto-stale detection before listing
415
477
  const transitionCount = detectAndTransitionStaleTasks(db);
416
- // Build query
417
- let query = 'SELECT * FROM v_task_board WHERE 1=1';
478
+ // Build query with optional dependency counts
479
+ let query;
480
+ if (params.include_dependency_counts) {
481
+ // Include dependency counts with LEFT JOINs
482
+ query = `
483
+ SELECT
484
+ vt.*,
485
+ COALESCE(blockers.blocked_by_count, 0) as blocked_by_count,
486
+ COALESCE(blocking.blocking_count, 0) as blocking_count
487
+ FROM v_task_board vt
488
+ LEFT JOIN (
489
+ SELECT blocked_task_id, COUNT(*) as blocked_by_count
490
+ FROM t_task_dependencies
491
+ GROUP BY blocked_task_id
492
+ ) blockers ON vt.id = blockers.blocked_task_id
493
+ LEFT JOIN (
494
+ SELECT blocker_task_id, COUNT(*) as blocking_count
495
+ FROM t_task_dependencies
496
+ GROUP BY blocker_task_id
497
+ ) blocking ON vt.id = blocking.blocker_task_id
498
+ WHERE 1=1
499
+ `;
500
+ }
501
+ else {
502
+ // Standard query without dependency counts
503
+ query = 'SELECT * FROM v_task_board WHERE 1=1';
504
+ }
418
505
  const queryParams = [];
419
506
  // Filter by status
420
507
  if (params.status) {
421
508
  if (!STATUS_TO_ID[params.status]) {
422
509
  throw new Error(`Invalid status: ${params.status}. Must be one of: todo, in_progress, waiting_review, blocked, done, archived`);
423
510
  }
424
- query += ' AND status = ?';
511
+ query += params.include_dependency_counts ? ' AND vt.status = ?' : ' AND status = ?';
425
512
  queryParams.push(params.status);
426
513
  }
427
514
  // Filter by assigned agent
428
515
  if (params.assigned_agent) {
429
- query += ' AND assigned_to = ?';
516
+ query += params.include_dependency_counts ? ' AND vt.assigned_to = ?' : ' AND assigned_to = ?';
430
517
  queryParams.push(params.assigned_agent);
431
518
  }
432
519
  // Filter by layer
433
520
  if (params.layer) {
434
- query += ' AND layer = ?';
521
+ query += params.include_dependency_counts ? ' AND vt.layer = ?' : ' AND layer = ?';
435
522
  queryParams.push(params.layer);
436
523
  }
437
524
  // Filter by tags
438
525
  if (params.tags && params.tags.length > 0) {
439
526
  for (const tag of params.tags) {
440
- query += ' AND tags LIKE ?';
527
+ query += params.include_dependency_counts ? ' AND vt.tags LIKE ?' : ' AND tags LIKE ?';
441
528
  queryParams.push(`%${tag}%`);
442
529
  }
443
530
  }
444
531
  // Order by updated timestamp (most recent first)
445
- query += ' ORDER BY updated_ts DESC';
532
+ query += params.include_dependency_counts ? ' ORDER BY vt.updated_ts DESC' : ' ORDER BY updated_ts DESC';
446
533
  // Pagination
447
534
  const limit = params.limit !== undefined ? params.limit : 50;
448
535
  const offset = params.offset || 0;
@@ -670,6 +757,167 @@ export function archiveTask(params) {
670
757
  throw new Error(`Failed to archive task: ${message}`);
671
758
  }
672
759
  }
760
+ /**
761
+ * Add dependency (blocking relationship) between tasks
762
+ */
763
+ export function addDependency(params) {
764
+ const db = getDatabase();
765
+ if (!params.blocker_task_id) {
766
+ throw new Error('Parameter "blocker_task_id" is required');
767
+ }
768
+ if (!params.blocked_task_id) {
769
+ throw new Error('Parameter "blocked_task_id" is required');
770
+ }
771
+ try {
772
+ return transaction(db, () => {
773
+ // Validation 1: No self-dependencies
774
+ if (params.blocker_task_id === params.blocked_task_id) {
775
+ throw new Error('Self-dependency not allowed');
776
+ }
777
+ // Validation 2: Both tasks must exist and check if archived
778
+ const blockerTask = db.prepare('SELECT id, status_id FROM t_tasks WHERE id = ?').get(params.blocker_task_id);
779
+ const blockedTask = db.prepare('SELECT id, status_id FROM t_tasks WHERE id = ?').get(params.blocked_task_id);
780
+ if (!blockerTask) {
781
+ throw new Error(`Blocker task #${params.blocker_task_id} not found`);
782
+ }
783
+ if (!blockedTask) {
784
+ throw new Error(`Blocked task #${params.blocked_task_id} not found`);
785
+ }
786
+ // Validation 3: Neither task is archived
787
+ if (blockerTask.status_id === TASK_STATUS.ARCHIVED) {
788
+ throw new Error(`Cannot add dependency: Task #${params.blocker_task_id} is archived`);
789
+ }
790
+ if (blockedTask.status_id === TASK_STATUS.ARCHIVED) {
791
+ throw new Error(`Cannot add dependency: Task #${params.blocked_task_id} is archived`);
792
+ }
793
+ // Validation 4: No direct circular (reverse relationship)
794
+ const reverseExists = db.prepare(`
795
+ SELECT 1 FROM t_task_dependencies
796
+ WHERE blocker_task_id = ? AND blocked_task_id = ?
797
+ `).get(params.blocked_task_id, params.blocker_task_id);
798
+ if (reverseExists) {
799
+ throw new Error(`Circular dependency detected: Task #${params.blocked_task_id} already blocks Task #${params.blocker_task_id}`);
800
+ }
801
+ // Validation 5: No transitive circular (check if adding this would create a cycle)
802
+ const cycleCheck = db.prepare(`
803
+ WITH RECURSIVE dependency_chain AS (
804
+ -- Start from the task that would be blocked
805
+ SELECT blocked_task_id as task_id, 1 as depth
806
+ FROM t_task_dependencies
807
+ WHERE blocker_task_id = ?
808
+
809
+ UNION ALL
810
+
811
+ -- Follow the chain of dependencies
812
+ SELECT d.blocked_task_id, dc.depth + 1
813
+ FROM t_task_dependencies d
814
+ JOIN dependency_chain dc ON d.blocker_task_id = dc.task_id
815
+ WHERE dc.depth < 100
816
+ )
817
+ SELECT task_id FROM dependency_chain WHERE task_id = ?
818
+ `).get(params.blocked_task_id, params.blocker_task_id);
819
+ if (cycleCheck) {
820
+ // Build cycle path for error message
821
+ const cyclePathResult = db.prepare(`
822
+ WITH RECURSIVE dependency_chain AS (
823
+ SELECT blocked_task_id as task_id, 1 as depth,
824
+ CAST(blocked_task_id AS TEXT) as path
825
+ FROM t_task_dependencies
826
+ WHERE blocker_task_id = ?
827
+
828
+ UNION ALL
829
+
830
+ SELECT d.blocked_task_id, dc.depth + 1,
831
+ dc.path || ' → ' || d.blocked_task_id
832
+ FROM t_task_dependencies d
833
+ JOIN dependency_chain dc ON d.blocker_task_id = dc.task_id
834
+ WHERE dc.depth < 100
835
+ )
836
+ SELECT path FROM dependency_chain WHERE task_id = ? ORDER BY depth DESC LIMIT 1
837
+ `).get(params.blocked_task_id, params.blocker_task_id);
838
+ const cyclePath = cyclePathResult?.path || `#${params.blocked_task_id} → ... → #${params.blocker_task_id}`;
839
+ throw new Error(`Circular dependency detected: Task #${params.blocker_task_id} → #${cyclePath} → #${params.blocker_task_id}`);
840
+ }
841
+ // All validations passed - insert dependency
842
+ const insertStmt = db.prepare(`
843
+ INSERT INTO t_task_dependencies (blocker_task_id, blocked_task_id)
844
+ VALUES (?, ?)
845
+ `);
846
+ insertStmt.run(params.blocker_task_id, params.blocked_task_id);
847
+ return {
848
+ success: true,
849
+ message: `Dependency added: Task #${params.blocker_task_id} blocks Task #${params.blocked_task_id}`
850
+ };
851
+ });
852
+ }
853
+ catch (error) {
854
+ const message = error instanceof Error ? error.message : String(error);
855
+ // Don't wrap error messages that are already descriptive
856
+ if (message.includes('not found') || message.includes('not allowed') || message.includes('Circular dependency') || message.includes('Cannot add dependency')) {
857
+ throw new Error(message);
858
+ }
859
+ throw new Error(`Failed to add dependency: ${message}`);
860
+ }
861
+ }
862
+ /**
863
+ * Remove dependency between tasks
864
+ */
865
+ export function removeDependency(params) {
866
+ const db = getDatabase();
867
+ if (!params.blocker_task_id) {
868
+ throw new Error('Parameter "blocker_task_id" is required');
869
+ }
870
+ if (!params.blocked_task_id) {
871
+ throw new Error('Parameter "blocked_task_id" is required');
872
+ }
873
+ try {
874
+ const deleteStmt = db.prepare(`
875
+ DELETE FROM t_task_dependencies
876
+ WHERE blocker_task_id = ? AND blocked_task_id = ?
877
+ `);
878
+ deleteStmt.run(params.blocker_task_id, params.blocked_task_id);
879
+ return {
880
+ success: true,
881
+ message: `Dependency removed: Task #${params.blocker_task_id} no longer blocks Task #${params.blocked_task_id}`
882
+ };
883
+ }
884
+ catch (error) {
885
+ const message = error instanceof Error ? error.message : String(error);
886
+ throw new Error(`Failed to remove dependency: ${message}`);
887
+ }
888
+ }
889
+ /**
890
+ * Get dependencies for a task (bidirectional: what blocks this task, what this task blocks)
891
+ */
892
+ export function getDependencies(params) {
893
+ const db = getDatabase();
894
+ if (!params.task_id) {
895
+ throw new Error('Parameter "task_id" is required');
896
+ }
897
+ const includeDetails = params.include_details || false;
898
+ try {
899
+ // Check if task exists
900
+ const taskExists = db.prepare('SELECT id FROM t_tasks WHERE id = ?').get(params.task_id);
901
+ if (!taskExists) {
902
+ throw new Error(`Task with id ${params.task_id} not found`);
903
+ }
904
+ // Use the shared helper function
905
+ const deps = queryTaskDependencies(db, params.task_id, includeDetails);
906
+ return {
907
+ task_id: params.task_id,
908
+ blockers: deps.blockers,
909
+ blocking: deps.blocking
910
+ };
911
+ }
912
+ catch (error) {
913
+ const message = error instanceof Error ? error.message : String(error);
914
+ // Don't wrap error messages that are already descriptive
915
+ if (message.includes('not found')) {
916
+ throw new Error(message);
917
+ }
918
+ throw new Error(`Failed to get dependencies: ${message}`);
919
+ }
920
+ }
673
921
  /**
674
922
  * Create multiple tasks atomically
675
923
  */
@@ -708,6 +956,7 @@ export function taskHelp() {
708
956
  tool: 'task',
709
957
  description: 'Kanban Task Watcher for managing tasks with AI-optimized lifecycle states',
710
958
  note: '💡 TIP: Use action: "example" to see comprehensive usage scenarios and real-world examples for all task actions.',
959
+ important: '🚨 AUTOMATIC FILE WATCHING: Linking files to tasks activates automatic file change monitoring and acceptance criteria validation. You can save 300 tokens per file compared to registering watchers manually. See auto_file_tracking section below.',
711
960
  actions: {
712
961
  create: {
713
962
  description: 'Create a new task',
@@ -776,6 +1025,7 @@ export function taskHelp() {
776
1025
  required_params: ['task_id', 'link_type', 'target_id'],
777
1026
  optional_params: ['link_relation'],
778
1027
  link_types: ['decision', 'constraint', 'file'],
1028
+ file_linking_behavior: '⚠️ IMPORTANT: When link_type="file", this action ACTIVATES AUTOMATIC FILE WATCHING. The file watcher monitors linked files for changes and validates acceptance criteria when files are saved. You can save 300 tokens per file compared to registering watchers manually.',
779
1029
  example: {
780
1030
  action: 'link',
781
1031
  task_id: 5,
@@ -808,6 +1058,69 @@ export function taskHelp() {
808
1058
  atomic: true
809
1059
  }
810
1060
  },
1061
+ add_dependency: {
1062
+ description: 'Add blocking relationship between tasks',
1063
+ required_params: ['blocker_task_id', 'blocked_task_id'],
1064
+ validations: [
1065
+ 'No self-dependencies',
1066
+ 'No circular dependencies (direct or transitive)',
1067
+ 'Both tasks must exist',
1068
+ 'Neither task can be archived'
1069
+ ],
1070
+ example: {
1071
+ action: 'add_dependency',
1072
+ blocker_task_id: 1,
1073
+ blocked_task_id: 2
1074
+ },
1075
+ note: 'Task #1 must be completed before Task #2 can start'
1076
+ },
1077
+ remove_dependency: {
1078
+ description: 'Remove blocking relationship between tasks',
1079
+ required_params: ['blocker_task_id', 'blocked_task_id'],
1080
+ example: {
1081
+ action: 'remove_dependency',
1082
+ blocker_task_id: 1,
1083
+ blocked_task_id: 2
1084
+ },
1085
+ note: 'Silently succeeds even if dependency does not exist'
1086
+ },
1087
+ get_dependencies: {
1088
+ description: 'Query task dependencies (bidirectional)',
1089
+ required_params: ['task_id'],
1090
+ optional_params: ['include_details'],
1091
+ returns: {
1092
+ blockers: 'Array of tasks that block this task',
1093
+ blocking: 'Array of tasks this task blocks'
1094
+ },
1095
+ example: {
1096
+ action: 'get_dependencies',
1097
+ task_id: 2,
1098
+ include_details: true
1099
+ },
1100
+ note: 'Defaults to metadata-only (token-efficient). Set include_details=true for full task details.'
1101
+ },
1102
+ watcher: {
1103
+ description: 'Query file watcher status and monitored files/tasks',
1104
+ required_params: [],
1105
+ optional_params: ['subaction'],
1106
+ subactions: ['status', 'list_files', 'list_tasks', 'help'],
1107
+ default_subaction: 'status',
1108
+ examples: {
1109
+ status: {
1110
+ action: 'watcher',
1111
+ subaction: 'status'
1112
+ },
1113
+ list_files: {
1114
+ action: 'watcher',
1115
+ subaction: 'list_files'
1116
+ },
1117
+ list_tasks: {
1118
+ action: 'watcher',
1119
+ subaction: 'list_tasks'
1120
+ }
1121
+ },
1122
+ note: 'Use to monitor which files/tasks are being watched. File watching activates automatically when you link files to tasks.'
1123
+ },
811
1124
  help: {
812
1125
  description: 'Return this help documentation',
813
1126
  example: { action: 'help' }
@@ -831,6 +1144,24 @@ export function taskHelp() {
831
1144
  3: 'high',
832
1145
  4: 'critical'
833
1146
  },
1147
+ auto_file_tracking: {
1148
+ description: 'Automatic file watching and acceptance criteria validation - save 300 tokens per file vs manual registration',
1149
+ recommendation: '⭐ BEST PRACTICE: Except in exceptional cases, it is recommended to set up file watchers for all tasks that involve code changes. This provides automatic status tracking with zero token overhead.',
1150
+ how_it_works: [
1151
+ '1. Link files to tasks using the link action with link_type="file"',
1152
+ '2. File watcher automatically activates and monitors linked files',
1153
+ '3. When files are saved, watcher detects changes',
1154
+ '4. If task has acceptance_criteria, watcher validates criteria against changes',
1155
+ '5. Results appear in terminal output with pass/fail status'
1156
+ ],
1157
+ requirements: [
1158
+ 'Task must have files linked via link action',
1159
+ 'File paths must be relative to project root (e.g., "src/api/auth.ts")',
1160
+ 'Watcher only monitors files explicitly linked to tasks'
1161
+ ],
1162
+ token_efficiency: 'File watching happens in background. No MCP tokens consumed until you query status. Manual file tracking would cost ~500-1000 tokens per file check.',
1163
+ documentation_reference: 'docs/AUTO_FILE_TRACKING.md - Complete guide with examples'
1164
+ },
834
1165
  documentation: {
835
1166
  task_overview: 'docs/TASK_OVERVIEW.md - Lifecycle, status transitions, auto-stale detection (363 lines, ~10k tokens)',
836
1167
  task_actions: 'docs/TASK_ACTIONS.md - All action references with examples (854 lines, ~21k tokens)',
@@ -842,4 +1173,316 @@ export function taskHelp() {
842
1173
  }
843
1174
  };
844
1175
  }
1176
+ /**
1177
+ * Query file watcher status and monitored files/tasks
1178
+ */
1179
+ export function watcherStatus(args) {
1180
+ const subaction = args.subaction || 'status';
1181
+ const watcher = FileWatcher.getInstance();
1182
+ if (subaction === 'help') {
1183
+ return {
1184
+ action: 'watcher',
1185
+ description: 'Query file watcher status and monitored files/tasks',
1186
+ subactions: {
1187
+ status: {
1188
+ description: 'Get overall watcher status (running, files watched, tasks monitored)',
1189
+ example: { action: 'watcher', subaction: 'status' }
1190
+ },
1191
+ list_files: {
1192
+ description: 'List all files being watched with their associated tasks',
1193
+ example: { action: 'watcher', subaction: 'list_files' }
1194
+ },
1195
+ list_tasks: {
1196
+ description: 'List all tasks that have active file watchers',
1197
+ example: { action: 'watcher', subaction: 'list_tasks' }
1198
+ },
1199
+ help: {
1200
+ description: 'Show this help documentation',
1201
+ example: { action: 'watcher', subaction: 'help' }
1202
+ }
1203
+ },
1204
+ note: 'File watching activates automatically when you link files to tasks using the link action with link_type="file". The watcher monitors linked files for changes and validates acceptance criteria.'
1205
+ };
1206
+ }
1207
+ if (subaction === 'status') {
1208
+ const status = watcher.getStatus();
1209
+ return {
1210
+ success: true,
1211
+ watcher_status: {
1212
+ running: status.running,
1213
+ files_watched: status.filesWatched,
1214
+ tasks_monitored: status.tasksWatched
1215
+ },
1216
+ message: status.running
1217
+ ? `File watcher is running. Monitoring ${status.filesWatched} file(s) across ${status.tasksWatched} task(s).`
1218
+ : 'File watcher is not running. Link files to tasks to activate automatic file watching.'
1219
+ };
1220
+ }
1221
+ if (subaction === 'list_files') {
1222
+ const db = getDatabase();
1223
+ const fileLinks = db.prepare(`
1224
+ SELECT DISTINCT tfl.file_path, t.id, t.title, ts.status_name
1225
+ FROM t_task_file_links tfl
1226
+ JOIN t_tasks t ON tfl.task_id = t.id
1227
+ JOIN m_task_statuses ts ON t.status_id = ts.id
1228
+ WHERE t.status_id != 6 -- Exclude archived tasks
1229
+ ORDER BY tfl.file_path, t.id
1230
+ `).all();
1231
+ // Group by file
1232
+ const fileMap = new Map();
1233
+ for (const link of fileLinks) {
1234
+ if (!fileMap.has(link.file_path)) {
1235
+ fileMap.set(link.file_path, []);
1236
+ }
1237
+ fileMap.get(link.file_path).push({
1238
+ task_id: link.id,
1239
+ task_title: link.title,
1240
+ status: link.status_name
1241
+ });
1242
+ }
1243
+ const files = Array.from(fileMap.entries()).map(([path, tasks]) => ({
1244
+ file_path: path,
1245
+ tasks: tasks
1246
+ }));
1247
+ return {
1248
+ success: true,
1249
+ files_watched: files.length,
1250
+ files: files,
1251
+ message: files.length > 0
1252
+ ? `Watching ${files.length} file(s) linked to tasks.`
1253
+ : 'No files currently linked to tasks. Use link action with link_type="file" to activate file watching.'
1254
+ };
1255
+ }
1256
+ if (subaction === 'list_tasks') {
1257
+ const db = getDatabase();
1258
+ const taskLinks = db.prepare(`
1259
+ SELECT t.id, t.title, ts.status_name, COUNT(DISTINCT tfl.file_path) as file_count,
1260
+ GROUP_CONCAT(DISTINCT tfl.file_path, ', ') as files
1261
+ FROM t_tasks t
1262
+ JOIN m_task_statuses ts ON t.status_id = ts.id
1263
+ JOIN t_task_file_links tfl ON t.id = tfl.task_id
1264
+ WHERE t.status_id != 6 -- Exclude archived tasks
1265
+ GROUP BY t.id, t.title, ts.status_name
1266
+ ORDER BY t.id
1267
+ `).all();
1268
+ const tasks = taskLinks.map(task => ({
1269
+ task_id: task.id,
1270
+ task_title: task.title,
1271
+ status: task.status_name,
1272
+ files_count: task.file_count,
1273
+ files: task.files.split(', ')
1274
+ }));
1275
+ return {
1276
+ success: true,
1277
+ tasks_monitored: tasks.length,
1278
+ tasks: tasks,
1279
+ message: tasks.length > 0
1280
+ ? `Monitoring ${tasks.length} task(s) with linked files.`
1281
+ : 'No tasks currently have linked files. Use link action with link_type="file" to activate file watching.'
1282
+ };
1283
+ }
1284
+ return {
1285
+ error: `Invalid subaction: ${subaction}. Valid subactions: status, list_files, list_tasks, help`
1286
+ };
1287
+ }
1288
+ /**
1289
+ * Get comprehensive examples for task tool
1290
+ * @returns Examples documentation object
1291
+ */
1292
+ export function taskExample() {
1293
+ return {
1294
+ tool: 'task',
1295
+ description: 'Comprehensive task management examples for Kanban-style workflow',
1296
+ scenarios: {
1297
+ basic_task_management: {
1298
+ title: 'Creating and Managing Tasks',
1299
+ examples: [
1300
+ {
1301
+ scenario: 'Create a new task',
1302
+ request: '{ action: "create", title: "Implement user authentication", description: "Add JWT-based auth to API", priority: 3, assigned_agent: "backend-agent", layer: "business", tags: ["authentication", "security"] }',
1303
+ explanation: 'Creates task in todo status with high priority'
1304
+ },
1305
+ {
1306
+ scenario: 'Get task details',
1307
+ request: '{ action: "get", task_id: 5 }',
1308
+ response: 'Full task details including metadata, links, and timestamps'
1309
+ },
1310
+ {
1311
+ scenario: 'List tasks by status',
1312
+ request: '{ action: "list", status: "in_progress", limit: 20 }',
1313
+ explanation: 'View all in-progress tasks'
1314
+ }
1315
+ ]
1316
+ },
1317
+ status_workflow: {
1318
+ title: 'Task Lifecycle (Status Transitions)',
1319
+ workflow: [
1320
+ {
1321
+ step: 1,
1322
+ status: 'todo',
1323
+ action: '{ action: "create", title: "...", status: "todo" }',
1324
+ description: 'Task created and waiting to be started'
1325
+ },
1326
+ {
1327
+ step: 2,
1328
+ status: 'in_progress',
1329
+ action: '{ action: "move", task_id: 1, new_status: "in_progress" }',
1330
+ description: 'Agent starts working on task'
1331
+ },
1332
+ {
1333
+ step: 3,
1334
+ status: 'waiting_review',
1335
+ action: '{ action: "move", task_id: 1, new_status: "waiting_review" }',
1336
+ description: 'Work complete, awaiting review/approval'
1337
+ },
1338
+ {
1339
+ step: 4,
1340
+ status: 'done',
1341
+ action: '{ action: "move", task_id: 1, new_status: "done" }',
1342
+ description: 'Task reviewed and completed'
1343
+ },
1344
+ {
1345
+ step: 5,
1346
+ status: 'archived',
1347
+ action: '{ action: "archive", task_id: 1 }',
1348
+ description: 'Task archived for historical record'
1349
+ }
1350
+ ],
1351
+ blocked_status: {
1352
+ description: 'Use "blocked" when task cannot proceed due to dependencies',
1353
+ example: '{ action: "move", task_id: 1, new_status: "blocked" }'
1354
+ }
1355
+ },
1356
+ auto_stale_detection: {
1357
+ title: 'Automatic Stale Task Management',
1358
+ behavior: [
1359
+ {
1360
+ rule: 'in_progress > 2 hours → waiting_review',
1361
+ explanation: 'Tasks stuck in progress auto-move to waiting_review',
1362
+ rationale: 'Prevents tasks from being forgotten while in progress'
1363
+ },
1364
+ {
1365
+ rule: 'waiting_review > 24 hours → todo',
1366
+ explanation: 'Unreviewed tasks return to todo queue',
1367
+ rationale: 'Ensures waiting tasks dont accumulate indefinitely'
1368
+ }
1369
+ ],
1370
+ configuration: {
1371
+ keys: ['task_stale_hours_in_progress', 'task_stale_hours_waiting_review', 'task_auto_stale_enabled'],
1372
+ note: 'Configure via config table in database'
1373
+ }
1374
+ },
1375
+ task_linking: {
1376
+ title: 'Linking Tasks to Context',
1377
+ examples: [
1378
+ {
1379
+ scenario: 'Link task to decision',
1380
+ request: '{ action: "link", task_id: 5, link_type: "decision", target_id: "api_auth_method", link_relation: "implements" }',
1381
+ explanation: 'Track which tasks implement specific decisions'
1382
+ },
1383
+ {
1384
+ scenario: 'Link task to constraint',
1385
+ request: '{ action: "link", task_id: 5, link_type: "constraint", target_id: 3, link_relation: "addresses" }',
1386
+ explanation: 'Show task addresses a performance/architecture/security constraint'
1387
+ },
1388
+ {
1389
+ scenario: 'Link task to file',
1390
+ request: '{ action: "link", task_id: 5, link_type: "file", target_id: "src/api/auth.ts", link_relation: "modifies" }',
1391
+ explanation: 'Activates automatic file watching for the task - saves 300 tokens per file vs manual registration',
1392
+ behavior: 'File watcher monitors linked files and validates acceptance criteria when files change'
1393
+ }
1394
+ ]
1395
+ },
1396
+ batch_operations: {
1397
+ title: 'Batch Task Creation',
1398
+ examples: [
1399
+ {
1400
+ scenario: 'Create multiple related tasks',
1401
+ request: '{ action: "batch_create", tasks: [{"title": "Design API", "priority": 3}, {"title": "Implement API", "priority": 3}, {"title": "Write tests", "priority": 2}], atomic: false }',
1402
+ explanation: 'Create task breakdown - use atomic:false for best-effort'
1403
+ }
1404
+ ]
1405
+ },
1406
+ filtering_queries: {
1407
+ title: 'Advanced Task Queries',
1408
+ examples: [
1409
+ {
1410
+ scenario: 'Find high-priority tasks for agent',
1411
+ request: '{ action: "list", assigned_agent: "backend-agent", priority: 3, status: "todo" }',
1412
+ note: 'Priority is numeric: 1=low, 2=medium, 3=high, 4=critical'
1413
+ },
1414
+ {
1415
+ scenario: 'Get all security-related tasks',
1416
+ request: '{ action: "list", tags: ["security"], limit: 50 }',
1417
+ explanation: 'Filter by tags for topic-based views'
1418
+ },
1419
+ {
1420
+ scenario: 'View infrastructure layer tasks',
1421
+ request: '{ action: "list", layer: "infrastructure" }',
1422
+ explanation: 'See all DevOps/config related tasks'
1423
+ }
1424
+ ]
1425
+ },
1426
+ file_watcher_status: {
1427
+ title: 'File Watcher Status Queries',
1428
+ examples: [
1429
+ {
1430
+ scenario: 'Check if file watcher is running',
1431
+ request: '{ action: "watcher", subaction: "status" }',
1432
+ explanation: 'Returns running status, files watched count, tasks monitored count',
1433
+ response: '{ running: true, files_watched: 5, tasks_monitored: 3 }'
1434
+ },
1435
+ {
1436
+ scenario: 'List all files being watched',
1437
+ request: '{ action: "watcher", subaction: "list_files" }',
1438
+ explanation: 'Shows file paths and which tasks are watching them',
1439
+ response: '{ files: [{ file_path: "src/api/auth.ts", tasks: [{ task_id: 5, title: "...", status: "in_progress" }] }] }'
1440
+ },
1441
+ {
1442
+ scenario: 'List tasks with active file watchers',
1443
+ request: '{ action: "watcher", subaction: "list_tasks" }',
1444
+ explanation: 'Shows tasks and which files they are watching',
1445
+ response: '{ tasks: [{ task_id: 5, title: "...", files: ["src/api/auth.ts", "src/api/middleware.ts"] }] }'
1446
+ }
1447
+ ]
1448
+ }
1449
+ },
1450
+ valid_transitions: {
1451
+ from_todo: ['in_progress', 'blocked', 'done', 'archived'],
1452
+ from_in_progress: ['waiting_review', 'blocked', 'todo'],
1453
+ from_waiting_review: ['done', 'in_progress', 'todo'],
1454
+ from_blocked: ['todo', 'in_progress'],
1455
+ from_done: ['archived', 'todo'],
1456
+ from_archived: []
1457
+ },
1458
+ best_practices: {
1459
+ task_creation: [
1460
+ 'Use descriptive titles (200 char max)',
1461
+ 'Set appropriate priority: 1=low, 2=medium (default), 3=high, 4=critical',
1462
+ 'Assign to layer where work will be done',
1463
+ 'Tag comprehensively for easy filtering',
1464
+ 'Include acceptance_criteria for complex tasks'
1465
+ ],
1466
+ status_management: [
1467
+ 'Move to in_progress when starting work',
1468
+ 'Use waiting_review for completed but unverified work',
1469
+ 'Set to blocked with notes explaining dependency',
1470
+ 'Archive done tasks periodically for cleaner views'
1471
+ ],
1472
+ linking: [
1473
+ '⭐ RECOMMENDED: Set up file watchers for all tasks involving code changes (except exceptional cases)',
1474
+ 'Link tasks to decisions they implement',
1475
+ 'Link to constraints they address',
1476
+ 'Link files to activate automatic file watching (save 300 tokens per file vs manual registration)',
1477
+ 'Use descriptive link_relation values'
1478
+ ],
1479
+ coordination: [
1480
+ 'Use assigned_agent for clear ownership',
1481
+ 'Filter by status for Kanban board views',
1482
+ 'Monitor auto-stale transitions for stuck work',
1483
+ 'Use tags for cross-cutting concerns (security, performance, etc.)'
1484
+ ]
1485
+ }
1486
+ };
1487
+ }
845
1488
  //# sourceMappingURL=tasks.js.map