sqlew 3.2.5 → 3.6.0

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 (291) hide show
  1. package/CHANGELOG.md +288 -1011
  2. package/README.md +80 -263
  3. package/assets/config.example.toml +97 -0
  4. package/assets/schema.sql +6 -1
  5. package/dist/adapters/index.d.ts +11 -0
  6. package/dist/adapters/index.d.ts.map +1 -0
  7. package/dist/adapters/index.js +21 -0
  8. package/dist/adapters/index.js.map +1 -0
  9. package/dist/adapters/mysql-adapter.d.ts +31 -0
  10. package/dist/adapters/mysql-adapter.d.ts.map +1 -0
  11. package/dist/adapters/mysql-adapter.js +63 -0
  12. package/dist/adapters/mysql-adapter.js.map +1 -0
  13. package/dist/adapters/postgresql-adapter.d.ts +31 -0
  14. package/dist/adapters/postgresql-adapter.d.ts.map +1 -0
  15. package/dist/adapters/postgresql-adapter.js +63 -0
  16. package/dist/adapters/postgresql-adapter.js.map +1 -0
  17. package/dist/adapters/sqlite-adapter.d.ts +37 -0
  18. package/dist/adapters/sqlite-adapter.d.ts.map +1 -0
  19. package/dist/adapters/sqlite-adapter.js +129 -0
  20. package/dist/adapters/sqlite-adapter.js.map +1 -0
  21. package/dist/adapters/types.d.ts +33 -0
  22. package/dist/adapters/types.d.ts.map +1 -0
  23. package/dist/adapters/types.js +2 -0
  24. package/dist/adapters/types.js.map +1 -0
  25. package/dist/cli.js +55 -54
  26. package/dist/cli.js.map +1 -1
  27. package/dist/config/example-generator.d.ts +11 -0
  28. package/dist/config/example-generator.d.ts.map +1 -0
  29. package/dist/config/example-generator.js +48 -0
  30. package/dist/config/example-generator.js.map +1 -0
  31. package/dist/config/loader.d.ts +46 -0
  32. package/dist/config/loader.d.ts.map +1 -0
  33. package/dist/config/loader.js +155 -0
  34. package/dist/config/loader.js.map +1 -0
  35. package/dist/config/types.d.ts +86 -0
  36. package/dist/config/types.d.ts.map +1 -0
  37. package/dist/config/types.js +28 -0
  38. package/dist/config/types.js.map +1 -0
  39. package/dist/constants.d.ts +9 -0
  40. package/dist/constants.d.ts.map +1 -1
  41. package/dist/constants.js +10 -0
  42. package/dist/constants.js.map +1 -1
  43. package/dist/database.d.ts +44 -122
  44. package/dist/database.d.ts.map +1 -1
  45. package/dist/database.js +145 -349
  46. package/dist/database.js.map +1 -1
  47. package/dist/index.js +223 -175
  48. package/dist/index.js.map +1 -1
  49. package/dist/knexfile.d.ts +6 -0
  50. package/dist/knexfile.d.ts.map +1 -0
  51. package/dist/knexfile.js +85 -0
  52. package/dist/knexfile.js.map +1 -0
  53. package/dist/migrations/add-help-system-tables.d.ts +35 -0
  54. package/dist/migrations/add-help-system-tables.d.ts.map +1 -0
  55. package/dist/migrations/add-help-system-tables.js +206 -0
  56. package/dist/migrations/add-help-system-tables.js.map +1 -0
  57. package/dist/migrations/add-token-tracking.d.ts +28 -0
  58. package/dist/migrations/add-token-tracking.d.ts.map +1 -0
  59. package/dist/migrations/add-token-tracking.js +108 -0
  60. package/dist/migrations/add-token-tracking.js.map +1 -0
  61. package/dist/migrations/add-v3.5.0-pruned-files.d.ts +26 -0
  62. package/dist/migrations/add-v3.5.0-pruned-files.d.ts.map +1 -0
  63. package/dist/migrations/add-v3.5.0-pruned-files.js +107 -0
  64. package/dist/migrations/add-v3.5.0-pruned-files.js.map +1 -0
  65. package/dist/migrations/index.d.ts +26 -12
  66. package/dist/migrations/index.d.ts.map +1 -1
  67. package/dist/migrations/index.js +162 -20
  68. package/dist/migrations/index.js.map +1 -1
  69. package/dist/migrations/knex/20251025020452_create_master_tables.d.ts +4 -0
  70. package/dist/migrations/knex/20251025020452_create_master_tables.d.ts.map +1 -0
  71. package/dist/migrations/knex/20251025020452_create_master_tables.js +65 -0
  72. package/dist/migrations/knex/20251025020452_create_master_tables.js.map +1 -0
  73. package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts +4 -0
  74. package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts.map +1 -0
  75. package/dist/migrations/knex/20251025021152_create_transaction_tables.js +235 -0
  76. package/dist/migrations/knex/20251025021152_create_transaction_tables.js.map +1 -0
  77. package/dist/migrations/knex/20251025021351_create_indexes.d.ts +4 -0
  78. package/dist/migrations/knex/20251025021351_create_indexes.d.ts.map +1 -0
  79. package/dist/migrations/knex/20251025021351_create_indexes.js +62 -0
  80. package/dist/migrations/knex/20251025021351_create_indexes.js.map +1 -0
  81. package/dist/migrations/knex/20251025021416_seed_master_data.d.ts +4 -0
  82. package/dist/migrations/knex/20251025021416_seed_master_data.d.ts.map +1 -0
  83. package/dist/migrations/knex/20251025021416_seed_master_data.js +58 -0
  84. package/dist/migrations/knex/20251025021416_seed_master_data.js.map +1 -0
  85. package/dist/migrations/knex/20251025070349_create_views.d.ts +4 -0
  86. package/dist/migrations/knex/20251025070349_create_views.d.ts.map +1 -0
  87. package/dist/migrations/knex/20251025070349_create_views.js +143 -0
  88. package/dist/migrations/knex/20251025070349_create_views.js.map +1 -0
  89. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts +4 -0
  90. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts.map +1 -0
  91. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js +15 -0
  92. package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js.map +1 -0
  93. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts +8 -0
  94. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts.map +1 -0
  95. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js +12 -0
  96. package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js.map +1 -0
  97. package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts +19 -0
  98. package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts.map +1 -0
  99. package/dist/migrations/knex/20251025090000_create_help_system_tables.js +115 -0
  100. package/dist/migrations/knex/20251025090000_create_help_system_tables.js.map +1 -0
  101. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts +13 -0
  102. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts.map +1 -0
  103. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js +377 -0
  104. package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js.map +1 -0
  105. package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts +15 -0
  106. package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts.map +1 -0
  107. package/dist/migrations/knex/20251025100000_seed_help_metadata.js +253 -0
  108. package/dist/migrations/knex/20251025100000_seed_help_metadata.js.map +1 -0
  109. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts +16 -0
  110. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts.map +1 -0
  111. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js +276 -0
  112. package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js.map +1 -0
  113. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts +8 -0
  114. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts.map +1 -0
  115. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js +64 -0
  116. package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js.map +1 -0
  117. package/dist/migrations/seed-help-data.d.ts +48 -0
  118. package/dist/migrations/seed-help-data.d.ts.map +1 -0
  119. package/dist/migrations/seed-help-data.js +1466 -0
  120. package/dist/migrations/seed-help-data.js.map +1 -0
  121. package/dist/migrations/seed-tool-metadata.d.ts +24 -0
  122. package/dist/migrations/seed-tool-metadata.d.ts.map +1 -0
  123. package/dist/migrations/seed-tool-metadata.js +392 -0
  124. package/dist/migrations/seed-tool-metadata.js.map +1 -0
  125. package/dist/migrations/v3.6.0-help-system-refactor.d.ts +46 -0
  126. package/dist/migrations/v3.6.0-help-system-refactor.d.ts.map +1 -0
  127. package/dist/migrations/v3.6.0-help-system-refactor.js +223 -0
  128. package/dist/migrations/v3.6.0-help-system-refactor.js.map +1 -0
  129. package/dist/schema.d.ts.map +1 -1
  130. package/dist/schema.js +2 -0
  131. package/dist/schema.js.map +1 -1
  132. package/dist/tests/git-aware-completion.test.d.ts +6 -0
  133. package/dist/tests/git-aware-completion.test.d.ts.map +1 -0
  134. package/dist/tests/git-aware-completion.test.js +160 -0
  135. package/dist/tests/git-aware-completion.test.js.map +1 -0
  136. package/dist/tests/help-system.test.d.ts +23 -0
  137. package/dist/tests/help-system.test.d.ts.map +1 -0
  138. package/dist/tests/help-system.test.js +374 -0
  139. package/dist/tests/help-system.test.js.map +1 -0
  140. package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts +6 -0
  141. package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts.map +1 -0
  142. package/dist/tests/tasks.auto-pruning-decision-link.test.js +264 -0
  143. package/dist/tests/tasks.auto-pruning-decision-link.test.js.map +1 -0
  144. package/dist/tests/tasks.auto-pruning-partial.test.d.ts +6 -0
  145. package/dist/tests/tasks.auto-pruning-partial.test.d.ts.map +1 -0
  146. package/dist/tests/tasks.auto-pruning-partial.test.js +285 -0
  147. package/dist/tests/tasks.auto-pruning-partial.test.js.map +1 -0
  148. package/dist/tests/tasks.auto-pruning-persistence.test.d.ts +6 -0
  149. package/dist/tests/tasks.auto-pruning-persistence.test.d.ts.map +1 -0
  150. package/dist/tests/tasks.auto-pruning-persistence.test.js +250 -0
  151. package/dist/tests/tasks.auto-pruning-persistence.test.js.map +1 -0
  152. package/dist/tests/tasks.auto-pruning-safety.test.d.ts +12 -0
  153. package/dist/tests/tasks.auto-pruning-safety.test.d.ts.map +1 -0
  154. package/dist/tests/tasks.auto-pruning-safety.test.js +217 -0
  155. package/dist/tests/tasks.auto-pruning-safety.test.js.map +1 -0
  156. package/dist/tests/tasks.dependencies.test.js +338 -307
  157. package/dist/tests/tasks.dependencies.test.js.map +1 -1
  158. package/dist/tests/tasks.link-file-backward-compat.test.d.ts +6 -0
  159. package/dist/tests/tasks.link-file-backward-compat.test.d.ts.map +1 -0
  160. package/dist/tests/tasks.link-file-backward-compat.test.js +247 -0
  161. package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -0
  162. package/dist/tests/tasks.watch-files-action.test.d.ts +6 -0
  163. package/dist/tests/tasks.watch-files-action.test.d.ts.map +1 -0
  164. package/dist/tests/tasks.watch-files-action.test.js +372 -0
  165. package/dist/tests/tasks.watch-files-action.test.js.map +1 -0
  166. package/dist/tests/tasks.watch-files-parameter.test.d.ts +6 -0
  167. package/dist/tests/tasks.watch-files-parameter.test.d.ts.map +1 -0
  168. package/dist/tests/tasks.watch-files-parameter.test.js +260 -0
  169. package/dist/tests/tasks.watch-files-parameter.test.js.map +1 -0
  170. package/dist/tests/two-step-git-completion.test.d.ts +6 -0
  171. package/dist/tests/two-step-git-completion.test.d.ts.map +1 -0
  172. package/dist/tests/two-step-git-completion.test.js +326 -0
  173. package/dist/tests/two-step-git-completion.test.js.map +1 -0
  174. package/dist/tests/vcs-staging.test.d.ts +6 -0
  175. package/dist/tests/vcs-staging.test.d.ts.map +1 -0
  176. package/dist/tests/vcs-staging.test.js +137 -0
  177. package/dist/tests/vcs-staging.test.js.map +1 -0
  178. package/dist/tools/config.d.ts +9 -4
  179. package/dist/tools/config.d.ts.map +1 -1
  180. package/dist/tools/config.js +16 -12
  181. package/dist/tools/config.js.map +1 -1
  182. package/dist/tools/constraints.d.ts +9 -3
  183. package/dist/tools/constraints.d.ts.map +1 -1
  184. package/dist/tools/constraints.js +66 -45
  185. package/dist/tools/constraints.js.map +1 -1
  186. package/dist/tools/context.d.ts +35 -16
  187. package/dist/tools/context.d.ts.map +1 -1
  188. package/dist/tools/context.js +374 -314
  189. package/dist/tools/context.js.map +1 -1
  190. package/dist/tools/files.d.ts +11 -4
  191. package/dist/tools/files.d.ts.map +1 -1
  192. package/dist/tools/files.js +173 -91
  193. package/dist/tools/files.js.map +1 -1
  194. package/dist/tools/help-queries.d.ts +130 -0
  195. package/dist/tools/help-queries.d.ts.map +1 -0
  196. package/dist/tools/help-queries.js +393 -0
  197. package/dist/tools/help-queries.js.map +1 -0
  198. package/dist/tools/messaging.d.ts +13 -6
  199. package/dist/tools/messaging.d.ts.map +1 -1
  200. package/dist/tools/messaging.js +217 -129
  201. package/dist/tools/messaging.js.map +1 -1
  202. package/dist/tools/tasks.d.ts +42 -12
  203. package/dist/tools/tasks.d.ts.map +1 -1
  204. package/dist/tools/tasks.js +809 -347
  205. package/dist/tools/tasks.js.map +1 -1
  206. package/dist/tools/utils.d.ts +13 -5
  207. package/dist/tools/utils.d.ts.map +1 -1
  208. package/dist/tools/utils.js +92 -115
  209. package/dist/tools/utils.js.map +1 -1
  210. package/dist/types.d.ts +4 -0
  211. package/dist/types.d.ts.map +1 -1
  212. package/dist/utils/activity-logging.d.ts +114 -0
  213. package/dist/utils/activity-logging.d.ts.map +1 -0
  214. package/dist/utils/activity-logging.js +162 -0
  215. package/dist/utils/activity-logging.js.map +1 -0
  216. package/dist/utils/batch.d.ts +2 -2
  217. package/dist/utils/batch.d.ts.map +1 -1
  218. package/dist/utils/batch.js +8 -8
  219. package/dist/utils/batch.js.map +1 -1
  220. package/dist/utils/cleanup.d.ts +21 -13
  221. package/dist/utils/cleanup.d.ts.map +1 -1
  222. package/dist/utils/cleanup.js +31 -24
  223. package/dist/utils/cleanup.js.map +1 -1
  224. package/dist/utils/debug-logger.d.ts +44 -0
  225. package/dist/utils/debug-logger.d.ts.map +1 -0
  226. package/dist/utils/debug-logger.js +116 -0
  227. package/dist/utils/debug-logger.js.map +1 -0
  228. package/dist/utils/file-pruning.d.ts +69 -0
  229. package/dist/utils/file-pruning.d.ts.map +1 -0
  230. package/dist/utils/file-pruning.js +185 -0
  231. package/dist/utils/file-pruning.js.map +1 -0
  232. package/dist/utils/help-tracking.d.ts +55 -0
  233. package/dist/utils/help-tracking.d.ts.map +1 -0
  234. package/dist/utils/help-tracking.js +88 -0
  235. package/dist/utils/help-tracking.js.map +1 -0
  236. package/dist/utils/quality-checks.d.ts +60 -0
  237. package/dist/utils/quality-checks.d.ts.map +1 -0
  238. package/dist/utils/quality-checks.js +228 -0
  239. package/dist/utils/quality-checks.js.map +1 -0
  240. package/dist/utils/retention.d.ts +13 -5
  241. package/dist/utils/retention.d.ts.map +1 -1
  242. package/dist/utils/retention.js +20 -8
  243. package/dist/utils/retention.js.map +1 -1
  244. package/dist/utils/task-stale-detection.d.ts +77 -7
  245. package/dist/utils/task-stale-detection.d.ts.map +1 -1
  246. package/dist/utils/task-stale-detection.js +309 -34
  247. package/dist/utils/task-stale-detection.js.map +1 -1
  248. package/dist/utils/token-estimation.d.ts +72 -0
  249. package/dist/utils/token-estimation.d.ts.map +1 -0
  250. package/dist/utils/token-estimation.js +71 -0
  251. package/dist/utils/token-estimation.js.map +1 -0
  252. package/dist/utils/token-logging.d.ts +48 -0
  253. package/dist/utils/token-logging.d.ts.map +1 -0
  254. package/dist/utils/token-logging.js +112 -0
  255. package/dist/utils/token-logging.js.map +1 -0
  256. package/dist/utils/vcs-adapter.d.ts +68 -0
  257. package/dist/utils/vcs-adapter.d.ts.map +1 -0
  258. package/dist/utils/vcs-adapter.js +187 -0
  259. package/dist/utils/vcs-adapter.js.map +1 -0
  260. package/dist/utils/view-queries.d.ts +34 -0
  261. package/dist/utils/view-queries.d.ts.map +1 -0
  262. package/dist/utils/view-queries.js +192 -0
  263. package/dist/utils/view-queries.js.map +1 -0
  264. package/dist/watcher/file-watcher.d.ts +54 -4
  265. package/dist/watcher/file-watcher.d.ts.map +1 -1
  266. package/dist/watcher/file-watcher.js +329 -33
  267. package/dist/watcher/file-watcher.js.map +1 -1
  268. package/dist/watcher/gitignore-parser.d.ts +70 -0
  269. package/dist/watcher/gitignore-parser.d.ts.map +1 -0
  270. package/dist/watcher/gitignore-parser.js +191 -0
  271. package/dist/watcher/gitignore-parser.js.map +1 -0
  272. package/dist/watcher/index.d.ts +1 -0
  273. package/dist/watcher/index.d.ts.map +1 -1
  274. package/dist/watcher/index.js +1 -0
  275. package/dist/watcher/index.js.map +1 -1
  276. package/docs/AI_AGENT_GUIDE.md +1 -1
  277. package/docs/ARCHITECTURE.md +12 -0
  278. package/docs/AUTO_FILE_TRACKING.md +486 -82
  279. package/docs/BEST_PRACTICES.md +56 -448
  280. package/docs/CONFIGURATION.md +908 -0
  281. package/docs/GIT_AWARE_AUTO_COMPLETE.md +645 -0
  282. package/docs/MIGRATION_v3.3.md +602 -0
  283. package/docs/MIGRATION_v3.6.0.md +170 -0
  284. package/docs/SHARED_CONCEPTS.md +65 -209
  285. package/docs/TASK_ACTIONS.md +12 -0
  286. package/docs/TASK_OVERVIEW.md +125 -24
  287. package/docs/TASK_PRUNING.md +589 -0
  288. package/docs/TASK_SYSTEM.md +83 -13
  289. package/docs/TOOL_REFERENCE.md +94 -6
  290. package/docs/TOOL_SELECTION.md +41 -248
  291. package/package.json +21 -7
