sqlew 3.2.5 → 3.5.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 (144) hide show
  1. package/CHANGELOG.md +815 -13
  2. package/README.md +53 -2
  3. package/assets/schema.sql +6 -1
  4. package/dist/config/loader.d.ts +46 -0
  5. package/dist/config/loader.d.ts.map +1 -0
  6. package/dist/config/loader.js +151 -0
  7. package/dist/config/loader.js.map +1 -0
  8. package/dist/config/types.d.ts +77 -0
  9. package/dist/config/types.d.ts.map +1 -0
  10. package/dist/config/types.js +28 -0
  11. package/dist/config/types.js.map +1 -0
  12. package/dist/constants.d.ts +9 -0
  13. package/dist/constants.d.ts.map +1 -1
  14. package/dist/constants.js +10 -0
  15. package/dist/constants.js.map +1 -1
  16. package/dist/database.d.ts +1 -1
  17. package/dist/database.d.ts.map +1 -1
  18. package/dist/database.js +77 -10
  19. package/dist/database.js.map +1 -1
  20. package/dist/index.js +21 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/migrations/add-v3.5.0-pruned-files.d.ts +26 -0
  23. package/dist/migrations/add-v3.5.0-pruned-files.d.ts.map +1 -0
  24. package/dist/migrations/add-v3.5.0-pruned-files.js +107 -0
  25. package/dist/migrations/add-v3.5.0-pruned-files.js.map +1 -0
  26. package/dist/migrations/index.d.ts +2 -1
  27. package/dist/migrations/index.d.ts.map +1 -1
  28. package/dist/migrations/index.js +16 -1
  29. package/dist/migrations/index.js.map +1 -1
  30. package/dist/tests/git-aware-completion.test.d.ts +6 -0
  31. package/dist/tests/git-aware-completion.test.d.ts.map +1 -0
  32. package/dist/tests/git-aware-completion.test.js +141 -0
  33. package/dist/tests/git-aware-completion.test.js.map +1 -0
  34. package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts +6 -0
  35. package/dist/tests/tasks.auto-pruning-decision-link.test.d.ts.map +1 -0
  36. package/dist/tests/tasks.auto-pruning-decision-link.test.js +250 -0
  37. package/dist/tests/tasks.auto-pruning-decision-link.test.js.map +1 -0
  38. package/dist/tests/tasks.auto-pruning-partial.test.d.ts +6 -0
  39. package/dist/tests/tasks.auto-pruning-partial.test.d.ts.map +1 -0
  40. package/dist/tests/tasks.auto-pruning-partial.test.js +274 -0
  41. package/dist/tests/tasks.auto-pruning-partial.test.js.map +1 -0
  42. package/dist/tests/tasks.auto-pruning-persistence.test.d.ts +6 -0
  43. package/dist/tests/tasks.auto-pruning-persistence.test.d.ts.map +1 -0
  44. package/dist/tests/tasks.auto-pruning-persistence.test.js +232 -0
  45. package/dist/tests/tasks.auto-pruning-persistence.test.js.map +1 -0
  46. package/dist/tests/tasks.auto-pruning-safety.test.d.ts +12 -0
  47. package/dist/tests/tasks.auto-pruning-safety.test.d.ts.map +1 -0
  48. package/dist/tests/tasks.auto-pruning-safety.test.js +196 -0
  49. package/dist/tests/tasks.auto-pruning-safety.test.js.map +1 -0
  50. package/dist/tests/tasks.link-file-backward-compat.test.d.ts +6 -0
  51. package/dist/tests/tasks.link-file-backward-compat.test.d.ts.map +1 -0
  52. package/dist/tests/tasks.link-file-backward-compat.test.js +235 -0
  53. package/dist/tests/tasks.link-file-backward-compat.test.js.map +1 -0
  54. package/dist/tests/tasks.watch-files-action.test.d.ts +6 -0
  55. package/dist/tests/tasks.watch-files-action.test.d.ts.map +1 -0
  56. package/dist/tests/tasks.watch-files-action.test.js +351 -0
  57. package/dist/tests/tasks.watch-files-action.test.js.map +1 -0
  58. package/dist/tests/tasks.watch-files-parameter.test.d.ts +6 -0
  59. package/dist/tests/tasks.watch-files-parameter.test.d.ts.map +1 -0
  60. package/dist/tests/tasks.watch-files-parameter.test.js +249 -0
  61. package/dist/tests/tasks.watch-files-parameter.test.js.map +1 -0
  62. package/dist/tests/two-step-git-completion.test.d.ts +6 -0
  63. package/dist/tests/two-step-git-completion.test.d.ts.map +1 -0
  64. package/dist/tests/two-step-git-completion.test.js +283 -0
  65. package/dist/tests/two-step-git-completion.test.js.map +1 -0
  66. package/dist/tests/vcs-staging.test.d.ts +6 -0
  67. package/dist/tests/vcs-staging.test.d.ts.map +1 -0
  68. package/dist/tests/vcs-staging.test.js +137 -0
  69. package/dist/tests/vcs-staging.test.js.map +1 -0
  70. package/dist/tools/config.d.ts +4 -2
  71. package/dist/tools/config.d.ts.map +1 -1
  72. package/dist/tools/config.js +13 -11
  73. package/dist/tools/config.js.map +1 -1
  74. package/dist/tools/constraints.d.ts +7 -4
  75. package/dist/tools/constraints.d.ts.map +1 -1
  76. package/dist/tools/constraints.js +19 -16
  77. package/dist/tools/constraints.js.map +1 -1
  78. package/dist/tools/context.d.ts +33 -17
  79. package/dist/tools/context.d.ts.map +1 -1
  80. package/dist/tools/context.js +84 -68
  81. package/dist/tools/context.js.map +1 -1
  82. package/dist/tools/files.d.ts +9 -5
  83. package/dist/tools/files.d.ts.map +1 -1
  84. package/dist/tools/files.js +19 -15
  85. package/dist/tools/files.js.map +1 -1
  86. package/dist/tools/messaging.d.ts +9 -5
  87. package/dist/tools/messaging.d.ts.map +1 -1
  88. package/dist/tools/messaging.js +20 -16
  89. package/dist/tools/messaging.js.map +1 -1
  90. package/dist/tools/tasks.d.ts +40 -12
  91. package/dist/tools/tasks.d.ts.map +1 -1
  92. package/dist/tools/tasks.js +475 -87
  93. package/dist/tools/tasks.js.map +1 -1
  94. package/dist/tools/utils.d.ts +11 -6
  95. package/dist/tools/utils.d.ts.map +1 -1
  96. package/dist/tools/utils.js +56 -44
  97. package/dist/tools/utils.js.map +1 -1
  98. package/dist/types.d.ts +4 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/utils/file-pruning.d.ts +69 -0
  101. package/dist/utils/file-pruning.d.ts.map +1 -0
  102. package/dist/utils/file-pruning.js +185 -0
  103. package/dist/utils/file-pruning.js.map +1 -0
  104. package/dist/utils/quality-checks.d.ts +60 -0
  105. package/dist/utils/quality-checks.d.ts.map +1 -0
  106. package/dist/utils/quality-checks.js +228 -0
  107. package/dist/utils/quality-checks.js.map +1 -0
  108. package/dist/utils/retention.d.ts +8 -0
  109. package/dist/utils/retention.d.ts.map +1 -1
  110. package/dist/utils/retention.js +12 -0
  111. package/dist/utils/retention.js.map +1 -1
  112. package/dist/utils/task-stale-detection.d.ts +69 -1
  113. package/dist/utils/task-stale-detection.d.ts.map +1 -1
  114. package/dist/utils/task-stale-detection.js +494 -17
  115. package/dist/utils/task-stale-detection.js.map +1 -1
  116. package/dist/utils/vcs-adapter.d.ts +68 -0
  117. package/dist/utils/vcs-adapter.d.ts.map +1 -0
  118. package/dist/utils/vcs-adapter.js +187 -0
  119. package/dist/utils/vcs-adapter.js.map +1 -0
  120. package/dist/watcher/file-watcher.d.ts +54 -4
  121. package/dist/watcher/file-watcher.d.ts.map +1 -1
  122. package/dist/watcher/file-watcher.js +312 -30
  123. package/dist/watcher/file-watcher.js.map +1 -1
  124. package/dist/watcher/gitignore-parser.d.ts +70 -0
  125. package/dist/watcher/gitignore-parser.d.ts.map +1 -0
  126. package/dist/watcher/gitignore-parser.js +191 -0
  127. package/dist/watcher/gitignore-parser.js.map +1 -0
  128. package/dist/watcher/index.d.ts +1 -0
  129. package/dist/watcher/index.d.ts.map +1 -1
  130. package/dist/watcher/index.js +1 -0
  131. package/dist/watcher/index.js.map +1 -1
  132. package/docs/AI_AGENT_GUIDE.md +1 -1
  133. package/docs/ARCHITECTURE.md +12 -0
  134. package/docs/AUTO_FILE_TRACKING.md +486 -82
  135. package/docs/CONFIGURATION.md +908 -0
  136. package/docs/GIT_AWARE_AUTO_COMPLETE.md +645 -0
  137. package/docs/MIGRATION_v3.3.md +602 -0
  138. package/docs/SHARED_CONCEPTS.md +2 -1
  139. package/docs/TASK_ACTIONS.md +12 -0
  140. package/docs/TASK_OVERVIEW.md +124 -23
  141. package/docs/TASK_PRUNING.md +589 -0
  142. package/docs/TASK_SYSTEM.md +83 -13
  143. package/docs/TOOL_REFERENCE.md +94 -6
  144. package/package.json +8 -6
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Integration tests for v3.5.0 Auto-Pruning persistence
3
+ * Tests audit trail persistence after task archival (no cascade deletion)
4
+ */
5
+ import { describe, it, beforeEach, afterEach } from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import Database from 'better-sqlite3';
8
+ import { initializeSchema } from '../schema.js';
9
+ import { runAllMigrations } from '../migrations/index.js';
10
+ import { getOrCreateAgent, transaction } from '../database.js';
11
+ /**
12
+ * Test database instance
13
+ */
14
+ let testDb;
15
+ /**
16
+ * Create an in-memory test database with schema and migrations
17
+ */
18
+ function createTestDatabase() {
19
+ const db = new Database(':memory:');
20
+ db.pragma('foreign_keys = ON');
21
+ initializeSchema(db);
22
+ // Run migrations to add t_task_pruned_files table (v3.5.0)
23
+ runAllMigrations(db);
24
+ return db;
25
+ }
26
+ /**
27
+ * Helper: Create a test task in 'done' status (ready to archive)
28
+ */
29
+ function createTestTask(db, title) {
30
+ const agentId = getOrCreateAgent(db, 'test-agent');
31
+ const statusId = 5; // done (ready to archive)
32
+ const result = db.prepare(`
33
+ INSERT INTO t_tasks (title, status_id, priority, created_by_agent_id, assigned_agent_id)
34
+ VALUES (?, ?, 2, ?, ?)
35
+ `).run(title, statusId, agentId, agentId);
36
+ return result.lastInsertRowid;
37
+ }
38
+ /**
39
+ * Helper: Create a pruned file record in audit table
40
+ */
41
+ function createPrunedFileRecord(db, taskId, filePath) {
42
+ const result = db.prepare(`
43
+ INSERT INTO t_task_pruned_files (task_id, file_path, pruned_ts)
44
+ VALUES (?, ?, unixepoch())
45
+ `).run(taskId, filePath);
46
+ return result.lastInsertRowid;
47
+ }
48
+ /**
49
+ * Helper: Get task status by ID
50
+ */
51
+ function getTaskStatus(db, taskId) {
52
+ const row = db.prepare('SELECT status_id FROM t_tasks WHERE id = ?').get(taskId);
53
+ if (!row) {
54
+ throw new Error(`Task not found: ${taskId}`);
55
+ }
56
+ return row.status_id;
57
+ }
58
+ /**
59
+ * Helper: Count pruned file records for a task
60
+ */
61
+ function countPrunedFiles(db, taskId) {
62
+ const row = db.prepare(`
63
+ SELECT COUNT(*) as count FROM t_task_pruned_files WHERE task_id = ?
64
+ `).get(taskId);
65
+ return row.count;
66
+ }
67
+ /**
68
+ * Helper: Archive a task (test version)
69
+ */
70
+ function archiveTask(db, taskId) {
71
+ const TASK_STATUS_DONE = 5;
72
+ const TASK_STATUS_ARCHIVED = 6;
73
+ return transaction(db, () => {
74
+ // Check if task is in 'done' status
75
+ const taskRow = db.prepare('SELECT status_id FROM t_tasks WHERE id = ?').get(taskId);
76
+ if (!taskRow) {
77
+ throw new Error(`Task with id ${taskId} not found`);
78
+ }
79
+ if (taskRow.status_id !== TASK_STATUS_DONE) {
80
+ throw new Error(`Task ${taskId} must be in 'done' status to archive`);
81
+ }
82
+ // Update to archived
83
+ db.prepare('UPDATE t_tasks SET status_id = ? WHERE id = ?').run(TASK_STATUS_ARCHIVED, taskId);
84
+ return { success: true, task_id: taskId };
85
+ });
86
+ }
87
+ /**
88
+ * Helper: Get pruned files for a task (test version)
89
+ */
90
+ function getPrunedFiles(db, taskId) {
91
+ const rows = db.prepare(`
92
+ SELECT file_path, pruned_ts
93
+ FROM t_task_pruned_files
94
+ WHERE task_id = ?
95
+ ORDER BY pruned_ts DESC
96
+ `).all(taskId);
97
+ return {
98
+ success: true,
99
+ count: rows.length,
100
+ pruned_files: rows
101
+ };
102
+ }
103
+ describe('Auto-pruning: Audit trail persistence after archival', () => {
104
+ beforeEach(() => {
105
+ testDb = createTestDatabase();
106
+ });
107
+ afterEach(() => {
108
+ testDb.close();
109
+ });
110
+ it('should preserve audit trail after task archival', () => {
111
+ // 1. Setup: Create task with pruned files
112
+ const taskId = createTestTask(testDb, 'Task with pruned files');
113
+ // Create multiple pruned file records
114
+ const prunedFileIds = [
115
+ createPrunedFileRecord(testDb, taskId, '/tmp/file1.ts'),
116
+ createPrunedFileRecord(testDb, taskId, '/tmp/file2.ts'),
117
+ createPrunedFileRecord(testDb, taskId, '/tmp/file3.ts')
118
+ ];
119
+ assert.equal(prunedFileIds.length, 3, 'Should create 3 pruned file records');
120
+ // 2. Verify pruned files exist before archival
121
+ const beforeCount = countPrunedFiles(testDb, taskId);
122
+ assert.equal(beforeCount, 3, 'Should have 3 pruned file records before archival');
123
+ // 3. Archive the task
124
+ const archiveResult = archiveTask(testDb, taskId);
125
+ assert.ok(archiveResult.success, 'Task archival should succeed');
126
+ assert.equal(archiveResult.task_id, taskId);
127
+ // 4. Verify task is archived
128
+ const taskStatus = getTaskStatus(testDb, taskId);
129
+ assert.equal(taskStatus, 6, 'Task should be archived (status_id 6)');
130
+ // 5. Verify pruned files still exist (NOT cascade deleted)
131
+ const afterCount = countPrunedFiles(testDb, taskId);
132
+ assert.equal(afterCount, 3, 'Should still have 3 pruned file records after archival');
133
+ // 6. Verify get_pruned_files works for archived tasks
134
+ const getPrunedResult = getPrunedFiles(testDb, taskId);
135
+ assert.ok(getPrunedResult.success, 'get_pruned_files should work for archived tasks');
136
+ assert.equal(getPrunedResult.count, 3, 'Should return count of 3 pruned files');
137
+ assert.equal(getPrunedResult.pruned_files.length, 3, 'Should return all 3 pruned files');
138
+ // Verify file paths are preserved
139
+ const filePaths = getPrunedResult.pruned_files.map((f) => f.file_path);
140
+ assert.ok(filePaths.includes('/tmp/file1.ts'), 'Should include file1.ts');
141
+ assert.ok(filePaths.includes('/tmp/file2.ts'), 'Should include file2.ts');
142
+ assert.ok(filePaths.includes('/tmp/file3.ts'), 'Should include file3.ts');
143
+ });
144
+ it('should maintain foreign key integrity after archival', () => {
145
+ const taskId = createTestTask(testDb, 'Task for FK test');
146
+ createPrunedFileRecord(testDb, taskId, '/tmp/test.ts');
147
+ // Archive task
148
+ const archiveResult = archiveTask(testDb, taskId);
149
+ assert.ok(archiveResult.success, 'Task archival should succeed');
150
+ // Verify foreign key still valid (can JOIN successfully)
151
+ const result = testDb.prepare(`
152
+ SELECT tpf.id, tpf.task_id, t.status_id
153
+ FROM t_task_pruned_files tpf
154
+ JOIN t_tasks t ON tpf.task_id = t.id
155
+ WHERE tpf.task_id = ?
156
+ `).get(taskId);
157
+ assert.ok(result, 'Foreign key join should succeed');
158
+ assert.equal(result.task_id, taskId, 'Task ID should match');
159
+ assert.equal(result.status_id, 6, 'Task status should be archived (6)');
160
+ });
161
+ it('should handle zero pruned files for archived task', () => {
162
+ const taskId = createTestTask(testDb, 'Task with no pruned files');
163
+ // Archive without any pruned files
164
+ const archiveResult = archiveTask(testDb, taskId);
165
+ assert.ok(archiveResult.success);
166
+ // Query pruned files - should return empty array
167
+ const getPrunedResult = getPrunedFiles(testDb, taskId);
168
+ assert.ok(getPrunedResult.success);
169
+ assert.equal(getPrunedResult.count, 0);
170
+ assert.equal(getPrunedResult.pruned_files.length, 0);
171
+ });
172
+ it('should preserve pruned file timestamps after archival', () => {
173
+ const taskId = createTestTask(testDb, 'Task for timestamp test');
174
+ // Create pruned file with explicit timestamp check
175
+ const beforeTs = Math.floor(Date.now() / 1000);
176
+ createPrunedFileRecord(testDb, taskId, '/tmp/timestamped.ts');
177
+ // Archive task
178
+ archiveTask(testDb, taskId);
179
+ // Get pruned files and verify timestamp
180
+ const getPrunedResult = getPrunedFiles(testDb, taskId);
181
+ assert.equal(getPrunedResult.pruned_files.length, 1);
182
+ const prunedFile = getPrunedResult.pruned_files[0];
183
+ assert.ok(prunedFile.pruned_ts, 'Should have pruned_ts timestamp');
184
+ assert.ok(prunedFile.file_path === '/tmp/timestamped.ts', 'File path should match');
185
+ // Verify timestamp is reasonable (within last few seconds)
186
+ const prunedAt = prunedFile.pruned_ts;
187
+ assert.ok(prunedAt >= beforeTs, 'Timestamp should be after or equal to test start');
188
+ assert.ok(prunedAt <= beforeTs + 5, 'Timestamp should be within 5 seconds');
189
+ });
190
+ it('should handle multiple archived tasks with pruned files', () => {
191
+ // Create multiple tasks with pruned files
192
+ const task1Id = createTestTask(testDb, 'Task 1');
193
+ const task2Id = createTestTask(testDb, 'Task 2');
194
+ const task3Id = createTestTask(testDb, 'Task 3');
195
+ createPrunedFileRecord(testDb, task1Id, '/tmp/task1-file1.ts');
196
+ createPrunedFileRecord(testDb, task1Id, '/tmp/task1-file2.ts');
197
+ createPrunedFileRecord(testDb, task2Id, '/tmp/task2-file1.ts');
198
+ createPrunedFileRecord(testDb, task3Id, '/tmp/task3-file1.ts');
199
+ createPrunedFileRecord(testDb, task3Id, '/tmp/task3-file2.ts');
200
+ createPrunedFileRecord(testDb, task3Id, '/tmp/task3-file3.ts');
201
+ // Archive all tasks
202
+ archiveTask(testDb, task1Id);
203
+ archiveTask(testDb, task2Id);
204
+ archiveTask(testDb, task3Id);
205
+ // Verify each task's pruned files are isolated and preserved
206
+ const task1Pruned = getPrunedFiles(testDb, task1Id);
207
+ assert.equal(task1Pruned.count, 2, 'Task 1 should have 2 pruned files');
208
+ const task2Pruned = getPrunedFiles(testDb, task2Id);
209
+ assert.equal(task2Pruned.count, 1, 'Task 2 should have 1 pruned file');
210
+ const task3Pruned = getPrunedFiles(testDb, task3Id);
211
+ assert.equal(task3Pruned.count, 3, 'Task 3 should have 3 pruned files');
212
+ // Verify file paths are isolated (no cross-contamination)
213
+ const task1Paths = task1Pruned.pruned_files.map((f) => f.file_path);
214
+ assert.ok(!task1Paths.includes('/tmp/task2-file1.ts'), 'Task 1 should not include Task 2 files');
215
+ assert.ok(!task1Paths.includes('/tmp/task3-file1.ts'), 'Task 1 should not include Task 3 files');
216
+ });
217
+ it('should verify CASCADE constraint when task is deleted (not archived)', () => {
218
+ // This test verifies the ON DELETE CASCADE behavior
219
+ const taskId = createTestTask(testDb, 'Task for deletion test');
220
+ createPrunedFileRecord(testDb, taskId, '/tmp/cascade-test.ts');
221
+ // Verify pruned file exists
222
+ const beforeCount = countPrunedFiles(testDb, taskId);
223
+ assert.equal(beforeCount, 1);
224
+ // DELETE task (not archive) - should cascade delete pruned files
225
+ testDb.prepare('DELETE FROM t_tasks WHERE id = ?').run(taskId);
226
+ // Verify pruned file was cascade deleted
227
+ const afterCount = countPrunedFiles(testDb, taskId);
228
+ assert.equal(afterCount, 0, 'Pruned files should be CASCADE deleted when task is deleted');
229
+ });
230
+ console.log('\n✅ All auto-pruning persistence tests passed!\n');
231
+ });
232
+ //# sourceMappingURL=tasks.auto-pruning-persistence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.auto-pruning-persistence.test.js","sourceRoot":"","sources":["../../src/tests/tasks.auto-pruning-persistence.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Integration tests for v3.5.0 Auto-Pruning Safety Check
3
+ * Tests that the system blocks task transition when ALL watched files are non-existent
4
+ *
5
+ * Test Scenario: Zero Work Done Protection
6
+ * - Create task with 3 watched files
7
+ * - Make ALL 3 files non-existent
8
+ * - Trigger detectAndTransitionToReview()
9
+ * - Expected: Error thrown, task status unchanged, transaction rolled back
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=tasks.auto-pruning-safety.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.auto-pruning-safety.test.d.ts","sourceRoot":"","sources":["../../src/tests/tasks.auto-pruning-safety.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Integration tests for v3.5.0 Auto-Pruning Safety Check
3
+ * Tests that the system blocks task transition when ALL watched files are non-existent
4
+ *
5
+ * Test Scenario: Zero Work Done Protection
6
+ * - Create task with 3 watched files
7
+ * - Make ALL 3 files non-existent
8
+ * - Trigger detectAndTransitionToReview()
9
+ * - Expected: Error thrown, task status unchanged, transaction rolled back
10
+ */
11
+ import { describe, it, beforeEach, afterEach } from 'node:test';
12
+ import assert from 'node:assert/strict';
13
+ import Database from 'better-sqlite3';
14
+ import { initializeSchema } from '../schema.js';
15
+ import { runAllMigrations } from '../migrations/index.js';
16
+ import { detectAndTransitionToReview } from '../utils/task-stale-detection.js';
17
+ describe('Auto-pruning: Safety check when all files pruned', () => {
18
+ let db;
19
+ beforeEach(() => {
20
+ db = new Database(':memory:');
21
+ db.pragma('foreign_keys = ON');
22
+ initializeSchema(db);
23
+ runAllMigrations(db);
24
+ });
25
+ afterEach(() => {
26
+ db.close();
27
+ });
28
+ it('should block transition when all watched files are non-existent', async () => {
29
+ // 1. Create test task in 'in_progress' status
30
+ const taskId = createTestTask(db);
31
+ // 2. Add 3 non-existent files to watch list
32
+ // Using paths that will never exist in the test environment
33
+ const nonExistentFiles = [
34
+ '/tmp/never-created-file-1.ts',
35
+ '/tmp/never-created-file-2.ts',
36
+ '/tmp/never-created-file-3.ts'
37
+ ];
38
+ addWatchedFiles(db, taskId, nonExistentFiles);
39
+ // 3. Make task appear "stale" by backdating its updated_ts
40
+ // Set updated_ts to 10 minutes ago (older than default 3-minute idle threshold)
41
+ const tenMinutesAgo = Math.floor(Date.now() / 1000) - (10 * 60);
42
+ db.prepare(`
43
+ UPDATE t_tasks
44
+ SET updated_ts = ?
45
+ WHERE id = ?
46
+ `).run(tenMinutesAgo, taskId);
47
+ // 4. Verify task is in 'in_progress' before attempt
48
+ const beforeStatus = db.prepare(`
49
+ SELECT s.name
50
+ FROM t_tasks t
51
+ JOIN m_task_statuses s ON t.status_id = s.id
52
+ WHERE t.id = ?
53
+ `).get(taskId);
54
+ assert.strictEqual(beforeStatus.name, 'in_progress', 'Task should start in in_progress');
55
+ // 5. Verify watch list has all 3 files before attempt
56
+ const beforeWatchCount = db.prepare(`
57
+ SELECT COUNT(*) as count FROM t_task_file_links WHERE task_id = ?
58
+ `).get(taskId);
59
+ assert.strictEqual(beforeWatchCount.count, 3, 'Watch list should have 3 files before pruning attempt');
60
+ // 6. Attempt transition - should NOT throw at top level
61
+ // The error should be caught internally and logged
62
+ // Task should remain in in_progress without transitioning
63
+ const transitioned = await detectAndTransitionToReview(db);
64
+ // 7. Verify zero tasks were transitioned
65
+ assert.strictEqual(transitioned, 0, 'No tasks should have been transitioned');
66
+ // 8. Verify task status unchanged (remains in in_progress)
67
+ const afterStatus = db.prepare(`
68
+ SELECT s.name
69
+ FROM t_tasks t
70
+ JOIN m_task_statuses s ON t.status_id = s.id
71
+ WHERE t.id = ?
72
+ `).get(taskId);
73
+ assert.strictEqual(afterStatus.name, 'in_progress', 'Task should remain in in_progress after safety check');
74
+ // 9. Verify NO audit records created (transaction rollback)
75
+ const prunedCount = db.prepare(`
76
+ SELECT COUNT(*) as count FROM t_task_pruned_files WHERE task_id = ?
77
+ `).get(taskId);
78
+ assert.strictEqual(prunedCount.count, 0, 'Should have no pruned file records due to rollback');
79
+ // 10. Verify watch list is NOT empty (transaction rolled back)
80
+ const afterWatchCount = db.prepare(`
81
+ SELECT COUNT(*) as count FROM t_task_file_links WHERE task_id = ?
82
+ `).get(taskId);
83
+ assert.strictEqual(afterWatchCount.count, 3, 'Watch list should still have all 3 files after rollback');
84
+ });
85
+ it('should prune SOME non-existent files and continue (partial prune)', async () => {
86
+ // Create task
87
+ const taskId = createTestTask(db);
88
+ // Add mix of existent and non-existent files
89
+ // Use package.json as a file that definitely exists in the project root
90
+ const mixedFiles = [
91
+ 'package.json', // exists
92
+ '/tmp/never-created-file-1.ts', // does not exist
93
+ '/tmp/never-created-file-2.ts' // does not exist
94
+ ];
95
+ addWatchedFiles(db, taskId, mixedFiles);
96
+ // Make task appear stale
97
+ const tenMinutesAgo = Math.floor(Date.now() / 1000) - (10 * 60);
98
+ db.prepare(`
99
+ UPDATE t_tasks
100
+ SET updated_ts = ?
101
+ WHERE id = ?
102
+ `).run(tenMinutesAgo, taskId);
103
+ // Verify initial watch count
104
+ const beforeWatchCount = db.prepare(`
105
+ SELECT COUNT(*) as count FROM t_task_file_links WHERE task_id = ?
106
+ `).get(taskId);
107
+ assert.strictEqual(beforeWatchCount.count, 3, 'Watch list should start with 3 files');
108
+ // Attempt transition - should proceed with partial pruning
109
+ await detectAndTransitionToReview(db);
110
+ // Verify some files were pruned
111
+ const prunedCount = db.prepare(`
112
+ SELECT COUNT(*) as count FROM t_task_pruned_files WHERE task_id = ?
113
+ `).get(taskId);
114
+ assert.strictEqual(prunedCount.count, 2, 'Should have 2 pruned file records (the non-existent ones)');
115
+ // Verify watch list now has only 1 file (the existing one)
116
+ const afterWatchCount = db.prepare(`
117
+ SELECT COUNT(*) as count FROM t_task_file_links WHERE task_id = ?
118
+ `).get(taskId);
119
+ assert.strictEqual(afterWatchCount.count, 1, 'Watch list should have 1 remaining file after partial prune');
120
+ // Verify remaining file is package.json
121
+ const remainingFile = db.prepare(`
122
+ SELECT f.path
123
+ FROM t_task_file_links tfl
124
+ JOIN m_files f ON tfl.file_id = f.id
125
+ WHERE tfl.task_id = ?
126
+ `).get(taskId);
127
+ assert.strictEqual(remainingFile.path, 'package.json', 'Remaining file should be package.json');
128
+ });
129
+ it('should handle task with no watched files gracefully', async () => {
130
+ // Create task without any watched files
131
+ const taskId = createTestTask(db);
132
+ // Make task appear stale
133
+ const tenMinutesAgo = Math.floor(Date.now() / 1000) - (10 * 60);
134
+ db.prepare(`
135
+ UPDATE t_tasks
136
+ SET updated_ts = ?
137
+ WHERE id = ?
138
+ `).run(tenMinutesAgo, taskId);
139
+ // Attempt transition - should skip this task
140
+ const transitioned = await detectAndTransitionToReview(db);
141
+ assert.strictEqual(transitioned, 0, 'Should not transition task with no watched files');
142
+ // Verify task status unchanged
143
+ const status = db.prepare(`
144
+ SELECT s.name
145
+ FROM t_tasks t
146
+ JOIN m_task_statuses s ON t.status_id = s.id
147
+ WHERE t.id = ?
148
+ `).get(taskId);
149
+ assert.strictEqual(status.name, 'in_progress', 'Task should remain in in_progress');
150
+ });
151
+ console.log('\n✅ All auto-pruning safety check tests passed!\n');
152
+ });
153
+ /**
154
+ * Helper: Create a test task in 'in_progress' status
155
+ */
156
+ function createTestTask(db) {
157
+ // Create test agent
158
+ const agentId = db.prepare(`
159
+ INSERT INTO m_agents (name) VALUES (?) RETURNING id
160
+ `).get('test-agent');
161
+ // Get 'in_progress' status ID
162
+ const statusId = db.prepare(`
163
+ SELECT id FROM m_task_statuses WHERE name = 'in_progress'
164
+ `).get();
165
+ // Create task with updated_ts set to now (will be backdated in tests)
166
+ const taskId = db.prepare(`
167
+ INSERT INTO t_tasks (
168
+ title,
169
+ status_id,
170
+ priority,
171
+ assigned_agent_id,
172
+ created_by_agent_id,
173
+ created_ts,
174
+ updated_ts
175
+ )
176
+ VALUES (?, ?, 2, ?, ?, unixepoch(), unixepoch())
177
+ RETURNING id
178
+ `).get('Test task for auto-pruning', statusId.id, agentId.id, agentId.id);
179
+ return taskId.id;
180
+ }
181
+ /**
182
+ * Helper: Add watched files to a task
183
+ */
184
+ function addWatchedFiles(db, taskId, filePaths) {
185
+ const insertFile = db.prepare(`
186
+ INSERT OR IGNORE INTO m_files (path) VALUES (?) RETURNING id
187
+ `);
188
+ const linkFile = db.prepare(`
189
+ INSERT INTO t_task_file_links (task_id, file_id) VALUES (?, ?)
190
+ `);
191
+ for (const filePath of filePaths) {
192
+ const fileResult = insertFile.get(filePath);
193
+ linkFile.run(taskId, fileResult.id);
194
+ }
195
+ }
196
+ //# sourceMappingURL=tasks.auto-pruning-safety.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.auto-pruning-safety.test.js","sourceRoot":"","sources":["../../src/tests/tasks.auto-pruning-safety.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Unit tests for backward compatibility of deprecated task.link(link_type="file") (v3.4.1)
3
+ * Tests that the deprecated API still works while showing deprecation warnings
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=tasks.link-file-backward-compat.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.link-file-backward-compat.test.d.ts","sourceRoot":"","sources":["../../src/tests/tasks.link-file-backward-compat.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}