sqlew 2.1.4 → 3.1.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 +891 -605
- package/LICENSE +49 -18
- package/README.md +337 -690
- package/assets/kanban-style.png +0 -0
- package/assets/schema.sql +532 -402
- package/dist/database.d.ts +9 -0
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +33 -34
- package/dist/database.js.map +1 -1
- package/dist/index.js +1050 -215
- package/dist/index.js.map +1 -1
- package/dist/migrations/add-task-tables.d.ts +47 -0
- package/dist/migrations/add-task-tables.d.ts.map +1 -0
- package/dist/migrations/add-task-tables.js +285 -0
- package/dist/migrations/add-task-tables.js.map +1 -0
- package/dist/migrations/index.d.ts +96 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +239 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/migrate-decisions-to-tasks.d.ts +61 -0
- package/dist/migrations/migrate-decisions-to-tasks.d.ts.map +1 -0
- package/dist/migrations/migrate-decisions-to-tasks.js +442 -0
- package/dist/migrations/migrate-decisions-to-tasks.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +14 -3
- package/dist/schema.js.map +1 -1
- package/dist/tools/constraints.d.ts +4 -0
- package/dist/tools/constraints.d.ts.map +1 -1
- package/dist/tools/constraints.js +6 -27
- package/dist/tools/constraints.js.map +1 -1
- package/dist/tools/context.d.ts +17 -1
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +195 -190
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/files.d.ts.map +1 -1
- package/dist/tools/files.js +113 -166
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/messaging.d.ts +2 -9
- package/dist/tools/messaging.d.ts.map +1 -1
- package/dist/tools/messaging.js +67 -126
- package/dist/tools/messaging.js.map +1 -1
- package/dist/tools/tasks.d.ts +90 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +844 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/utils.d.ts +8 -1
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +50 -21
- package/dist/tools/utils.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/batch.d.ts +69 -0
- package/dist/utils/batch.d.ts.map +1 -0
- package/dist/utils/batch.js +148 -0
- package/dist/utils/batch.js.map +1 -0
- package/dist/utils/query-builder.d.ts +68 -0
- package/dist/utils/query-builder.d.ts.map +1 -0
- package/dist/utils/query-builder.js +116 -0
- package/dist/utils/query-builder.js.map +1 -0
- package/dist/utils/task-stale-detection.d.ts +28 -0
- package/dist/utils/task-stale-detection.d.ts.map +1 -0
- package/dist/utils/task-stale-detection.js +92 -0
- package/dist/utils/task-stale-detection.js.map +1 -0
- package/dist/utils/validators.d.ts +57 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +117 -0
- package/dist/utils/validators.js.map +1 -0
- package/dist/watcher/file-watcher.d.ts +75 -0
- package/dist/watcher/file-watcher.d.ts.map +1 -0
- package/dist/watcher/file-watcher.js +374 -0
- package/dist/watcher/file-watcher.js.map +1 -0
- package/dist/watcher/index.d.ts +8 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +7 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/watcher/test-executor.d.ts +23 -0
- package/dist/watcher/test-executor.d.ts.map +1 -0
- package/dist/watcher/test-executor.js +226 -0
- package/dist/watcher/test-executor.js.map +1 -0
- package/docs/ACCEPTANCE_CRITERIA.md +625 -0
- package/docs/AI_AGENT_GUIDE.md +1471 -648
- package/{ARCHITECTURE.md → docs/ARCHITECTURE.md} +636 -636
- package/docs/AUTO_FILE_TRACKING.md +436 -0
- package/docs/BEST_PRACTICES.md +481 -0
- package/docs/DECISION_TO_TASK_MIGRATION_GUIDE.md +457 -0
- package/docs/MIGRATION_CHAIN.md +280 -0
- package/{MIGRATION_v2.md → docs/MIGRATION_v2.md} +538 -538
- package/docs/SHARED_CONCEPTS.md +339 -0
- package/docs/TASK_ACTIONS.md +854 -0
- package/docs/TASK_LINKING.md +729 -0
- package/docs/TASK_MIGRATION.md +701 -0
- package/docs/TASK_OVERVIEW.md +363 -0
- package/docs/TASK_SYSTEM.md +1244 -0
- package/docs/TOOL_REFERENCE.md +471 -0
- package/docs/TOOL_SELECTION.md +279 -0
- package/docs/WORKFLOWS.md +602 -0
- package/package.json +65 -64
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher - Auto-tracking file changes linked to tasks
|
|
3
|
+
* Monitors files and auto-transitions task status on file modification
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - chokidar-based file watching with debouncing
|
|
7
|
+
* - Dynamic file registration (add/remove files at runtime)
|
|
8
|
+
* - Auto-transition: todo → in_progress on file change
|
|
9
|
+
* - Maps file paths → task IDs for efficient lookup
|
|
10
|
+
*/
|
|
11
|
+
import chokidar from 'chokidar';
|
|
12
|
+
import { getDatabase } from '../database.js';
|
|
13
|
+
import { basename, dirname } from 'path';
|
|
14
|
+
import { existsSync } from 'fs';
|
|
15
|
+
import { executeAcceptanceCriteria } from './test-executor.js';
|
|
16
|
+
/**
|
|
17
|
+
* FileWatcher class - Singleton pattern
|
|
18
|
+
*/
|
|
19
|
+
export class FileWatcher {
|
|
20
|
+
static instance = null;
|
|
21
|
+
watcher = null;
|
|
22
|
+
watchedFiles = new Map();
|
|
23
|
+
isRunning = false;
|
|
24
|
+
debounceTimers = new Map();
|
|
25
|
+
DEBOUNCE_MS = 2000; // Wait 2s after last write
|
|
26
|
+
constructor() {
|
|
27
|
+
// Private constructor for singleton
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get singleton instance
|
|
31
|
+
*/
|
|
32
|
+
static getInstance() {
|
|
33
|
+
if (!FileWatcher.instance) {
|
|
34
|
+
FileWatcher.instance = new FileWatcher();
|
|
35
|
+
}
|
|
36
|
+
return FileWatcher.instance;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Initialize and start the file watcher
|
|
40
|
+
*/
|
|
41
|
+
async start() {
|
|
42
|
+
if (this.isRunning) {
|
|
43
|
+
console.error('⚠ File watcher already running');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Initialize chokidar with debouncing
|
|
48
|
+
this.watcher = chokidar.watch([], {
|
|
49
|
+
persistent: true,
|
|
50
|
+
ignoreInitial: true, // Don't trigger on startup
|
|
51
|
+
awaitWriteFinish: {
|
|
52
|
+
stabilityThreshold: this.DEBOUNCE_MS,
|
|
53
|
+
pollInterval: 100
|
|
54
|
+
},
|
|
55
|
+
ignored: /(^|[\/\\])\../, // Ignore dotfiles
|
|
56
|
+
});
|
|
57
|
+
// Set up event handlers
|
|
58
|
+
this.watcher.on('change', (path) => {
|
|
59
|
+
this.handleFileChange(path);
|
|
60
|
+
});
|
|
61
|
+
this.watcher.on('add', (path) => {
|
|
62
|
+
this.handleFileChange(path);
|
|
63
|
+
});
|
|
64
|
+
this.watcher.on('error', (error) => {
|
|
65
|
+
console.error('File watcher error:', error);
|
|
66
|
+
});
|
|
67
|
+
// Load existing task-file links from database
|
|
68
|
+
await this.loadTaskFileLinks();
|
|
69
|
+
this.isRunning = true;
|
|
70
|
+
console.error('✓ File watcher started successfully');
|
|
71
|
+
console.error(` Watching ${this.watchedFiles.size} files for ${this.getTotalTaskCount()} tasks`);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('Failed to start file watcher:', error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stop the file watcher
|
|
80
|
+
*/
|
|
81
|
+
async stop() {
|
|
82
|
+
if (!this.isRunning) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
// Clear all debounce timers
|
|
87
|
+
this.debounceTimers.forEach(timer => clearTimeout(timer));
|
|
88
|
+
this.debounceTimers.clear();
|
|
89
|
+
// Close watcher
|
|
90
|
+
if (this.watcher) {
|
|
91
|
+
await this.watcher.close();
|
|
92
|
+
this.watcher = null;
|
|
93
|
+
}
|
|
94
|
+
this.isRunning = false;
|
|
95
|
+
console.error('✓ File watcher stopped');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error('Error stopping file watcher:', error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Register a file to watch for a specific task
|
|
104
|
+
*/
|
|
105
|
+
registerFile(filePath, taskId, taskTitle, currentStatus) {
|
|
106
|
+
if (!this.watcher) {
|
|
107
|
+
console.error('Cannot register file: watcher not initialized');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Normalize path
|
|
111
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
112
|
+
// Add to watched files map
|
|
113
|
+
if (!this.watchedFiles.has(normalizedPath)) {
|
|
114
|
+
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
|
+
}
|
|
128
|
+
const mappings = this.watchedFiles.get(normalizedPath);
|
|
129
|
+
// Check if task already registered for this file
|
|
130
|
+
const existing = mappings.find(m => m.taskId === taskId);
|
|
131
|
+
if (existing) {
|
|
132
|
+
// Update status
|
|
133
|
+
existing.currentStatus = currentStatus;
|
|
134
|
+
existing.taskTitle = taskTitle;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Add new mapping
|
|
138
|
+
mappings.push({ taskId, taskTitle, currentStatus });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Unregister a file from watching (when task completes or is archived)
|
|
143
|
+
*/
|
|
144
|
+
unregisterFile(filePath, taskId) {
|
|
145
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
146
|
+
const mappings = this.watchedFiles.get(normalizedPath);
|
|
147
|
+
if (!mappings) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Remove this task from the file's mappings
|
|
151
|
+
const filtered = mappings.filter(m => m.taskId !== taskId);
|
|
152
|
+
if (filtered.length === 0) {
|
|
153
|
+
// No more tasks watching this file
|
|
154
|
+
this.watchedFiles.delete(normalizedPath);
|
|
155
|
+
if (this.watcher) {
|
|
156
|
+
this.watcher.unwatch(normalizedPath);
|
|
157
|
+
}
|
|
158
|
+
console.error(` Stopped watching: ${basename(normalizedPath)}`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.watchedFiles.set(normalizedPath, filtered);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Unregister all files for a specific task
|
|
166
|
+
*/
|
|
167
|
+
unregisterTask(taskId) {
|
|
168
|
+
const filesToUnregister = [];
|
|
169
|
+
// Find all files linked to this task
|
|
170
|
+
this.watchedFiles.forEach((mappings, filePath) => {
|
|
171
|
+
if (mappings.some(m => m.taskId === taskId)) {
|
|
172
|
+
filesToUnregister.push(filePath);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
// Unregister each file
|
|
176
|
+
filesToUnregister.forEach(filePath => {
|
|
177
|
+
this.unregisterFile(filePath, taskId);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Handle file change event
|
|
182
|
+
*/
|
|
183
|
+
async handleFileChange(filePath) {
|
|
184
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
185
|
+
const mappings = this.watchedFiles.get(normalizedPath);
|
|
186
|
+
if (!mappings || mappings.length === 0) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
console.error(`\n📝 File changed: ${basename(normalizedPath)}`);
|
|
190
|
+
const db = getDatabase();
|
|
191
|
+
// Process each task linked to this file
|
|
192
|
+
for (const mapping of mappings) {
|
|
193
|
+
const { taskId, taskTitle, currentStatus } = mapping;
|
|
194
|
+
// Auto-transition: todo → in_progress
|
|
195
|
+
if (currentStatus === 'todo') {
|
|
196
|
+
try {
|
|
197
|
+
// Get status IDs
|
|
198
|
+
const todoStatusId = db.prepare('SELECT id FROM m_task_statuses WHERE name = ?').get('todo');
|
|
199
|
+
const inProgressStatusId = db.prepare('SELECT id FROM m_task_statuses WHERE name = ?').get('in_progress');
|
|
200
|
+
if (!todoStatusId || !inProgressStatusId) {
|
|
201
|
+
console.error('Cannot find task status IDs');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
// Update task status: todo → in_progress
|
|
205
|
+
db.prepare(`
|
|
206
|
+
UPDATE t_tasks
|
|
207
|
+
SET status_id = ?, updated_ts = unixepoch()
|
|
208
|
+
WHERE id = ? AND status_id = ?
|
|
209
|
+
`).run(inProgressStatusId.id, taskId, todoStatusId.id);
|
|
210
|
+
// Update in-memory status
|
|
211
|
+
mapping.currentStatus = 'in_progress';
|
|
212
|
+
console.error(` ✓ Task #${taskId} "${taskTitle}": todo → in_progress`);
|
|
213
|
+
// Log to activity log
|
|
214
|
+
const agentId = db.prepare('SELECT assigned_agent_id FROM t_tasks WHERE id = ?').get(taskId);
|
|
215
|
+
if (agentId?.assigned_agent_id) {
|
|
216
|
+
db.prepare(`
|
|
217
|
+
INSERT INTO t_activity_log (agent_id, action_type, target, details)
|
|
218
|
+
VALUES (?, ?, ?, ?)
|
|
219
|
+
`).run(agentId.assigned_agent_id, 'task_auto_transition', `task_id:${taskId}`, JSON.stringify({
|
|
220
|
+
from_status: 'todo',
|
|
221
|
+
to_status: 'in_progress',
|
|
222
|
+
trigger: 'file_change',
|
|
223
|
+
file_path: normalizedPath
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
console.error(`Error auto-transitioning task #${taskId}:`, error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Check acceptance criteria for in_progress tasks
|
|
232
|
+
if (currentStatus === 'in_progress' || mapping.currentStatus === 'in_progress') {
|
|
233
|
+
await this.checkAcceptanceCriteria(taskId, taskTitle, mapping);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
console.error(` • Task #${taskId} "${taskTitle}": status ${currentStatus}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Check acceptance criteria and auto-complete task if all pass
|
|
242
|
+
*/
|
|
243
|
+
async checkAcceptanceCriteria(taskId, taskTitle, mapping) {
|
|
244
|
+
const db = getDatabase();
|
|
245
|
+
try {
|
|
246
|
+
// Get acceptance criteria JSON
|
|
247
|
+
const taskDetails = db.prepare(`
|
|
248
|
+
SELECT acceptance_criteria_json
|
|
249
|
+
FROM t_task_details
|
|
250
|
+
WHERE task_id = ?
|
|
251
|
+
`).get(taskId);
|
|
252
|
+
if (!taskDetails || !taskDetails.acceptance_criteria_json) {
|
|
253
|
+
// No acceptance criteria defined, skip auto-completion
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const checks = JSON.parse(taskDetails.acceptance_criteria_json);
|
|
257
|
+
if (!Array.isArray(checks) || checks.length === 0) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
console.error(` 🔍 Checking acceptance criteria for task #${taskId}...`);
|
|
261
|
+
// Execute all checks
|
|
262
|
+
const { allPassed, results } = await executeAcceptanceCriteria(checks);
|
|
263
|
+
// Log individual check results
|
|
264
|
+
results.forEach((result, index) => {
|
|
265
|
+
const icon = result.success ? '✓' : '✗';
|
|
266
|
+
console.error(` ${icon} Check ${index + 1}: ${result.message}`);
|
|
267
|
+
if (result.details) {
|
|
268
|
+
console.error(` Details: ${result.details}`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
if (allPassed) {
|
|
272
|
+
// All checks passed - auto-complete task: in_progress → done
|
|
273
|
+
const inProgressStatusId = db.prepare('SELECT id FROM m_task_statuses WHERE name = ?').get('in_progress');
|
|
274
|
+
const doneStatusId = db.prepare('SELECT id FROM m_task_statuses WHERE name = ?').get('done');
|
|
275
|
+
if (!inProgressStatusId || !doneStatusId) {
|
|
276
|
+
console.error('Cannot find task status IDs');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
db.prepare(`
|
|
280
|
+
UPDATE t_tasks
|
|
281
|
+
SET status_id = ?, completed_ts = unixepoch(), updated_ts = unixepoch()
|
|
282
|
+
WHERE id = ? AND status_id = ?
|
|
283
|
+
`).run(doneStatusId.id, taskId, inProgressStatusId.id);
|
|
284
|
+
// Update in-memory status
|
|
285
|
+
mapping.currentStatus = 'done';
|
|
286
|
+
console.error(` 🎉 Task #${taskId} "${taskTitle}": in_progress → done (all checks passed!)`);
|
|
287
|
+
// Unregister from watcher (done tasks don't need watching)
|
|
288
|
+
this.unregisterTask(taskId);
|
|
289
|
+
// Log to activity log
|
|
290
|
+
const agentId = db.prepare('SELECT assigned_agent_id FROM t_tasks WHERE id = ?').get(taskId);
|
|
291
|
+
if (agentId?.assigned_agent_id) {
|
|
292
|
+
db.prepare(`
|
|
293
|
+
INSERT INTO t_activity_log (agent_id, action_type, target, details)
|
|
294
|
+
VALUES (?, ?, ?, ?)
|
|
295
|
+
`).run(agentId.assigned_agent_id, 'task_auto_complete', `task_id:${taskId}`, JSON.stringify({
|
|
296
|
+
from_status: 'in_progress',
|
|
297
|
+
to_status: 'done',
|
|
298
|
+
trigger: 'acceptance_criteria_passed',
|
|
299
|
+
checks_passed: results.length
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
const failedCount = results.filter(r => !r.success).length;
|
|
305
|
+
console.error(` ⏳ Task #${taskId}: ${failedCount}/${results.length} checks failed, staying in_progress`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
console.error(`Error checking acceptance criteria for task #${taskId}:`, error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Load existing task-file links from database
|
|
314
|
+
*/
|
|
315
|
+
async loadTaskFileLinks() {
|
|
316
|
+
const db = getDatabase();
|
|
317
|
+
try {
|
|
318
|
+
// Query all active tasks with file links
|
|
319
|
+
const query = `
|
|
320
|
+
SELECT
|
|
321
|
+
t.id as task_id,
|
|
322
|
+
t.title as task_title,
|
|
323
|
+
s.name as status,
|
|
324
|
+
f.path as file_path
|
|
325
|
+
FROM t_tasks t
|
|
326
|
+
JOIN m_task_statuses s ON t.status_id = s.id
|
|
327
|
+
JOIN t_task_file_links tfl ON t.id = tfl.task_id
|
|
328
|
+
JOIN m_files f ON tfl.file_id = f.id
|
|
329
|
+
WHERE s.name IN ('todo', 'in_progress', 'waiting_review', 'blocked')
|
|
330
|
+
`;
|
|
331
|
+
const links = db.prepare(query).all();
|
|
332
|
+
// Register each file
|
|
333
|
+
links.forEach(link => {
|
|
334
|
+
this.registerFile(link.file_path, link.task_id, link.task_title, link.status);
|
|
335
|
+
});
|
|
336
|
+
console.error(` Loaded ${links.length} task-file links from database`);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
console.error('Error loading task-file links:', error);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Normalize file path (resolve relative paths, remove trailing slashes)
|
|
345
|
+
*/
|
|
346
|
+
normalizePath(filePath) {
|
|
347
|
+
// Remove trailing slashes
|
|
348
|
+
let normalized = filePath.replace(/[\/\\]+$/, '');
|
|
349
|
+
// Convert backslashes to forward slashes (Windows compatibility)
|
|
350
|
+
normalized = normalized.replace(/\\/g, '/');
|
|
351
|
+
return normalized;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get total count of tasks being watched
|
|
355
|
+
*/
|
|
356
|
+
getTotalTaskCount() {
|
|
357
|
+
const taskIds = new Set();
|
|
358
|
+
this.watchedFiles.forEach(mappings => {
|
|
359
|
+
mappings.forEach(m => taskIds.add(m.taskId));
|
|
360
|
+
});
|
|
361
|
+
return taskIds.size;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Get current watcher status
|
|
365
|
+
*/
|
|
366
|
+
getStatus() {
|
|
367
|
+
return {
|
|
368
|
+
running: this.isRunning,
|
|
369
|
+
filesWatched: this.watchedFiles.size,
|
|
370
|
+
tasksWatched: this.getTotalTaskCount()
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=file-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watcher module exports
|
|
3
|
+
* Provides file watching and test execution functionality
|
|
4
|
+
*/
|
|
5
|
+
export { FileWatcher } from './file-watcher.js';
|
|
6
|
+
export { executeAcceptanceCriteria } from './test-executor.js';
|
|
7
|
+
export type { CheckResult } from './test-executor.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Executor - Runs acceptance criteria checks for tasks
|
|
3
|
+
* Supports multiple check types: tests_pass, code_removed, code_contains, file_exists
|
|
4
|
+
*/
|
|
5
|
+
import { AcceptanceCheck } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Result of executing an acceptance check
|
|
8
|
+
*/
|
|
9
|
+
export interface CheckResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
details?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Execute all acceptance checks for a task
|
|
16
|
+
* @param checks - Array of acceptance check definitions
|
|
17
|
+
* @returns Object with overall success status and individual results
|
|
18
|
+
*/
|
|
19
|
+
export declare function executeAcceptanceCriteria(checks: AcceptanceCheck[]): Promise<{
|
|
20
|
+
allPassed: boolean;
|
|
21
|
+
results: CheckResult[];
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=test-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-executor.d.ts","sourceRoot":"","sources":["../../src/watcher/test-executor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI9C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,eAAe,EAAE,GACxB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CAuCzD"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Executor - Runs acceptance criteria checks for tasks
|
|
3
|
+
* Supports multiple check types: tests_pass, code_removed, code_contains, file_exists
|
|
4
|
+
*/
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import { readFileSync, existsSync } from 'fs';
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Execute all acceptance checks for a task
|
|
11
|
+
* @param checks - Array of acceptance check definitions
|
|
12
|
+
* @returns Object with overall success status and individual results
|
|
13
|
+
*/
|
|
14
|
+
export async function executeAcceptanceCriteria(checks) {
|
|
15
|
+
const results = [];
|
|
16
|
+
for (const check of checks) {
|
|
17
|
+
let result;
|
|
18
|
+
try {
|
|
19
|
+
switch (check.type) {
|
|
20
|
+
case 'tests_pass':
|
|
21
|
+
result = await executeTestsPass(check);
|
|
22
|
+
break;
|
|
23
|
+
case 'code_removed':
|
|
24
|
+
result = executeCodeRemoved(check);
|
|
25
|
+
break;
|
|
26
|
+
case 'code_contains':
|
|
27
|
+
result = executeCodeContains(check);
|
|
28
|
+
break;
|
|
29
|
+
case 'file_exists':
|
|
30
|
+
result = executeFileExists(check);
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
result = {
|
|
34
|
+
success: false,
|
|
35
|
+
message: `Unknown check type: ${check.type}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
result = {
|
|
41
|
+
success: false,
|
|
42
|
+
message: `Error executing check: ${error instanceof Error ? error.message : String(error)}`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
results.push(result);
|
|
46
|
+
}
|
|
47
|
+
const allPassed = results.every(r => r.success);
|
|
48
|
+
return { allPassed, results };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Execute a shell command and check if it passes
|
|
52
|
+
* Check type: tests_pass
|
|
53
|
+
*/
|
|
54
|
+
async function executeTestsPass(check) {
|
|
55
|
+
if (!check.command) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
message: 'tests_pass check requires "command" parameter'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const timeout = check.timeout || 60; // Default 60s timeout
|
|
62
|
+
try {
|
|
63
|
+
const { stdout, stderr } = await execAsync(check.command, {
|
|
64
|
+
timeout: timeout * 1000,
|
|
65
|
+
maxBuffer: 1024 * 1024 * 10 // 10MB buffer
|
|
66
|
+
});
|
|
67
|
+
const output = stdout + stderr;
|
|
68
|
+
// If expected_pattern specified, check for it in output
|
|
69
|
+
if (check.expected_pattern) {
|
|
70
|
+
const regex = new RegExp(check.expected_pattern);
|
|
71
|
+
if (regex.test(output)) {
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
message: `Command succeeded and output matches pattern "${check.expected_pattern}"`,
|
|
75
|
+
details: output.slice(0, 500) // First 500 chars
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
message: `Command succeeded but output does not match pattern "${check.expected_pattern}"`,
|
|
82
|
+
details: output.slice(0, 500)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// No pattern specified, success if command exits with code 0
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
message: `Command "${check.command}" executed successfully`,
|
|
90
|
+
details: output.slice(0, 500)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
// Command failed or timed out
|
|
95
|
+
const output = (error.stdout || '') + (error.stderr || '');
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
message: error.killed
|
|
99
|
+
? `Command "${check.command}" timed out after ${timeout}s`
|
|
100
|
+
: `Command "${check.command}" failed with exit code ${error.code || 'unknown'}`,
|
|
101
|
+
details: output.slice(0, 500)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if code pattern has been removed from file
|
|
107
|
+
* Check type: code_removed
|
|
108
|
+
*/
|
|
109
|
+
function executeCodeRemoved(check) {
|
|
110
|
+
if (!check.file) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
message: 'code_removed check requires "file" parameter'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (!check.pattern) {
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
message: 'code_removed check requires "pattern" parameter'
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (!existsSync(check.file)) {
|
|
123
|
+
// File doesn't exist - pattern is definitely removed!
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
message: `File "${check.file}" does not exist (pattern removed)`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const content = readFileSync(check.file, 'utf-8');
|
|
131
|
+
const regex = new RegExp(check.pattern);
|
|
132
|
+
if (!regex.test(content)) {
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
message: `Pattern "${check.pattern}" not found in "${check.file}" (removed)`
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const match = content.match(regex);
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
message: `Pattern "${check.pattern}" still exists in "${check.file}"`,
|
|
143
|
+
details: match ? `Found: ${match[0].slice(0, 100)}` : undefined
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
message: `Error reading file "${check.file}": ${error instanceof Error ? error.message : String(error)}`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if code pattern exists in file
|
|
156
|
+
* Check type: code_contains
|
|
157
|
+
*/
|
|
158
|
+
function executeCodeContains(check) {
|
|
159
|
+
if (!check.file) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
message: 'code_contains check requires "file" parameter'
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (!check.pattern) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
message: 'code_contains check requires "pattern" parameter'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (!existsSync(check.file)) {
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
message: `File "${check.file}" does not exist`
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const content = readFileSync(check.file, 'utf-8');
|
|
179
|
+
const regex = new RegExp(check.pattern);
|
|
180
|
+
if (regex.test(content)) {
|
|
181
|
+
const match = content.match(regex);
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
message: `Pattern "${check.pattern}" found in "${check.file}"`,
|
|
185
|
+
details: match ? `Match: ${match[0].slice(0, 100)}` : undefined
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
message: `Pattern "${check.pattern}" not found in "${check.file}"`
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
message: `Error reading file "${check.file}": ${error instanceof Error ? error.message : String(error)}`
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if file exists
|
|
204
|
+
* Check type: file_exists
|
|
205
|
+
*/
|
|
206
|
+
function executeFileExists(check) {
|
|
207
|
+
if (!check.file) {
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
message: 'file_exists check requires "file" parameter'
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (existsSync(check.file)) {
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
message: `File "${check.file}" exists`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
message: `File "${check.file}" does not exist`
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=test-executor.js.map
|