@@ -2,11 +2,13 @@
2
2
  * File Watcher - Auto-tracking file changes linked to tasks
3
3
  * Monitors files and auto-transitions task status on file modification
4
4
  *
5
- * Features:
6
- * - chokidar-based file watching with debouncing
5
+ * Features (v3.5.1):
6
+ * - chokidar v4 file watching with automatic WSL support
7
+ * - Project root watching with .gitignore support
7
8
  * - Dynamic file registration (add/remove files at runtime)
8
9
  * - Auto-transition: todo → in_progress on file change
9
10
  * - Maps file paths → task IDs for efficient lookup
11
+ * - Respects .gitignore patterns and built-in ignore rules
10
12
  */
11
13
  /**
12
14
  * FileWatcher class - Singleton pattern
@@ -18,13 +20,22 @@ export declare class FileWatcher {
18
20
  private isRunning;
19
21
  private debounceTimers;
20
22
  private readonly DEBOUNCE_MS;
23
+ private gitignoreParser;
24
+ private projectRoot;
25
+ private lastModifiedTimes;
26
+ private filesModifiedSet;
27
+ private vcsDetectionInterval;
21
28
  private constructor();
22
29
  /**
23
30
  * Get singleton instance
24
31
  */
25
32
  static getInstance(): FileWatcher;
