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.
- package/CHANGELOG.md +288 -1011
- package/README.md +80 -263
- package/assets/config.example.toml +97 -0
- package/assets/schema.sql +6 -1
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +21 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mysql-adapter.d.ts +31 -0
- package/dist/adapters/mysql-adapter.d.ts.map +1 -0
- package/dist/adapters/mysql-adapter.js +63 -0
- package/dist/adapters/mysql-adapter.js.map +1 -0
- package/dist/adapters/postgresql-adapter.d.ts +31 -0
- package/dist/adapters/postgresql-adapter.d.ts.map +1 -0
- package/dist/adapters/postgresql-adapter.js +63 -0
- package/dist/adapters/postgresql-adapter.js.map +1 -0
- package/dist/adapters/sqlite-adapter.d.ts +37 -0
- package/dist/adapters/sqlite-adapter.d.ts.map +1 -0
- package/dist/adapters/sqlite-adapter.js +129 -0
- package/dist/adapters/sqlite-adapter.js.map +1 -0
- package/dist/adapters/types.d.ts +33 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli.js +55 -54
- package/dist/cli.js.map +1 -1
- package/dist/config/example-generator.d.ts +11 -0
- package/dist/config/example-generator.d.ts.map +1 -0
- package/dist/config/example-generator.js +48 -0
- package/dist/config/example-generator.js.map +1 -0
- package/dist/config/loader.d.ts +46 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +155 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +86 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +28 -0
- package/dist/config/types.js.map +1 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -1
- package/dist/database.d.ts +44 -122
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +145 -349
- package/dist/database.js.map +1 -1
- package/dist/index.js +223 -175
- package/dist/index.js.map +1 -1
- package/dist/knexfile.d.ts +6 -0
- package/dist/knexfile.d.ts.map +1 -0
- package/dist/knexfile.js +85 -0
- package/dist/knexfile.js.map +1 -0
- package/dist/migrations/add-help-system-tables.d.ts +35 -0
- package/dist/migrations/add-help-system-tables.d.ts.map +1 -0
- package/dist/migrations/add-help-system-tables.js +206 -0
- package/dist/migrations/add-help-system-tables.js.map +1 -0
- package/dist/migrations/add-token-tracking.d.ts +28 -0
- package/dist/migrations/add-token-tracking.d.ts.map +1 -0
- package/dist/migrations/add-token-tracking.js +108 -0
- package/dist/migrations/add-token-tracking.js.map +1 -0
- package/dist/migrations/add-v3.5.0-pruned-files.d.ts +26 -0
- package/dist/migrations/add-v3.5.0-pruned-files.d.ts.map +1 -0
- package/dist/migrations/add-v3.5.0-pruned-files.js +107 -0
- package/dist/migrations/add-v3.5.0-pruned-files.js.map +1 -0
- package/dist/migrations/index.d.ts +26 -12
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +162 -20
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/knex/20251025020452_create_master_tables.d.ts +4 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.js +65 -0
- package/dist/migrations/knex/20251025020452_create_master_tables.js.map +1 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts +4 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.js +235 -0
- package/dist/migrations/knex/20251025021152_create_transaction_tables.js.map +1 -0
- package/dist/migrations/knex/20251025021351_create_indexes.d.ts +4 -0
- package/dist/migrations/knex/20251025021351_create_indexes.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021351_create_indexes.js +62 -0
- package/dist/migrations/knex/20251025021351_create_indexes.js.map +1 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.d.ts +4 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.d.ts.map +1 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.js +58 -0
- package/dist/migrations/knex/20251025021416_seed_master_data.js.map +1 -0
- package/dist/migrations/knex/20251025070349_create_views.d.ts +4 -0
- package/dist/migrations/knex/20251025070349_create_views.d.ts.map +1 -0
- package/dist/migrations/knex/20251025070349_create_views.js +143 -0
- package/dist/migrations/knex/20251025070349_create_views.js.map +1 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts +4 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.d.ts.map +1 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js +15 -0
- package/dist/migrations/knex/20251025081221_add_link_type_to_task_decision_links.js.map +1 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts +8 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.d.ts.map +1 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js +12 -0
- package/dist/migrations/knex/20251025082220_fix_task_dependencies_columns.js.map +1 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts +19 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.d.ts.map +1 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.js +115 -0
- package/dist/migrations/knex/20251025090000_create_help_system_tables.js.map +1 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts +13 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.d.ts.map +1 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js +377 -0
- package/dist/migrations/knex/20251025090100_seed_help_categories_and_use_cases.js.map +1 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts +15 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.d.ts.map +1 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.js +253 -0
- package/dist/migrations/knex/20251025100000_seed_help_metadata.js.map +1 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts +16 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.d.ts.map +1 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js +276 -0
- package/dist/migrations/knex/20251025100100_seed_remaining_use_cases.js.map +1 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts +8 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.d.ts.map +1 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js +64 -0
- package/dist/migrations/knex/20251025120000_add_cascade_to_task_dependencies.js.map +1 -0
- package/dist/migrations/seed-help-data.d.ts +48 -0
- package/dist/migrations/seed-help-data.d.ts.map +1 -0
- package/dist/migrations/seed-help-data.js +1466 -0
- package/dist/migrations/seed-help-data.js.map +1 -0
- package/dist/migrations/seed-tool-metadata.d.ts +24 -0
- package/dist/migrations/seed-tool-metadata.d.ts.map +1 -0
- package/dist/migrations/seed-tool-metadata.js +392 -0
- package/dist/migrations/seed-tool-metadata.js.map +1 -0
- package/dist/migrations/v3.6.0-help-system-refactor.d.ts +46 -0
- package/dist/migrations/v3.6.0-help-system-refactor.d.ts.map +1 -0
- package/dist/migrations/v3.6.0-help-system-refactor.js +223 -0
- package/dist/migrations/v3.6.0-help-system-refactor.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -1
- package/dist/tests/git-aware-completion.test.d.ts +6 -0
- package/dist/tests/git-aware-completion.test.d.ts.map +1 -0
- package/dist/tests/git-aware-completion.test.js +160 -0
- package/dist/tests/git-aware-completion.test.js.map +1 -0
- package/dist/tests/help-system.test.d.ts +23 -0
- package/dist/tests/help-system.test.d.ts.map +1 -0
- package/dist/tests/help-system.test.js +374 -0
- package/dist/tests/help-system.test.js.map +1 -0
- package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts +6 -0
- package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts.map +1 -0
- package/dist/tests/tasks.auto-pruning-decision-link.test.js +264 -0
- package/dist/tests/tasks.auto-pruning-decision-link.test.js.map +1 -0
- package/dist/tests/tasks.auto-pruning-partial.test.d.ts +6 -0
- package/dist/tests/tasks.auto-pruning-partial.test.d.ts.map +1 -0
- package/dist/tests/tasks.auto-pruning-partial.test.js +285 -0
- package/dist/tests/tasks.auto-pruning-partial.test.js.map +1 -0
- package/dist/tests/tasks.auto-pruning-persistence.test.d.ts +6 -0
- package/dist/tests/tasks.auto-pruning-persistence.test.d.ts.map +1 -0
- package/dist/tests/tasks.auto-pruning-persistence.test.js +250 -0
- package/dist/tests/tasks.auto-pruning-persistence.test.js.map +1 -0
- package/dist/tests/tasks.auto-pruning-safety.test.d.ts +12 -0
- package/dist/tests/tasks.auto-pruning-safety.test.d.ts.map +1 -0
- package/dist/tests/tasks.auto-pruning-safety.test.js +217 -0
- package/dist/tests/tasks.auto-pruning-safety.test.js.map +1 -0
- package/dist/tests/tasks.dependencies.test.js +338 -307
- package/dist/tests/tasks.dependencies.test.js.map +1 -1
- package/dist/tests/tasks.link-file-backward-compat.test.d.ts +6 -0
- package/dist/tests/tasks.link-file-backward-compat.test.d.ts.map +1 -0
- package/dist/tests/tasks.link-file-backward-compat.test.js +247 -0
- package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -0
- package/dist/tests/tasks.watch-files-action.test.d.ts +6 -0
- package/dist/tests/tasks.watch-files-action.test.d.ts.map +1 -0
- package/dist/tests/tasks.watch-files-action.test.js +372 -0
- package/dist/tests/tasks.watch-files-action.test.js.map +1 -0
- package/dist/tests/tasks.watch-files-parameter.test.d.ts +6 -0
- package/dist/tests/tasks.watch-files-parameter.test.d.ts.map +1 -0
- package/dist/tests/tasks.watch-files-parameter.test.js +260 -0
- package/dist/tests/tasks.watch-files-parameter.test.js.map +1 -0
- package/dist/tests/two-step-git-completion.test.d.ts +6 -0
- package/dist/tests/two-step-git-completion.test.d.ts.map +1 -0
- package/dist/tests/two-step-git-completion.test.js +326 -0
- package/dist/tests/two-step-git-completion.test.js.map +1 -0
- package/dist/tests/vcs-staging.test.d.ts +6 -0
- package/dist/tests/vcs-staging.test.d.ts.map +1 -0
- package/dist/tests/vcs-staging.test.js +137 -0
- package/dist/tests/vcs-staging.test.js.map +1 -0
- package/dist/tools/config.d.ts +9 -4
- package/dist/tools/config.d.ts.map +1 -1
- package/dist/tools/config.js +16 -12
- package/dist/tools/config.js.map +1 -1
- package/dist/tools/constraints.d.ts +9 -3
- package/dist/tools/constraints.d.ts.map +1 -1
- package/dist/tools/constraints.js +66 -45
- package/dist/tools/constraints.js.map +1 -1
- package/dist/tools/context.d.ts +35 -16
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +374 -314
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/files.d.ts +11 -4
- package/dist/tools/files.d.ts.map +1 -1
- package/dist/tools/files.js +173 -91
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/help-queries.d.ts +130 -0
- package/dist/tools/help-queries.d.ts.map +1 -0
- package/dist/tools/help-queries.js +393 -0
- package/dist/tools/help-queries.js.map +1 -0
- package/dist/tools/messaging.d.ts +13 -6
- package/dist/tools/messaging.d.ts.map +1 -1
- package/dist/tools/messaging.js +217 -129
- package/dist/tools/messaging.js.map +1 -1
- package/dist/tools/tasks.d.ts +42 -12
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +809 -347
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/utils.d.ts +13 -5
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +92 -115
- package/dist/tools/utils.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/activity-logging.d.ts +114 -0
- package/dist/utils/activity-logging.d.ts.map +1 -0
- package/dist/utils/activity-logging.js +162 -0
- package/dist/utils/activity-logging.js.map +1 -0
- package/dist/utils/batch.d.ts +2 -2
- package/dist/utils/batch.d.ts.map +1 -1
- package/dist/utils/batch.js +8 -8
- package/dist/utils/batch.js.map +1 -1
- package/dist/utils/cleanup.d.ts +21 -13
- package/dist/utils/cleanup.d.ts.map +1 -1
- package/dist/utils/cleanup.js +31 -24
- package/dist/utils/cleanup.js.map +1 -1
- package/dist/utils/debug-logger.d.ts +44 -0
- package/dist/utils/debug-logger.d.ts.map +1 -0
- package/dist/utils/debug-logger.js +116 -0
- package/dist/utils/debug-logger.js.map +1 -0
- package/dist/utils/file-pruning.d.ts +69 -0
- package/dist/utils/file-pruning.d.ts.map +1 -0
- package/dist/utils/file-pruning.js +185 -0
- package/dist/utils/file-pruning.js.map +1 -0
- package/dist/utils/help-tracking.d.ts +55 -0
- package/dist/utils/help-tracking.d.ts.map +1 -0
- package/dist/utils/help-tracking.js +88 -0
- package/dist/utils/help-tracking.js.map +1 -0
- package/dist/utils/quality-checks.d.ts +60 -0
- package/dist/utils/quality-checks.d.ts.map +1 -0
- package/dist/utils/quality-checks.js +228 -0
- package/dist/utils/quality-checks.js.map +1 -0
- package/dist/utils/retention.d.ts +13 -5
- package/dist/utils/retention.d.ts.map +1 -1
- package/dist/utils/retention.js +20 -8
- package/dist/utils/retention.js.map +1 -1
- package/dist/utils/task-stale-detection.d.ts +77 -7
- package/dist/utils/task-stale-detection.d.ts.map +1 -1
- package/dist/utils/task-stale-detection.js +309 -34
- package/dist/utils/task-stale-detection.js.map +1 -1
- package/dist/utils/token-estimation.d.ts +72 -0
- package/dist/utils/token-estimation.d.ts.map +1 -0
- package/dist/utils/token-estimation.js +71 -0
- package/dist/utils/token-estimation.js.map +1 -0
- package/dist/utils/token-logging.d.ts +48 -0
- package/dist/utils/token-logging.d.ts.map +1 -0
- package/dist/utils/token-logging.js +112 -0
- package/dist/utils/token-logging.js.map +1 -0
- package/dist/utils/vcs-adapter.d.ts +68 -0
- package/dist/utils/vcs-adapter.d.ts.map +1 -0
- package/dist/utils/vcs-adapter.js +187 -0
- package/dist/utils/vcs-adapter.js.map +1 -0
- package/dist/utils/view-queries.d.ts +34 -0
- package/dist/utils/view-queries.d.ts.map +1 -0
- package/dist/utils/view-queries.js +192 -0
- package/dist/utils/view-queries.js.map +1 -0
- package/dist/watcher/file-watcher.d.ts +54 -4
- package/dist/watcher/file-watcher.d.ts.map +1 -1
- package/dist/watcher/file-watcher.js +329 -33
- package/dist/watcher/file-watcher.js.map +1 -1
- package/dist/watcher/gitignore-parser.d.ts +70 -0
- package/dist/watcher/gitignore-parser.d.ts.map +1 -0
- package/dist/watcher/gitignore-parser.js +191 -0
- package/dist/watcher/gitignore-parser.js.map +1 -0
- package/dist/watcher/index.d.ts +1 -0
- package/dist/watcher/index.d.ts.map +1 -1
- package/dist/watcher/index.js +1 -0
- package/dist/watcher/index.js.map +1 -1
- package/docs/AI_AGENT_GUIDE.md +1 -1
- package/docs/ARCHITECTURE.md +12 -0
- package/docs/AUTO_FILE_TRACKING.md +486 -82
- package/docs/BEST_PRACTICES.md +56 -448
- package/docs/CONFIGURATION.md +908 -0
- package/docs/GIT_AWARE_AUTO_COMPLETE.md +645 -0
- package/docs/MIGRATION_v3.3.md +602 -0
- package/docs/MIGRATION_v3.6.0.md +170 -0
- package/docs/SHARED_CONCEPTS.md +65 -209
- package/docs/TASK_ACTIONS.md +12 -0
- package/docs/TASK_OVERVIEW.md +125 -24
- package/docs/TASK_PRUNING.md +589 -0
- package/docs/TASK_SYSTEM.md +83 -13
- package/docs/TOOL_REFERENCE.md +94 -6
- package/docs/TOOL_SELECTION.md +41 -248
- 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
|
|
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
|
-
*
|
|
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 (
|
|
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
|
|
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
|
|
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 {
|
|
13
|
-
import {
|
|
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
|
-
*
|
|
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
|
|
48
|
-
this.
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
*/
|