@compilr-dev/cli 0.4.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/README.md +110 -0
- package/dist/agent.d.ts +62 -0
- package/dist/agent.js +317 -0
- package/dist/agents/registry.d.ts +66 -0
- package/dist/agents/registry.js +238 -0
- package/dist/agents/types.d.ts +40 -0
- package/dist/agents/types.js +94 -0
- package/dist/commands/custom-registry.d.ts +69 -0
- package/dist/commands/custom-registry.js +246 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/types.d.ts +31 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands.d.ts +63 -0
- package/dist/commands.js +324 -0
- package/dist/db/index.d.ts +42 -0
- package/dist/db/index.js +146 -0
- package/dist/db/repositories/document-repository.d.ts +63 -0
- package/dist/db/repositories/document-repository.js +184 -0
- package/dist/db/repositories/index.d.ts +9 -0
- package/dist/db/repositories/index.js +6 -0
- package/dist/db/repositories/project-repository.d.ts +132 -0
- package/dist/db/repositories/project-repository.js +337 -0
- package/dist/db/repositories/work-item-repository.d.ts +115 -0
- package/dist/db/repositories/work-item-repository.js +389 -0
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +143 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +348 -0
- package/dist/index.old.d.ts +7 -0
- package/dist/index.old.js +1014 -0
- package/dist/repl.d.ts +121 -0
- package/dist/repl.js +1878 -0
- package/dist/settings/index.d.ts +80 -0
- package/dist/settings/index.js +195 -0
- package/dist/shared-handlers.d.ts +63 -0
- package/dist/shared-handlers.js +57 -0
- package/dist/slash-autocomplete.d.ts +41 -0
- package/dist/slash-autocomplete.js +638 -0
- package/dist/state.d.ts +75 -0
- package/dist/state.js +130 -0
- package/dist/tabbed-menu.d.ts +11 -0
- package/dist/tabbed-menu.js +328 -0
- package/dist/templates/backlog-md.d.ts +7 -0
- package/dist/templates/backlog-md.js +94 -0
- package/dist/templates/claude-md.d.ts +7 -0
- package/dist/templates/claude-md.js +189 -0
- package/dist/templates/coding-standards.d.ts +7 -0
- package/dist/templates/coding-standards.js +299 -0
- package/dist/templates/compilr-md.d.ts +7 -0
- package/dist/templates/compilr-md.js +189 -0
- package/dist/templates/config-json.d.ts +38 -0
- package/dist/templates/config-json.js +39 -0
- package/dist/templates/gitignore.d.ts +7 -0
- package/dist/templates/gitignore.js +85 -0
- package/dist/templates/index.d.ts +19 -0
- package/dist/templates/index.js +302 -0
- package/dist/templates/package-json.d.ts +7 -0
- package/dist/templates/package-json.js +111 -0
- package/dist/templates/readme-md.d.ts +7 -0
- package/dist/templates/readme-md.js +161 -0
- package/dist/templates/tsconfig.d.ts +7 -0
- package/dist/templates/tsconfig.js +61 -0
- package/dist/templates/types.d.ts +33 -0
- package/dist/templates/types.js +24 -0
- package/dist/test-autocomplete.d.ts +7 -0
- package/dist/test-autocomplete.js +85 -0
- package/dist/test-tabbed-menu.d.ts +7 -0
- package/dist/test-tabbed-menu.js +25 -0
- package/dist/themes/colors.d.ts +49 -0
- package/dist/themes/colors.js +135 -0
- package/dist/themes/index.d.ts +23 -0
- package/dist/themes/index.js +24 -0
- package/dist/themes/registry.d.ts +60 -0
- package/dist/themes/registry.js +195 -0
- package/dist/themes/types.d.ts +82 -0
- package/dist/themes/types.js +7 -0
- package/dist/tool-selector.d.ts +71 -0
- package/dist/tool-selector.js +184 -0
- package/dist/tools/ask-user-simple.d.ts +19 -0
- package/dist/tools/ask-user-simple.js +86 -0
- package/dist/tools/ask-user.d.ts +32 -0
- package/dist/tools/ask-user.js +113 -0
- package/dist/tools/backlog.d.ts +53 -0
- package/dist/tools/backlog.js +709 -0
- package/dist/tools.d.ts +15 -0
- package/dist/tools.js +121 -0
- package/dist/ui/agents-overlay.d.ts +12 -0
- package/dist/ui/agents-overlay.js +501 -0
- package/dist/ui/arch-type-overlay.d.ts +20 -0
- package/dist/ui/arch-type-overlay.js +229 -0
- package/dist/ui/ask-user-overlay.d.ts +26 -0
- package/dist/ui/ask-user-overlay.js +647 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay.js +242 -0
- package/dist/ui/backlog-overlay.d.ts +17 -0
- package/dist/ui/backlog-overlay.js +786 -0
- package/dist/ui/commands-overlay.d.ts +11 -0
- package/dist/ui/commands-overlay.js +410 -0
- package/dist/ui/config-overlay.d.ts +34 -0
- package/dist/ui/config-overlay.js +977 -0
- package/dist/ui/conversation.d.ts +82 -0
- package/dist/ui/conversation.js +508 -0
- package/dist/ui/diff.d.ts +38 -0
- package/dist/ui/diff.js +182 -0
- package/dist/ui/ephemeral.d.ts +111 -0
- package/dist/ui/ephemeral.js +413 -0
- package/dist/ui/file-autocomplete.d.ts +45 -0
- package/dist/ui/file-autocomplete.js +237 -0
- package/dist/ui/footer.d.ts +153 -0
- package/dist/ui/footer.js +422 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/init-overlay.d.ts +24 -0
- package/dist/ui/init-overlay.js +525 -0
- package/dist/ui/input-prompt-v2.d.ts +179 -0
- package/dist/ui/input-prompt-v2.js +991 -0
- package/dist/ui/input-prompt.d.ts +97 -0
- package/dist/ui/input-prompt.js +800 -0
- package/dist/ui/iteration-limit-overlay.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay.js +150 -0
- package/dist/ui/keys-overlay.d.ts +14 -0
- package/dist/ui/keys-overlay.js +181 -0
- package/dist/ui/model-warning-overlay.d.ts +30 -0
- package/dist/ui/model-warning-overlay.js +171 -0
- package/dist/ui/overlay-controller.d.ts +25 -0
- package/dist/ui/overlay-controller.js +35 -0
- package/dist/ui/overlays.d.ts +47 -0
- package/dist/ui/overlays.js +627 -0
- package/dist/ui/permission-overlay.d.ts +16 -0
- package/dist/ui/permission-overlay.js +494 -0
- package/dist/ui/terminal.d.ts +117 -0
- package/dist/ui/terminal.js +237 -0
- package/dist/ui/todo-zone.d.ts +112 -0
- package/dist/ui/todo-zone.js +353 -0
- package/dist/ui/tools-overlay.d.ts +26 -0
- package/dist/ui/tools-overlay.js +278 -0
- package/dist/ui/tutorial-overlay.d.ts +10 -0
- package/dist/ui/tutorial-overlay.js +936 -0
- package/dist/ui/types.d.ts +103 -0
- package/dist/ui/types.js +33 -0
- package/dist/utils/credentials.d.ts +55 -0
- package/dist/utils/credentials.js +268 -0
- package/dist/utils/model-tiers.d.ts +37 -0
- package/dist/utils/model-tiers.js +118 -0
- package/dist/utils/project-memory.d.ts +47 -0
- package/dist/utils/project-memory.js +117 -0
- package/dist/utils/project-status.d.ts +56 -0
- package/dist/utils/project-status.js +237 -0
- package/package.json +66 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Work Item Repository
|
|
3
|
+
*
|
|
4
|
+
* Handles all database operations for work items (backlog items, tasks, bugs).
|
|
5
|
+
*/
|
|
6
|
+
import { getDb } from '../index.js';
|
|
7
|
+
/**
|
|
8
|
+
* Convert database record to WorkItem object
|
|
9
|
+
*/
|
|
10
|
+
function recordToWorkItem(record) {
|
|
11
|
+
return {
|
|
12
|
+
id: record.id,
|
|
13
|
+
projectId: record.project_id,
|
|
14
|
+
itemNumber: record.item_number,
|
|
15
|
+
itemId: record.item_id,
|
|
16
|
+
type: record.type,
|
|
17
|
+
status: record.status,
|
|
18
|
+
priority: record.priority,
|
|
19
|
+
guidedStep: record.guided_step,
|
|
20
|
+
title: record.title,
|
|
21
|
+
description: record.description,
|
|
22
|
+
estimatedEffort: record.estimated_effort,
|
|
23
|
+
actualMinutes: record.actual_minutes,
|
|
24
|
+
completedAt: record.completed_at ? new Date(record.completed_at) : null,
|
|
25
|
+
completedBy: record.completed_by,
|
|
26
|
+
commitHash: record.commit_hash,
|
|
27
|
+
createdAt: new Date(record.created_at),
|
|
28
|
+
updatedAt: new Date(record.updated_at),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get prefix for item ID based on type
|
|
33
|
+
*/
|
|
34
|
+
function getTypePrefix(type) {
|
|
35
|
+
const prefixes = {
|
|
36
|
+
feature: 'REQ',
|
|
37
|
+
bug: 'BUG',
|
|
38
|
+
'tech-debt': 'TEC',
|
|
39
|
+
chore: 'CHR',
|
|
40
|
+
};
|
|
41
|
+
return prefixes[type];
|
|
42
|
+
}
|
|
43
|
+
export const workItemRepository = {
|
|
44
|
+
/**
|
|
45
|
+
* Create a new work item
|
|
46
|
+
*/
|
|
47
|
+
create(input) {
|
|
48
|
+
const db = getDb();
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
// Get next item number for this project
|
|
51
|
+
const maxResult = db
|
|
52
|
+
.prepare('SELECT MAX(item_number) as max_num FROM work_items WHERE project_id = ?')
|
|
53
|
+
.get(input.project_id);
|
|
54
|
+
const itemNumber = (maxResult.max_num ?? 0) + 1;
|
|
55
|
+
const itemId = `${getTypePrefix(input.type)}-${String(itemNumber).padStart(3, '0')}`;
|
|
56
|
+
const result = db
|
|
57
|
+
.prepare(`
|
|
58
|
+
INSERT INTO work_items (
|
|
59
|
+
project_id, item_number, item_id, type, status, priority,
|
|
60
|
+
title, description, estimated_effort,
|
|
61
|
+
created_at, updated_at
|
|
62
|
+
) VALUES (
|
|
63
|
+
@project_id, @item_number, @item_id, @type, @status, @priority,
|
|
64
|
+
@title, @description, @estimated_effort,
|
|
65
|
+
@created_at, @updated_at
|
|
66
|
+
)
|
|
67
|
+
`)
|
|
68
|
+
.run({
|
|
69
|
+
project_id: input.project_id,
|
|
70
|
+
item_number: itemNumber,
|
|
71
|
+
item_id: itemId,
|
|
72
|
+
type: input.type,
|
|
73
|
+
status: 'backlog',
|
|
74
|
+
priority: input.priority ?? 'medium',
|
|
75
|
+
title: input.title,
|
|
76
|
+
description: input.description ?? null,
|
|
77
|
+
estimated_effort: input.estimated_effort ?? null,
|
|
78
|
+
created_at: now,
|
|
79
|
+
updated_at: now,
|
|
80
|
+
});
|
|
81
|
+
const item = this.getById(Number(result.lastInsertRowid));
|
|
82
|
+
if (!item)
|
|
83
|
+
throw new Error('Failed to create work item');
|
|
84
|
+
return item;
|
|
85
|
+
},
|
|
86
|
+
/**
|
|
87
|
+
* Get work item by database ID
|
|
88
|
+
*/
|
|
89
|
+
getById(id) {
|
|
90
|
+
const db = getDb();
|
|
91
|
+
const record = db
|
|
92
|
+
.prepare('SELECT * FROM work_items WHERE id = ?')
|
|
93
|
+
.get(id);
|
|
94
|
+
return record ? recordToWorkItem(record) : null;
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* Get work item by item_id (e.g., "REQ-001")
|
|
98
|
+
*/
|
|
99
|
+
getByItemId(projectId, itemId) {
|
|
100
|
+
const db = getDb();
|
|
101
|
+
const record = db
|
|
102
|
+
.prepare('SELECT * FROM work_items WHERE project_id = ? AND item_id = ?')
|
|
103
|
+
.get(projectId, itemId);
|
|
104
|
+
return record ? recordToWorkItem(record) : null;
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Query work items with filters
|
|
108
|
+
*/
|
|
109
|
+
query(input) {
|
|
110
|
+
const db = getDb();
|
|
111
|
+
const conditions = [];
|
|
112
|
+
const params = {};
|
|
113
|
+
if (input.project_id) {
|
|
114
|
+
conditions.push('project_id = @project_id');
|
|
115
|
+
params.project_id = input.project_id;
|
|
116
|
+
}
|
|
117
|
+
if (input.status && input.status !== 'all') {
|
|
118
|
+
conditions.push('status = @status');
|
|
119
|
+
params.status = input.status;
|
|
120
|
+
}
|
|
121
|
+
if (input.type && input.type !== 'all') {
|
|
122
|
+
conditions.push('type = @type');
|
|
123
|
+
params.type = input.type;
|
|
124
|
+
}
|
|
125
|
+
if (input.priority && input.priority !== 'all') {
|
|
126
|
+
conditions.push('priority = @priority');
|
|
127
|
+
params.priority = input.priority;
|
|
128
|
+
}
|
|
129
|
+
if (input.search) {
|
|
130
|
+
conditions.push('(title LIKE @search OR description LIKE @search)');
|
|
131
|
+
params.search = `%${input.search}%`;
|
|
132
|
+
}
|
|
133
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
134
|
+
// Get total count
|
|
135
|
+
const countResult = db
|
|
136
|
+
.prepare(`SELECT COUNT(*) as count FROM work_items ${whereClause}`)
|
|
137
|
+
.get(params);
|
|
138
|
+
// Priority order: critical > high > medium > low
|
|
139
|
+
const priorityOrder = `
|
|
140
|
+
CASE priority
|
|
141
|
+
WHEN 'critical' THEN 1
|
|
142
|
+
WHEN 'high' THEN 2
|
|
143
|
+
WHEN 'medium' THEN 3
|
|
144
|
+
WHEN 'low' THEN 4
|
|
145
|
+
ELSE 5
|
|
146
|
+
END
|
|
147
|
+
`;
|
|
148
|
+
// Get items with pagination
|
|
149
|
+
let query = `
|
|
150
|
+
SELECT * FROM work_items ${whereClause}
|
|
151
|
+
ORDER BY
|
|
152
|
+
CASE status WHEN 'in_progress' THEN 0 ELSE 1 END,
|
|
153
|
+
${priorityOrder},
|
|
154
|
+
created_at ASC
|
|
155
|
+
`;
|
|
156
|
+
const limit = input.limit ?? 50;
|
|
157
|
+
const offset = input.offset ?? 0;
|
|
158
|
+
query += ` LIMIT @limit OFFSET @offset`;
|
|
159
|
+
params.limit = limit;
|
|
160
|
+
params.offset = offset;
|
|
161
|
+
const records = db.prepare(query).all(params);
|
|
162
|
+
return {
|
|
163
|
+
items: records.map(recordToWorkItem),
|
|
164
|
+
total: countResult.count,
|
|
165
|
+
hasMore: offset + records.length < countResult.count,
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
/**
|
|
169
|
+
* Get next work item (highest priority backlog item)
|
|
170
|
+
*/
|
|
171
|
+
getNext(projectId, type) {
|
|
172
|
+
const db = getDb();
|
|
173
|
+
const priorityOrder = `
|
|
174
|
+
CASE priority
|
|
175
|
+
WHEN 'critical' THEN 1
|
|
176
|
+
WHEN 'high' THEN 2
|
|
177
|
+
WHEN 'medium' THEN 3
|
|
178
|
+
WHEN 'low' THEN 4
|
|
179
|
+
ELSE 5
|
|
180
|
+
END
|
|
181
|
+
`;
|
|
182
|
+
let query = `
|
|
183
|
+
SELECT * FROM work_items
|
|
184
|
+
WHERE project_id = ? AND status = 'backlog'
|
|
185
|
+
`;
|
|
186
|
+
const params = [projectId];
|
|
187
|
+
if (type) {
|
|
188
|
+
query += ' AND type = ?';
|
|
189
|
+
params.push(type);
|
|
190
|
+
}
|
|
191
|
+
query += ` ORDER BY ${priorityOrder}, created_at ASC LIMIT 1`;
|
|
192
|
+
const record = db.prepare(query).get(...params);
|
|
193
|
+
return record ? recordToWorkItem(record) : null;
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* Update a work item
|
|
197
|
+
*/
|
|
198
|
+
update(id, input) {
|
|
199
|
+
const db = getDb();
|
|
200
|
+
const updates = [];
|
|
201
|
+
const params = { id };
|
|
202
|
+
const oldItem = this.getById(id);
|
|
203
|
+
if (!oldItem)
|
|
204
|
+
return null;
|
|
205
|
+
if (input.type !== undefined) {
|
|
206
|
+
updates.push('type = @type');
|
|
207
|
+
params.type = input.type;
|
|
208
|
+
}
|
|
209
|
+
if (input.status !== undefined) {
|
|
210
|
+
updates.push('status = @status');
|
|
211
|
+
params.status = input.status;
|
|
212
|
+
// Handle status transitions
|
|
213
|
+
if (input.status === 'completed' && oldItem.status !== 'completed') {
|
|
214
|
+
updates.push('completed_at = @completed_at');
|
|
215
|
+
params.completed_at = new Date().toISOString();
|
|
216
|
+
}
|
|
217
|
+
if (input.status === 'in_progress' && oldItem.status === 'backlog') {
|
|
218
|
+
// Starting work - could track start time here
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (input.priority !== undefined) {
|
|
222
|
+
updates.push('priority = @priority');
|
|
223
|
+
params.priority = input.priority;
|
|
224
|
+
}
|
|
225
|
+
if (input.guided_step !== undefined) {
|
|
226
|
+
updates.push('guided_step = @guided_step');
|
|
227
|
+
params.guided_step = input.guided_step;
|
|
228
|
+
}
|
|
229
|
+
if (input.title !== undefined) {
|
|
230
|
+
updates.push('title = @title');
|
|
231
|
+
params.title = input.title;
|
|
232
|
+
}
|
|
233
|
+
if (input.description !== undefined) {
|
|
234
|
+
updates.push('description = @description');
|
|
235
|
+
params.description = input.description;
|
|
236
|
+
}
|
|
237
|
+
if (input.estimated_effort !== undefined) {
|
|
238
|
+
updates.push('estimated_effort = @estimated_effort');
|
|
239
|
+
params.estimated_effort = input.estimated_effort;
|
|
240
|
+
}
|
|
241
|
+
if (input.actual_minutes !== undefined) {
|
|
242
|
+
updates.push('actual_minutes = @actual_minutes');
|
|
243
|
+
params.actual_minutes = input.actual_minutes;
|
|
244
|
+
}
|
|
245
|
+
if (input.commit_hash !== undefined) {
|
|
246
|
+
updates.push('commit_hash = @commit_hash');
|
|
247
|
+
params.commit_hash = input.commit_hash;
|
|
248
|
+
}
|
|
249
|
+
if (updates.length === 0) {
|
|
250
|
+
return oldItem;
|
|
251
|
+
}
|
|
252
|
+
// Always update updated_at
|
|
253
|
+
updates.push('updated_at = @updated_at');
|
|
254
|
+
params.updated_at = new Date().toISOString();
|
|
255
|
+
const query = `UPDATE work_items SET ${updates.join(', ')} WHERE id = @id`;
|
|
256
|
+
db.prepare(query).run(params);
|
|
257
|
+
// Record history
|
|
258
|
+
this.recordHistory(id, oldItem.projectId, input, oldItem);
|
|
259
|
+
return this.getById(id);
|
|
260
|
+
},
|
|
261
|
+
/**
|
|
262
|
+
* Record change in history
|
|
263
|
+
*/
|
|
264
|
+
recordHistory(workItemId, projectId, changes, oldItem) {
|
|
265
|
+
const db = getDb();
|
|
266
|
+
const now = new Date().toISOString();
|
|
267
|
+
// Record status changes
|
|
268
|
+
if (changes.status && changes.status !== oldItem.status) {
|
|
269
|
+
db.prepare(`
|
|
270
|
+
INSERT INTO work_item_history (work_item_id, project_id, action, old_value, new_value, changed_by, changed_at)
|
|
271
|
+
VALUES (?, ?, 'status_change', ?, ?, 'agent', ?)
|
|
272
|
+
`).run(workItemId, projectId, oldItem.status, changes.status, now);
|
|
273
|
+
}
|
|
274
|
+
// Record priority changes
|
|
275
|
+
if (changes.priority && changes.priority !== oldItem.priority) {
|
|
276
|
+
db.prepare(`
|
|
277
|
+
INSERT INTO work_item_history (work_item_id, project_id, action, old_value, new_value, changed_by, changed_at)
|
|
278
|
+
VALUES (?, ?, 'priority_change', ?, ?, 'agent', ?)
|
|
279
|
+
`).run(workItemId, projectId, oldItem.priority, changes.priority, now);
|
|
280
|
+
}
|
|
281
|
+
// Record guided step changes
|
|
282
|
+
if (changes.guided_step !== undefined && changes.guided_step !== oldItem.guidedStep) {
|
|
283
|
+
db.prepare(`
|
|
284
|
+
INSERT INTO work_item_history (work_item_id, project_id, action, old_value, new_value, changed_by, changed_at)
|
|
285
|
+
VALUES (?, ?, 'step_advance', ?, ?, 'agent', ?)
|
|
286
|
+
`).run(workItemId, projectId, oldItem.guidedStep ?? 'none', changes.guided_step ?? 'none', now);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* Delete a work item
|
|
291
|
+
*/
|
|
292
|
+
delete(id) {
|
|
293
|
+
const db = getDb();
|
|
294
|
+
const result = db.prepare('DELETE FROM work_items WHERE id = ?').run(id);
|
|
295
|
+
return result.changes > 0;
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* Get work item counts by status for a project
|
|
299
|
+
*/
|
|
300
|
+
getStatusCounts(projectId) {
|
|
301
|
+
const db = getDb();
|
|
302
|
+
const results = db
|
|
303
|
+
.prepare(`
|
|
304
|
+
SELECT status, COUNT(*) as count
|
|
305
|
+
FROM work_items
|
|
306
|
+
WHERE project_id = ?
|
|
307
|
+
GROUP BY status
|
|
308
|
+
`)
|
|
309
|
+
.all(projectId);
|
|
310
|
+
const counts = {
|
|
311
|
+
backlog: 0,
|
|
312
|
+
in_progress: 0,
|
|
313
|
+
completed: 0,
|
|
314
|
+
skipped: 0,
|
|
315
|
+
};
|
|
316
|
+
for (const row of results) {
|
|
317
|
+
counts[row.status] = row.count;
|
|
318
|
+
}
|
|
319
|
+
return counts;
|
|
320
|
+
},
|
|
321
|
+
/**
|
|
322
|
+
* Get history for a work item
|
|
323
|
+
*/
|
|
324
|
+
getHistory(workItemId) {
|
|
325
|
+
const db = getDb();
|
|
326
|
+
const records = db
|
|
327
|
+
.prepare(`
|
|
328
|
+
SELECT * FROM work_item_history
|
|
329
|
+
WHERE work_item_id = ?
|
|
330
|
+
ORDER BY changed_at DESC
|
|
331
|
+
`)
|
|
332
|
+
.all(workItemId);
|
|
333
|
+
return records.map((r) => ({
|
|
334
|
+
action: r.action,
|
|
335
|
+
oldValue: r.old_value,
|
|
336
|
+
newValue: r.new_value,
|
|
337
|
+
notes: r.notes,
|
|
338
|
+
changedBy: r.changed_by,
|
|
339
|
+
changedAt: new Date(r.changed_at),
|
|
340
|
+
}));
|
|
341
|
+
},
|
|
342
|
+
/**
|
|
343
|
+
* Bulk import work items (for /promote command)
|
|
344
|
+
*/
|
|
345
|
+
bulkCreate(projectId, items) {
|
|
346
|
+
const db = getDb();
|
|
347
|
+
const now = new Date().toISOString();
|
|
348
|
+
const createdIds = [];
|
|
349
|
+
// Get starting item number
|
|
350
|
+
const maxResult = db
|
|
351
|
+
.prepare('SELECT MAX(item_number) as max_num FROM work_items WHERE project_id = ?')
|
|
352
|
+
.get(projectId);
|
|
353
|
+
let itemNumber = (maxResult.max_num ?? 0) + 1;
|
|
354
|
+
const insertStmt = db.prepare(`
|
|
355
|
+
INSERT INTO work_items (
|
|
356
|
+
project_id, item_number, item_id, type, status, priority,
|
|
357
|
+
title, description, created_at, updated_at
|
|
358
|
+
) VALUES (
|
|
359
|
+
@project_id, @item_number, @item_id, @type, @status, @priority,
|
|
360
|
+
@title, @description, @created_at, @updated_at
|
|
361
|
+
)
|
|
362
|
+
`);
|
|
363
|
+
// Use transaction for atomicity
|
|
364
|
+
const insertAll = db.transaction(() => {
|
|
365
|
+
for (const item of items) {
|
|
366
|
+
const itemId = `${getTypePrefix(item.type)}-${String(itemNumber).padStart(3, '0')}`;
|
|
367
|
+
const result = insertStmt.run({
|
|
368
|
+
project_id: projectId,
|
|
369
|
+
item_number: itemNumber,
|
|
370
|
+
item_id: itemId,
|
|
371
|
+
type: item.type,
|
|
372
|
+
status: 'backlog',
|
|
373
|
+
priority: item.priority ?? 'medium',
|
|
374
|
+
title: item.title,
|
|
375
|
+
description: item.description ?? null,
|
|
376
|
+
created_at: now,
|
|
377
|
+
updated_at: now,
|
|
378
|
+
});
|
|
379
|
+
createdIds.push(Number(result.lastInsertRowid));
|
|
380
|
+
itemNumber++;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
insertAll();
|
|
384
|
+
// Fetch all created items
|
|
385
|
+
return createdIds
|
|
386
|
+
.map((id) => this.getById(id))
|
|
387
|
+
.filter((item) => item !== null);
|
|
388
|
+
},
|
|
389
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Schema for compilr-cli workflow system
|
|
3
|
+
*
|
|
4
|
+
* Based on: db-workflow-architecture.md
|
|
5
|
+
* Database: SQLite via better-sqlite3
|
|
6
|
+
*/
|
|
7
|
+
export declare const SCHEMA_VERSION = 1;
|
|
8
|
+
export declare const SCHEMA_SQL = "\n-- Schema version tracking\nCREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Projects table\nCREATE TABLE IF NOT EXISTS projects (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT UNIQUE NOT NULL,\n display_name TEXT NOT NULL,\n description TEXT,\n\n -- Project type and status\n type TEXT DEFAULT 'general',\n status TEXT DEFAULT 'active',\n\n -- Paths\n path TEXT NOT NULL,\n docs_path TEXT,\n repo_pattern TEXT DEFAULT 'single',\n\n -- Tech stack (auto-detected)\n language TEXT,\n framework TEXT,\n package_manager TEXT,\n runtime_version TEXT,\n\n -- Commands (JSON)\n commands TEXT,\n\n -- Git info\n git_remote TEXT,\n git_branch TEXT DEFAULT 'main',\n\n -- Workflow\n workflow_mode TEXT DEFAULT 'flexible',\n lifecycle_state TEXT DEFAULT 'setup',\n current_item_id TEXT,\n\n -- Context & Metadata (JSON)\n last_context TEXT,\n metadata TEXT,\n\n -- Timestamps\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_activity_at DATETIME\n);\n\n-- Work items (backlog items, tasks, bugs)\nCREATE TABLE IF NOT EXISTS work_items (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL,\n\n -- Identity\n item_number INTEGER NOT NULL,\n item_id TEXT NOT NULL,\n\n -- Classification\n type TEXT NOT NULL,\n status TEXT DEFAULT 'backlog',\n priority TEXT DEFAULT 'medium',\n\n -- Guided mode: current step in work item cycle\n guided_step TEXT,\n\n -- Content\n title TEXT NOT NULL,\n description TEXT,\n\n -- Effort tracking\n estimated_effort TEXT,\n actual_minutes INTEGER,\n\n -- Completion\n completed_at DATETIME,\n completed_by TEXT,\n commit_hash TEXT,\n\n -- Timestamps\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\n FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,\n UNIQUE (project_id, item_id)\n);\n\n-- Project documents (PRD, architecture, etc.)\nCREATE TABLE IF NOT EXISTS project_documents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL,\n\n doc_type TEXT NOT NULL,\n title TEXT NOT NULL,\n content TEXT NOT NULL,\n\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\n FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- Work item history (audit trail)\nCREATE TABLE IF NOT EXISTS work_item_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n work_item_id INTEGER NOT NULL,\n project_id INTEGER NOT NULL,\n\n action TEXT NOT NULL,\n old_value TEXT,\n new_value TEXT,\n notes TEXT,\n\n changed_by TEXT,\n changed_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n\n FOREIGN KEY (work_item_id) REFERENCES work_items(id) ON DELETE CASCADE,\n FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE\n);\n\n-- Indexes for common queries\nCREATE INDEX IF NOT EXISTS idx_projects_path ON projects(path);\nCREATE INDEX IF NOT EXISTS idx_projects_docs_path ON projects(docs_path);\nCREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);\n\nCREATE INDEX IF NOT EXISTS idx_work_items_project ON work_items(project_id);\nCREATE INDEX IF NOT EXISTS idx_work_items_status ON work_items(status);\nCREATE INDEX IF NOT EXISTS idx_work_items_priority ON work_items(priority);\n\nCREATE INDEX IF NOT EXISTS idx_project_documents_project ON project_documents(project_id);\nCREATE INDEX IF NOT EXISTS idx_project_documents_type ON project_documents(doc_type);\n\nCREATE INDEX IF NOT EXISTS idx_work_item_history_item ON work_item_history(work_item_id);\n";
|
|
9
|
+
export interface ProjectRecord {
|
|
10
|
+
id: number;
|
|
11
|
+
name: string;
|
|
12
|
+
display_name: string;
|
|
13
|
+
description: string | null;
|
|
14
|
+
type: string;
|
|
15
|
+
status: string;
|
|
16
|
+
path: string;
|
|
17
|
+
docs_path: string | null;
|
|
18
|
+
repo_pattern: string;
|
|
19
|
+
language: string | null;
|
|
20
|
+
framework: string | null;
|
|
21
|
+
package_manager: string | null;
|
|
22
|
+
runtime_version: string | null;
|
|
23
|
+
commands: string | null;
|
|
24
|
+
git_remote: string | null;
|
|
25
|
+
git_branch: string;
|
|
26
|
+
workflow_mode: string;
|
|
27
|
+
lifecycle_state: string;
|
|
28
|
+
current_item_id: string | null;
|
|
29
|
+
last_context: string | null;
|
|
30
|
+
metadata: string | null;
|
|
31
|
+
created_at: string;
|
|
32
|
+
updated_at: string;
|
|
33
|
+
last_activity_at: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface WorkItemRecord {
|
|
36
|
+
id: number;
|
|
37
|
+
project_id: number;
|
|
38
|
+
item_number: number;
|
|
39
|
+
item_id: string;
|
|
40
|
+
type: string;
|
|
41
|
+
status: string;
|
|
42
|
+
priority: string;
|
|
43
|
+
guided_step: string | null;
|
|
44
|
+
title: string;
|
|
45
|
+
description: string | null;
|
|
46
|
+
estimated_effort: string | null;
|
|
47
|
+
actual_minutes: number | null;
|
|
48
|
+
completed_at: string | null;
|
|
49
|
+
completed_by: string | null;
|
|
50
|
+
commit_hash: string | null;
|
|
51
|
+
created_at: string;
|
|
52
|
+
updated_at: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ProjectDocumentRecord {
|
|
55
|
+
id: number;
|
|
56
|
+
project_id: number;
|
|
57
|
+
doc_type: string;
|
|
58
|
+
title: string;
|
|
59
|
+
content: string;
|
|
60
|
+
created_at: string;
|
|
61
|
+
updated_at: string;
|
|
62
|
+
}
|
|
63
|
+
export interface WorkItemHistoryRecord {
|
|
64
|
+
id: number;
|
|
65
|
+
work_item_id: number;
|
|
66
|
+
project_id: number;
|
|
67
|
+
action: string;
|
|
68
|
+
old_value: string | null;
|
|
69
|
+
new_value: string | null;
|
|
70
|
+
notes: string | null;
|
|
71
|
+
changed_by: string | null;
|
|
72
|
+
changed_at: string;
|
|
73
|
+
}
|
|
74
|
+
export type ProjectType = 'general' | 'web' | 'cli' | 'library' | 'api';
|
|
75
|
+
export type ProjectStatus = 'active' | 'paused' | 'completed' | 'archived';
|
|
76
|
+
export type RepoPattern = 'single' | 'two-repo';
|
|
77
|
+
export type WorkflowMode = 'flexible' | 'guided';
|
|
78
|
+
export type LifecycleState = 'setup' | 'active' | 'maintenance' | 'complete';
|
|
79
|
+
export type WorkItemType = 'feature' | 'bug' | 'tech-debt' | 'chore';
|
|
80
|
+
export type WorkItemStatus = 'backlog' | 'in_progress' | 'completed' | 'skipped';
|
|
81
|
+
export type WorkItemPriority = 'critical' | 'high' | 'medium' | 'low';
|
|
82
|
+
export type GuidedStep = 'plan' | 'implement' | 'test' | 'commit' | 'review';
|
|
83
|
+
export type DocumentType = 'prd' | 'architecture' | 'design' | 'notes';
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Schema for compilr-cli workflow system
|
|
3
|
+
*
|
|
4
|
+
* Based on: db-workflow-architecture.md
|
|
5
|
+
* Database: SQLite via better-sqlite3
|
|
6
|
+
*/
|
|
7
|
+
export const SCHEMA_VERSION = 1;
|
|
8
|
+
export const SCHEMA_SQL = `
|
|
9
|
+
-- Schema version tracking
|
|
10
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
11
|
+
version INTEGER PRIMARY KEY,
|
|
12
|
+
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- Projects table
|
|
16
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
name TEXT UNIQUE NOT NULL,
|
|
19
|
+
display_name TEXT NOT NULL,
|
|
20
|
+
description TEXT,
|
|
21
|
+
|
|
22
|
+
-- Project type and status
|
|
23
|
+
type TEXT DEFAULT 'general',
|
|
24
|
+
status TEXT DEFAULT 'active',
|
|
25
|
+
|
|
26
|
+
-- Paths
|
|
27
|
+
path TEXT NOT NULL,
|
|
28
|
+
docs_path TEXT,
|
|
29
|
+
repo_pattern TEXT DEFAULT 'single',
|
|
30
|
+
|
|
31
|
+
-- Tech stack (auto-detected)
|
|
32
|
+
language TEXT,
|
|
33
|
+
framework TEXT,
|
|
34
|
+
package_manager TEXT,
|
|
35
|
+
runtime_version TEXT,
|
|
36
|
+
|
|
37
|
+
-- Commands (JSON)
|
|
38
|
+
commands TEXT,
|
|
39
|
+
|
|
40
|
+
-- Git info
|
|
41
|
+
git_remote TEXT,
|
|
42
|
+
git_branch TEXT DEFAULT 'main',
|
|
43
|
+
|
|
44
|
+
-- Workflow
|
|
45
|
+
workflow_mode TEXT DEFAULT 'flexible',
|
|
46
|
+
lifecycle_state TEXT DEFAULT 'setup',
|
|
47
|
+
current_item_id TEXT,
|
|
48
|
+
|
|
49
|
+
-- Context & Metadata (JSON)
|
|
50
|
+
last_context TEXT,
|
|
51
|
+
metadata TEXT,
|
|
52
|
+
|
|
53
|
+
-- Timestamps
|
|
54
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
55
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
+
last_activity_at DATETIME
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
-- Work items (backlog items, tasks, bugs)
|
|
60
|
+
CREATE TABLE IF NOT EXISTS work_items (
|
|
61
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
|
+
project_id INTEGER NOT NULL,
|
|
63
|
+
|
|
64
|
+
-- Identity
|
|
65
|
+
item_number INTEGER NOT NULL,
|
|
66
|
+
item_id TEXT NOT NULL,
|
|
67
|
+
|
|
68
|
+
-- Classification
|
|
69
|
+
type TEXT NOT NULL,
|
|
70
|
+
status TEXT DEFAULT 'backlog',
|
|
71
|
+
priority TEXT DEFAULT 'medium',
|
|
72
|
+
|
|
73
|
+
-- Guided mode: current step in work item cycle
|
|
74
|
+
guided_step TEXT,
|
|
75
|
+
|
|
76
|
+
-- Content
|
|
77
|
+
title TEXT NOT NULL,
|
|
78
|
+
description TEXT,
|
|
79
|
+
|
|
80
|
+
-- Effort tracking
|
|
81
|
+
estimated_effort TEXT,
|
|
82
|
+
actual_minutes INTEGER,
|
|
83
|
+
|
|
84
|
+
-- Completion
|
|
85
|
+
completed_at DATETIME,
|
|
86
|
+
completed_by TEXT,
|
|
87
|
+
commit_hash TEXT,
|
|
88
|
+
|
|
89
|
+
-- Timestamps
|
|
90
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
91
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
92
|
+
|
|
93
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
94
|
+
UNIQUE (project_id, item_id)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
-- Project documents (PRD, architecture, etc.)
|
|
98
|
+
CREATE TABLE IF NOT EXISTS project_documents (
|
|
99
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
100
|
+
project_id INTEGER NOT NULL,
|
|
101
|
+
|
|
102
|
+
doc_type TEXT NOT NULL,
|
|
103
|
+
title TEXT NOT NULL,
|
|
104
|
+
content TEXT NOT NULL,
|
|
105
|
+
|
|
106
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
107
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
108
|
+
|
|
109
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
-- Work item history (audit trail)
|
|
113
|
+
CREATE TABLE IF NOT EXISTS work_item_history (
|
|
114
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
115
|
+
work_item_id INTEGER NOT NULL,
|
|
116
|
+
project_id INTEGER NOT NULL,
|
|
117
|
+
|
|
118
|
+
action TEXT NOT NULL,
|
|
119
|
+
old_value TEXT,
|
|
120
|
+
new_value TEXT,
|
|
121
|
+
notes TEXT,
|
|
122
|
+
|
|
123
|
+
changed_by TEXT,
|
|
124
|
+
changed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
125
|
+
|
|
126
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id) ON DELETE CASCADE,
|
|
127
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
-- Indexes for common queries
|
|
131
|
+
CREATE INDEX IF NOT EXISTS idx_projects_path ON projects(path);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_projects_docs_path ON projects(docs_path);
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);
|
|
134
|
+
|
|
135
|
+
CREATE INDEX IF NOT EXISTS idx_work_items_project ON work_items(project_id);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_work_items_status ON work_items(status);
|
|
137
|
+
CREATE INDEX IF NOT EXISTS idx_work_items_priority ON work_items(priority);
|
|
138
|
+
|
|
139
|
+
CREATE INDEX IF NOT EXISTS idx_project_documents_project ON project_documents(project_id);
|
|
140
|
+
CREATE INDEX IF NOT EXISTS idx_project_documents_type ON project_documents(doc_type);
|
|
141
|
+
|
|
142
|
+
CREATE INDEX IF NOT EXISTS idx_work_item_history_item ON work_item_history(work_item_id);
|
|
143
|
+
`;
|
package/dist/debug.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Logger
|
|
3
|
+
*
|
|
4
|
+
* Writes debug output to a file for troubleshooting.
|
|
5
|
+
* Enable with DEBUG=1 environment variable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function debug(category: string, message: string, data?: unknown): void;
|
|
8
|
+
export declare function debugError(category: string, error: unknown): void;
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug Logger
|
|
3
|
+
*
|
|
4
|
+
* Writes debug output to a file for troubleshooting.
|
|
5
|
+
* Enable with DEBUG=1 environment variable.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
const DEBUG_ENABLED = process.env.DEBUG === '1';
|
|
10
|
+
const LOG_FILE = path.join(process.cwd(), 'compilr-debug.log');
|
|
11
|
+
let initialized = false;
|
|
12
|
+
function ensureInit() {
|
|
13
|
+
if (!initialized && DEBUG_ENABLED) {
|
|
14
|
+
// Clear log file on start
|
|
15
|
+
fs.writeFileSync(LOG_FILE, `=== Debug Log Started: ${new Date().toISOString()} ===\n`);
|
|
16
|
+
initialized = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function debug(category, message, data) {
|
|
20
|
+
if (!DEBUG_ENABLED)
|
|
21
|
+
return;
|
|
22
|
+
ensureInit();
|
|
23
|
+
const timestamp = new Date().toISOString();
|
|
24
|
+
let line = `[${timestamp}] [${category}] ${message}`;
|
|
25
|
+
if (data !== undefined) {
|
|
26
|
+
try {
|
|
27
|
+
line += '\n ' + JSON.stringify(data, null, 2).replace(/\n/g, '\n ');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
line += '\n [Unstringifiable data]';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
fs.appendFileSync(LOG_FILE, line + '\n');
|
|
34
|
+
}
|
|
35
|
+
export function debugError(category, error) {
|
|
36
|
+
if (!DEBUG_ENABLED)
|
|
37
|
+
return;
|
|
38
|
+
ensureInit();
|
|
39
|
+
const timestamp = new Date().toISOString();
|
|
40
|
+
let line = `[${timestamp}] [${category}] ERROR: `;
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
line += `${error.message}\n Stack: ${error.stack ?? 'no stack'}`;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
line += String(error);
|
|
46
|
+
}
|
|
47
|
+
fs.appendFileSync(LOG_FILE, line + '\n');
|
|
48
|
+
}
|
package/dist/index.d.ts
ADDED