26
33
  /**
27
- * Initialize and start the file watcher
34
+ * Detect if running on WSL (Windows Subsystem for Linux)
35
+ */
36
+ private isWSL;
37
+ /**
38
+ * Initialize and start the file watcher (v3.5.1 - Chokidar v4)
28
39
  */
29
40
  start(): Promise<void>;
30
41
  /**
@@ -43,10 +54,38 @@ export declare class FileWatcher {
43
54
  * Unregister all files for a specific task
44
55
  */
45
56
  unregisterTask(taskId: number): void;
57
+ /**
58
+ * Check if a path is a VCS index file
59
+ */
60
+ private isVCSIndexFile;
61
+ /**
62
+ * Get VCS index file path for given VCS type (v3.5.3)
63
+ * Centralized mapping for easier extension to Mercurial/SVN
64
+ * @param vcsType - VCS type string (Git, Mercurial, SVN)
65
+ * @returns Absolute path to VCS index file, or null if VCS has no local index
66
+ */
67
+ private getVCSIndexPath;
68
+ /**
69
+ * Re-detect VCS and start watching index files (v3.5.3)
70
+ * Called when VCS might be initialized after watcher starts
71
+ * Public method for external triggering (e.g., after git init)
72
+ */
73
+ refreshVCSWatching(): Promise<void>;
74
+ /**
75
+ * Watch VCS index files for commit detection
76
+ */
77
+ private watchVCSIndexFiles;
46
78
  /**
47
79
  * Handle file change event
48
80
  */
49
81
  private handleFileChange;
82
+ /**
83
+ * Handle VCS index file change - triggers two-step Git-aware workflow (v3.5.2)
84
+ * Step 1: Staging (git add) → waiting_review → done
85
+ * Step 2: Commit (git commit) → done → archived
86
+ * Fallback: If files already committed (not in staging), use v3.4.0 logic
87
+ */
88
+ private handleVCSIndexChange;
50
89
  /**
51
90
  * Check acceptance criteria and auto-complete task if all pass
52
91
  */
@@ -56,13 +95,24 @@ export declare class FileWatcher {
56
95
  */
57
96
  private loadTaskFileLinks;
58
97
  /**
59
- * Normalize file path (resolve relative paths, remove trailing slashes)
98
+ * Normalize file path (convert to relative path from project root, remove trailing slashes)
60
99
  */
61
100
  private normalizePath;
62
101
  /**
63
102
  * Get total count of tasks being watched
64
103
  */
65
104
  private getTotalTaskCount;
105
+ /**
106
+ * Check if task is ready for review and transition if conditions met
107
+ * Quality gates:
108
+ * - All watched files modified at least once
109
+ * - TypeScript compiles without errors (if .ts files)
110
+ * - Tests pass (if test files exist)
111
+ * - Idle for configured time (default 15 minutes)
112
+ *
113
+ * @param taskId - Task ID to check
114
+ */
115
+ private checkAndTransitionToReview;
66
116
  /**
67
117
  * Get current watcher status
68
118
  */
@@ -1 +1 @@
1
- {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkBH;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA4B;IACnD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAA6C;IACjE,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,OAAO;IAIP;;OAEG;WACW,WAAW,IAAI,WAAW;IAOxC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2CnC;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBlC;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAwCrG;;OAEG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAuB7D;;OAEG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgB3C;;OAEG;YACW,gBAAgB;IAwE9B;;OAEG;YACW,uBAAuB;IA2FrC;;OAEG;YACW,iBAAiB;IAqC/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAUrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACI,SAAS,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB;CAOF"}
1
+ {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqCH;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA4B;IACnD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAA6C;IACjE,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,gBAAgB,CAAuC;IAC/D,OAAO,CAAC,oBAAoB,CAA+B;IAE3D,OAAO;IAMP;;OAEG;WACW,WAAW,IAAI,WAAW;IAOxC;;OAEG;IACH,OAAO,CAAC,KAAK;IASb;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsFnC;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkClC;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IA4BrG;;OAEG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAoB7D;;OAEG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgB3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;OAIG;IACU,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAUhD;;OAEG;YACW,kBAAkB;IAyBhC;;OAEG;YACW,gBAAgB;IAuF9B;;;;;OAKG;YACW,oBAAoB;IAsClC;;OAEG;YACW,uBAAuB;IA2FrC;;OAEG;YACW,iBAAiB;IAqC/B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;OASG;YACW,0BAA0B;IA6GxC;;OAEG;IACI,SAAS,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB;CAOF"}
@@ -2,17 +2,37 @@
2
2
  * File Watcher - Auto-tracking file changes linked to tasks
3
3
  * Monitors files and auto-transitions task status on file modification
4
4
  *
5
- * Features:
6
- * - chokidar-based file watching with debouncing
5
+ * Features (v3.5.1):
6
+ * - chokidar v4 file watching with automatic WSL support
7
+ * - Project root watching with .gitignore support
7
8
  * - Dynamic file registration (add/remove files at runtime)
8
9
  * - Auto-transition: todo → in_progress on file change
9
10
  * - Maps file paths → task IDs for efficient lookup
11
+ * - Respects .gitignore patterns and built-in ignore rules
10
12
  */
11
13
  import chokidar from 'chokidar';
12
- import { getDatabase } from '../database.js';
13
- import { basename, dirname } from 'path';
14
+ import { getAdapter, getConfigInt, getConfigBool } from '../database.js';
15
+ import { SQLiteAdapter } from '../adapters/index.js';
16
+ import { basename, join } from 'path';
14
17
  import { existsSync } from 'fs';
18
+ import { execSync } from 'child_process';
15
19
  import { executeAcceptanceCriteria } from './test-executor.js';
20
+ import { createGitIgnoreParser } from './gitignore-parser.js';
21
+ import { checkReadyForReview } from '../utils/quality-checks.js';
22
+ import { CONFIG_KEYS } from '../constants.js';
23
+ import { detectAndCompleteReviewedTasks, detectAndCompleteOnStaging, detectAndArchiveOnCommit } from '../utils/task-stale-detection.js';
24
+ import { detectVCS } from '../utils/vcs-adapter.js';
25
+ /**
26
+ * Helper to get raw better-sqlite3 Database instance from adapter
27
+ * For legacy code that uses db.prepare() directly
28
+ */
29
+ function getRawDb() {
30
+ const adapter = getAdapter();
31
+ if (adapter instanceof SQLiteAdapter) {
32
+ return adapter.getRawDatabase();
33
+ }
34
+ throw new Error('File watcher only supported for SQLite adapter');
35
+ }
16
36
  /**
17
37
  * FileWatcher class - Singleton pattern
18
38
  */
@@ -23,8 +43,15 @@ export class FileWatcher {
23
43
  isRunning = false;
24
44
  debounceTimers = new Map();
25
45
  DEBOUNCE_MS = 2000; // Wait 2s after last write
46
+ gitignoreParser = null;
47
+ projectRoot;
48
+ lastModifiedTimes = new Map(); // taskId -> timestamp
49
+ filesModifiedSet = new Map(); // taskId -> modified files
50
+ vcsDetectionInterval = null; // Periodic VCS re-detection (v3.5.3)
26
51
  constructor() {
27
52
  // Private constructor for singleton
53
+ // Determine project root (current working directory)
54
+ this.projectRoot = process.cwd();
28
55
  }
29
56
  /**
30
57
  * Get singleton instance
@@ -36,7 +63,19 @@ export class FileWatcher {
36
63
  return FileWatcher.instance;
37
64
  }
38
65
  /**
39
- * Initialize and start the file watcher
66
+ * Detect if running on WSL (Windows Subsystem for Linux)
67
+ */
68
+ isWSL() {
69
+ try {
70
+ const result = execSync('uname -r', { encoding: 'utf-8' });
71
+ return result.toLowerCase().includes('microsoft') || result.toLowerCase().includes('wsl');
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }
77
+ /**
78
+ * Initialize and start the file watcher (v3.5.1 - Chokidar v4)
40
79
  */
41
80
  async start() {
42
81
  if (this.isRunning) {
@@ -44,31 +83,71 @@ export class FileWatcher {
44
83
  return;
45
84
  }
46
85
  try {
47
- // Initialize chokidar with debouncing
48
- this.watcher = chokidar.watch([], {
86
+ // Initialize gitignore parser
87
+ this.gitignoreParser = createGitIgnoreParser(this.projectRoot);
88
+ // Detect WSL (informational only - chokidar v4 handles WSL automatically)
89
+ const isWSL = this.isWSL();
90
+ if (isWSL) {
91
+ console.error('✓ WSL detected - chokidar v4 handles WSL automatically');
92
+ }
93
+ // Initialize chokidar v4 with debouncing and gitignore support
94
+ // NOTE: Chokidar v4 automatically detects and handles WSL without manual polling configuration
95
+ this.watcher = chokidar.watch(this.projectRoot, {
49
96
  persistent: true,
50
97
  ignoreInitial: true, // Don't trigger on startup
51
98
  awaitWriteFinish: {
52
99
  stabilityThreshold: this.DEBOUNCE_MS,
53
100
  pollInterval: 100
54
101
  },
55
- ignored: /(^|[\/\\])\../, // Ignore dotfiles
102
+ ignored: (path) => {
103
+ // Use gitignore parser to filter files
104
+ if (this.gitignoreParser) {
105
+ return this.gitignoreParser.shouldIgnore(path);
106
+ }
107
+ // Fallback: ignore dotfiles
108
+ return /(^|[\/\\])\./.test(path);
109
+ },
56
110
  });
57
111
  // Set up event handlers
58
112
  this.watcher.on('change', (path) => {
59
- this.handleFileChange(path);
113
+ // Check if this is a VCS index file
114
+ if (this.isVCSIndexFile(path)) {
115
+ this.handleVCSIndexChange(path);
116
+ }
117
+ else {
118
+ this.handleFileChange(path);
119
+ }
60
120
  });
61
121
  this.watcher.on('add', (path) => {
62
- this.handleFileChange(path);
122
+ // Check if this is a VCS index file
123
+ if (this.isVCSIndexFile(path)) {
124
+ this.handleVCSIndexChange(path);
125
+ }
126
+ else {
127
+ this.handleFileChange(path);
128
+ }
63
129
  });
64
130
  this.watcher.on('error', (error) => {
65
131
  console.error('File watcher error:', error);
66
132
  });
67
133
  // Load existing task-file links from database
68
134
  await this.loadTaskFileLinks();
135
+ // Initialize tracking maps
136
+ this.lastModifiedTimes.clear();
137
+ this.filesModifiedSet.clear();
138
+ // Watch VCS index files for commit detection (v3.4.0 VCS-aware auto-complete)
139
+ await this.watchVCSIndexFiles();
140
+ // Periodic VCS re-detection (v3.5.3)
141
+ // Handles case where git is initialized after watcher starts
142
+ // Check every 5 minutes for new VCS initialization
143
+ this.vcsDetectionInterval = setInterval(async () => {
144
+ await this.refreshVCSWatching();
145
+ }, 5 * 60 * 1000); // 5 minutes
69
146
  this.isRunning = true;
70
147
  console.error('✓ File watcher started successfully');
148
+ console.error(` Project root: ${this.projectRoot}`);
71
149
  console.error(` Watching ${this.watchedFiles.size} files for ${this.getTotalTaskCount()} tasks`);
150
+ console.error(` .gitignore patterns loaded: ${existsSync(this.projectRoot + '/.gitignore') ? 'Yes' : 'No'}`);
72
151
  }
73
152
  catch (error) {
74
153
  console.error('Failed to start file watcher:', error);
@@ -86,6 +165,14 @@ export class FileWatcher {
86
165
  // Clear all debounce timers
87
166
  this.debounceTimers.forEach(timer => clearTimeout(timer));
88
167
  this.debounceTimers.clear();
168
+ // Clear tracking maps
169
+ this.lastModifiedTimes.clear();
170
+ this.filesModifiedSet.clear();
171
+ // Clear VCS detection interval (v3.5.3)
172
+ if (this.vcsDetectionInterval) {
173
+ clearInterval(this.vcsDetectionInterval);
174
+ this.vcsDetectionInterval = null;
175
+ }
89
176
  // Close watcher
90
177
  if (this.watcher) {
91
178
  await this.watcher.close();
@@ -112,18 +199,6 @@ export class FileWatcher {
112
199
  // Add to watched files map
113
200
  if (!this.watchedFiles.has(normalizedPath)) {
114
201
  this.watchedFiles.set(normalizedPath, []);
115
- // Check if file exists
116
- if (existsSync(normalizedPath)) {
117
- // File exists - watch it directly
118
- this.watcher.add(normalizedPath);
119
- console.error(` Watching file: ${basename(normalizedPath)} for task #${taskId}`);
120
- }
121
- else {
122
- // File doesn't exist - watch parent directory
123
- const parentDir = dirname(normalizedPath);
124
- this.watcher.add(parentDir);
125
- console.error(` Watching directory: ${parentDir} for task #${taskId} (file doesn't exist yet)`);
126
- }
127
202
  }
128
203
  const mappings = this.watchedFiles.get(normalizedPath);
129
204
  // Check if task already registered for this file
@@ -152,10 +227,7 @@ export class FileWatcher {
152
227
  if (filtered.length === 0) {
153
228
  // No more tasks watching this file
154
229
  this.watchedFiles.delete(normalizedPath);
155
- if (this.watcher) {
156
- this.watcher.unwatch(normalizedPath);
157
- }
158
- console.error(` Stopped watching: ${basename(normalizedPath)}`);
230
+ console.error(` Removed task mapping for: ${basename(normalizedPath)}`);
159
231
  }
160
232
  else {
161
233
  this.watchedFiles.set(normalizedPath, filtered);
@@ -177,6 +249,72 @@ export class FileWatcher {
177
249
  this.unregisterFile(filePath, taskId);
178
250
  });
179
251
  }
252
+ /**
253
+ * Check if a path is a VCS index file
254
+ */
255
+ isVCSIndexFile(path) {
256
+ // Git index file
257
+ if (path.endsWith('.git/index') || path.endsWith('.git\\index')) {
258
+ return true;
259
+ }
260
+ // Mercurial dirstate file
261
+ if (path.endsWith('.hg/dirstate') || path.endsWith('.hg\\dirstate')) {
262
+ return true;
263
+ }
264
+ // SVN doesn't have a local index file that changes on commit
265
+ return false;
266
+ }
267
+ /**
268
+ * Get VCS index file path for given VCS type (v3.5.3)
269
+ * Centralized mapping for easier extension to Mercurial/SVN
270
+ * @param vcsType - VCS type string (Git, Mercurial, SVN)
271
+ * @returns Absolute path to VCS index file, or null if VCS has no local index
272
+ */
273
+ getVCSIndexPath(vcsType) {
274
+ const vcsIndexPaths = {
275
+ 'Git': join(this.projectRoot, '.git', 'index'),
276
+ 'Mercurial': join(this.projectRoot, '.hg', 'dirstate'),
277
+ 'SVN': null, // SVN has no local index file (commits are remote)
278
+ };
279
+ return vcsIndexPaths[vcsType] || null;
280
+ }
281
+ /**
282
+ * Re-detect VCS and start watching index files (v3.5.3)
283
+ * Called when VCS might be initialized after watcher starts
284
+ * Public method for external triggering (e.g., after git init)
285
+ */
286
+ async refreshVCSWatching() {
287
+ if (!this.watcher || !this.isRunning) {
288
+ console.error('⚠ Cannot refresh VCS watching: watcher not running');
289
+ return;
290
+ }
291
+ console.error('\n🔍 Re-detecting VCS...');
292
+ await this.watchVCSIndexFiles();
293
+ }
294
+ /**
295
+ * Watch VCS index files for commit detection
296
+ */
297
+ async watchVCSIndexFiles() {
298
+ // Detect VCS type
299
+ const vcsAdapter = await detectVCS(this.projectRoot);
300
+ if (!vcsAdapter) {
301
+ console.error('ℹ No VCS detected - skipping VCS index watching');
302
+ return;
303
+ }
304
+ const vcsType = vcsAdapter.getVCSType();
305
+ const indexPath = this.getVCSIndexPath(vcsType);
306
+ if (indexPath === null) {
307
+ console.error(`ℹ ${vcsType} detected - no local index file to watch (commits are remote)`);
308
+ return;
309
+ }
310
+ if (existsSync(indexPath) && this.watcher) {
311
+ this.watcher.add(indexPath);
312
+ console.error(`✓ Watching ${indexPath} for ${vcsType} commits (v3.5.3)`);
313
+ }
314
+ else if (!existsSync(indexPath)) {
315
+ console.error(`⚠ ${vcsType} index file not found: ${indexPath}`);
316
+ }
317
+ }
180
318
  /**
181
319
  * Handle file change event
182
320
  */
@@ -187,10 +325,17 @@ export class FileWatcher {
187
325
  return;
188
326
  }
189
327
  console.error(`\n📝 File changed: ${basename(normalizedPath)}`);
190
- const db = getDatabase();
328
+ const adapter = getAdapter();
329
+ const db = getRawDb();
191
330
  // Process each task linked to this file
192
331
  for (const mapping of mappings) {
193
332
  const { taskId, taskTitle, currentStatus } = mapping;
333
+ // Track file modification
334
+ this.lastModifiedTimes.set(taskId, Date.now());
335
+ if (!this.filesModifiedSet.has(taskId)) {
336
+ this.filesModifiedSet.set(taskId, new Set());
337
+ }
338
+ this.filesModifiedSet.get(taskId).add(normalizedPath);
194
339
  // Auto-transition: todo → in_progress
195
340
  if (currentStatus === 'todo') {
196
341
  try {
@@ -231,17 +376,62 @@ export class FileWatcher {
231
376
  // Check acceptance criteria for in_progress tasks
232
377
  if (currentStatus === 'in_progress' || mapping.currentStatus === 'in_progress') {
233
378
  await this.checkAcceptanceCriteria(taskId, taskTitle, mapping);
379
+ // After debounce period, check if task is ready for review
380
+ // Use setTimeout to check after idle period
381
+ const idleMinutes = await getConfigInt(adapter, CONFIG_KEYS.REVIEW_IDLE_MINUTES, 15);
382
+ setTimeout(async () => {
383
+ await this.checkAndTransitionToReview(taskId);
384
+ }, idleMinutes * 60 * 1000);
234
385
  }
235
386
  else {
236
387
  console.error(` • Task #${taskId} "${taskTitle}": status ${currentStatus}`);
237
388
  }
238
389
  }
239
390
  }
391
+ /**
392
+ * Handle VCS index file change - triggers two-step Git-aware workflow (v3.5.2)
393
+ * Step 1: Staging (git add) → waiting_review → done
394
+ * Step 2: Commit (git commit) → done → archived
395
+ * Fallback: If files already committed (not in staging), use v3.4.0 logic
396
+ */
397
+ async handleVCSIndexChange(filePath) {
398
+ console.error('\n🔄 VCS index changed - checking for tasks ready to auto-transition');
399
+ const db = getAdapter();
400
+ try {
401
+ // Step 1: Check for staged files → complete tasks (waiting_review → done)
402
+ const stagingCompleted = await detectAndCompleteOnStaging(db);
403
+ // Step 2: Check for committed files → archive tasks (done → archived)
404
+ const commitArchived = await detectAndArchiveOnCommit(db);
405
+ // Fallback: Check for committed files → complete tasks (waiting_review → done)
406
+ // This handles cases where files go straight to commit without visible staging
407
+ const commitCompleted = await detectAndCompleteReviewedTasks(db);
408
+ // Log results
409
+ const transitions = [];
410
+ if (stagingCompleted > 0) {
411
+ transitions.push(`${stagingCompleted} task(s) completed (git add detected)`);
412
+ }
413
+ if (commitCompleted > 0) {
414
+ transitions.push(`${commitCompleted} task(s) completed (git commit fallback)`);
415
+ }
416
+ if (commitArchived > 0) {
417
+ transitions.push(`${commitArchived} task(s) archived (git commit detected)`);
418
+ }
419
+ if (transitions.length > 0) {
420
+ console.error(` ✓ ${transitions.join(', ')}`);
421
+ }
422
+ else {
423
+ console.error(` ℹ No tasks ready for auto-transition`);
424
+ }
425
+ }
426
+ catch (error) {
427
+ console.error(` ✗ Error during VCS-aware auto-transition:`, error);
428
+ }
429
+ }
240
430
  /**
241
431
  * Check acceptance criteria and auto-complete task if all pass
242
432
  */
243
433
  async checkAcceptanceCriteria(taskId, taskTitle, mapping) {
244
- const db = getDatabase();
434
+ const db = getRawDb();
245
435
  try {
246
436
  // Get acceptance criteria JSON
247
437
  const taskDetails = db.prepare(`
@@ -313,7 +503,7 @@ export class FileWatcher {
313
503
  * Load existing task-file links from database
314
504
  */
315
505
  async loadTaskFileLinks() {
316
- const db = getDatabase();
506
+ const db = getRawDb();
317
507
  try {
318
508
  // Query all active tasks with file links
319
509
  const query = `
@@ -341,13 +531,21 @@ export class FileWatcher {
341
531
  }
342
532
  }
343
533
  /**
344
- * Normalize file path (resolve relative paths, remove trailing slashes)
534
+ * Normalize file path (convert to relative path from project root, remove trailing slashes)
345
535
  */
346
536
  normalizePath(filePath) {
347
- // Remove trailing slashes
348
- let normalized = filePath.replace(/[\/\\]+$/, '');
349
537
  // Convert backslashes to forward slashes (Windows compatibility)
350
- normalized = normalized.replace(/\\/g, '/');
538
+ let normalized = filePath.replace(/\\/g, '/');
539
+ // Remove trailing slashes
540
+ normalized = normalized.replace(/[\/\\]+$/, '');
541
+ // Convert absolute paths to relative paths from project root
542
+ const projectRootNormalized = this.projectRoot.replace(/\\/g, '/');
543
+ if (normalized.startsWith(projectRootNormalized + '/')) {
544
+ normalized = normalized.substring(projectRootNormalized.length + 1);
545
+ }
546
+ else if (normalized.startsWith(projectRootNormalized)) {
547
+ normalized = normalized.substring(projectRootNormalized.length);
548
+ }
351
549
  return normalized;
352
550
  }
353
551
  /**
@@ -360,6 +558,104 @@ export class FileWatcher {
360
558
  });
361
559
  return taskIds.size;
362
560
  }
561
+ /**
562
+ * Check if task is ready for review and transition if conditions met
563
+ * Quality gates:
564
+ * - All watched files modified at least once
565
+ * - TypeScript compiles without errors (if .ts files)
566
+ * - Tests pass (if test files exist)
567
+ * - Idle for configured time (default 15 minutes)
568
+ *
569
+ * @param taskId - Task ID to check
570
+ */
571
+ async checkAndTransitionToReview(taskId) {
572
+ const adapter = getAdapter();
573
+ const db = getRawDb();
574
+ try {
575
+ // Get current task status
576
+ const task = db.prepare(`
577
+ SELECT t.status_id, s.name as status_name, td.acceptance_criteria_json
578
+ FROM t_tasks t
579
+ JOIN m_task_statuses s ON s.id = t.status_id
580
+ LEFT JOIN t_task_details td ON td.task_id = t.id
581
+ WHERE t.id = ?
582
+ `).get(taskId);
583
+ if (!task) {
584
+ return; // Task not found
585
+ }
586
+ // Only check for in_progress tasks
587
+ if (task.status_name !== 'in_progress') {
588
+ return;
589
+ }
590
+ // Read configuration
591
+ const idleMinutes = await getConfigInt(adapter, CONFIG_KEYS.REVIEW_IDLE_MINUTES, 15);
592
+ const requireAllFilesModified = await getConfigBool(adapter, CONFIG_KEYS.REVIEW_REQUIRE_ALL_FILES_MODIFIED, true);
593
+ const requireTestsPass = await getConfigBool(adapter, CONFIG_KEYS.REVIEW_REQUIRE_TESTS_PASS, true);
594
+ const requireCompile = await getConfigBool(adapter, CONFIG_KEYS.REVIEW_REQUIRE_COMPILE, true);
595
+ // Check idle time
596
+ const lastModified = this.lastModifiedTimes.get(taskId);
597
+ if (!lastModified) {
598
+ return; // No modifications tracked yet
599
+ }
600
+ const idleTimeMs = Date.now() - lastModified;
601
+ const requiredIdleMs = idleMinutes * 60 * 1000;
602
+ if (idleTimeMs < requiredIdleMs) {
603
+ return; // Not idle long enough
604
+ }
605
+ // Get all watched files for this task
606
+ const filePaths = [];
607
+ this.watchedFiles.forEach((mappings, path) => {
608
+ if (mappings.some(m => m.taskId === taskId)) {
609
+ filePaths.push(path);
610
+ }
611
+ });
612
+ if (filePaths.length === 0) {
613
+ return; // No files being watched
614
+ }
615
+ // Get modified files set
616
+ const modifiedFiles = this.filesModifiedSet.get(taskId) || new Set();
617
+ // Run quality checks
618
+ const { ready, results } = await checkReadyForReview(db, taskId, filePaths, modifiedFiles, {
619
+ requireAllFilesModified,
620
+ requireTestsPass,
621
+ requireCompile,
622
+ });
623
+ if (ready) {
624
+ // All quality gates passed - transition to waiting_review
625
+ console.error(` ✓ Quality checks passed for task #${taskId}`);
626
+ // Log individual results
627
+ results.forEach(({ check, result }) => {
628
+ console.error(` • ${check}: ${result.message}`);
629
+ });
630
+ // Update task status
631
+ db.prepare(`
632
+ UPDATE t_tasks
633
+ SET status_id = (SELECT id FROM m_task_statuses WHERE name = 'waiting_review'),
634
+ updated_ts = unixepoch()
635
+ WHERE id = ?
636
+ `).run(taskId);
637
+ console.error(` → Task #${taskId} auto-transitioned to waiting_review`);
638
+ // Clear tracking for this task
639
+ this.lastModifiedTimes.delete(taskId);
640
+ this.filesModifiedSet.delete(taskId);
641
+ }
642
+ else {
643
+ // Some checks failed - log details
644
+ const failedChecks = results.filter(({ result }) => !result.passed);
645
+ console.error(` ℹ Task #${taskId} not ready for review (${failedChecks.length} checks failed)`);
646
+ failedChecks.forEach(({ check, result }) => {
647
+ console.error(` • ${check}: ${result.message}`);
648
+ if (result.details) {
649
+ console.error(` ${result.details}`);
650
+ }
651
+ });
652
+ }
653
+ }
654
+ catch (error) {
655
+ // Log error but don't crash the watcher
656
+ console.error(`Error checking review readiness for task #${taskId}:`, error);
657
+ }
658
+ }
363
659
  /**
364
660
  * Get current watcher status
365
661
  */