clitrigger 0.1.5 → 0.1.6

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 (112) hide show
  1. package/README.md +121 -97
  2. package/README_KR.md +243 -0
  3. package/dist/client/assets/index-BiXO2oR3.js +541 -0
  4. package/dist/client/assets/index-tGOot928.css +1 -0
  5. package/dist/client/index.html +2 -2
  6. package/dist/server/data/cli-models-registry.json +34 -0
  7. package/dist/server/db/queries.d.ts +95 -48
  8. package/dist/server/db/queries.d.ts.map +1 -1
  9. package/dist/server/db/queries.js +328 -129
  10. package/dist/server/db/queries.js.map +1 -1
  11. package/dist/server/db/schema.d.ts.map +1 -1
  12. package/dist/server/db/schema.js +67 -35
  13. package/dist/server/db/schema.js.map +1 -1
  14. package/dist/server/index.d.ts.map +1 -1
  15. package/dist/server/index.js +27 -13
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/server/lib/git.d.ts +3 -0
  18. package/dist/server/lib/git.d.ts.map +1 -0
  19. package/dist/server/lib/git.js +7 -0
  20. package/dist/server/lib/git.js.map +1 -0
  21. package/dist/server/routes/{pipelines.d.ts → analytics.d.ts} +1 -1
  22. package/dist/server/routes/analytics.d.ts.map +1 -0
  23. package/dist/server/routes/analytics.js +102 -0
  24. package/dist/server/routes/analytics.js.map +1 -0
  25. package/dist/server/routes/cli-status.d.ts +3 -0
  26. package/dist/server/routes/cli-status.d.ts.map +1 -0
  27. package/dist/server/routes/cli-status.js +28 -0
  28. package/dist/server/routes/cli-status.js.map +1 -0
  29. package/dist/server/routes/discussions.js +5 -5
  30. package/dist/server/routes/discussions.js.map +1 -1
  31. package/dist/server/routes/execution.d.ts.map +1 -1
  32. package/dist/server/routes/execution.js +9 -5
  33. package/dist/server/routes/execution.js.map +1 -1
  34. package/dist/server/routes/images.d.ts +21 -0
  35. package/dist/server/routes/images.d.ts.map +1 -1
  36. package/dist/server/routes/images.js +163 -1
  37. package/dist/server/routes/images.js.map +1 -1
  38. package/dist/server/routes/logs.js +3 -3
  39. package/dist/server/routes/logs.js.map +1 -1
  40. package/dist/server/routes/models.d.ts.map +1 -1
  41. package/dist/server/routes/models.js +2 -0
  42. package/dist/server/routes/models.js.map +1 -1
  43. package/dist/server/routes/planner.d.ts +3 -0
  44. package/dist/server/routes/planner.d.ts.map +1 -0
  45. package/dist/server/routes/planner.js +344 -0
  46. package/dist/server/routes/planner.js.map +1 -0
  47. package/dist/server/routes/projects.d.ts.map +1 -1
  48. package/dist/server/routes/projects.js +159 -1
  49. package/dist/server/routes/projects.js.map +1 -1
  50. package/dist/server/routes/schedules.d.ts.map +1 -1
  51. package/dist/server/routes/schedules.js +39 -0
  52. package/dist/server/routes/schedules.js.map +1 -1
  53. package/dist/server/routes/sessions.d.ts +3 -0
  54. package/dist/server/routes/sessions.d.ts.map +1 -0
  55. package/dist/server/routes/sessions.js +200 -0
  56. package/dist/server/routes/sessions.js.map +1 -0
  57. package/dist/server/routes/todos.d.ts.map +1 -1
  58. package/dist/server/routes/todos.js +16 -4
  59. package/dist/server/routes/todos.js.map +1 -1
  60. package/dist/server/services/claude-manager.d.ts.map +1 -1
  61. package/dist/server/services/claude-manager.js +63 -28
  62. package/dist/server/services/claude-manager.js.map +1 -1
  63. package/dist/server/services/cli-adapters.d.ts +60 -0
  64. package/dist/server/services/cli-adapters.d.ts.map +1 -1
  65. package/dist/server/services/cli-adapters.js +123 -5
  66. package/dist/server/services/cli-adapters.js.map +1 -1
  67. package/dist/server/services/cli-status.d.ts +8 -0
  68. package/dist/server/services/cli-status.d.ts.map +1 -0
  69. package/dist/server/services/cli-status.js +57 -0
  70. package/dist/server/services/cli-status.js.map +1 -0
  71. package/dist/server/services/discussion-orchestrator.d.ts.map +1 -1
  72. package/dist/server/services/discussion-orchestrator.js +33 -6
  73. package/dist/server/services/discussion-orchestrator.js.map +1 -1
  74. package/dist/server/services/log-streamer.d.ts +12 -0
  75. package/dist/server/services/log-streamer.d.ts.map +1 -1
  76. package/dist/server/services/log-streamer.js +117 -31
  77. package/dist/server/services/log-streamer.js.map +1 -1
  78. package/dist/server/services/model-sync.d.ts +22 -0
  79. package/dist/server/services/model-sync.d.ts.map +1 -0
  80. package/dist/server/services/model-sync.js +181 -0
  81. package/dist/server/services/model-sync.js.map +1 -0
  82. package/dist/server/services/orchestrator.d.ts +18 -0
  83. package/dist/server/services/orchestrator.d.ts.map +1 -1
  84. package/dist/server/services/orchestrator.js +81 -11
  85. package/dist/server/services/orchestrator.js.map +1 -1
  86. package/dist/server/services/pty-output-filter.d.ts.map +1 -1
  87. package/dist/server/services/pty-output-filter.js +91 -8
  88. package/dist/server/services/pty-output-filter.js.map +1 -1
  89. package/dist/server/services/session-manager.d.ts +22 -0
  90. package/dist/server/services/session-manager.d.ts.map +1 -0
  91. package/dist/server/services/session-manager.js +249 -0
  92. package/dist/server/services/session-manager.js.map +1 -0
  93. package/dist/server/services/worktree-manager.d.ts +11 -1
  94. package/dist/server/services/worktree-manager.d.ts.map +1 -1
  95. package/dist/server/services/worktree-manager.js +128 -32
  96. package/dist/server/services/worktree-manager.js.map +1 -1
  97. package/dist/server/websocket/events.d.ts +13 -22
  98. package/dist/server/websocket/events.d.ts.map +1 -1
  99. package/dist/server/websocket/index.d.ts.map +1 -1
  100. package/dist/server/websocket/index.js +17 -1
  101. package/dist/server/websocket/index.js.map +1 -1
  102. package/package.json +3 -3
  103. package/README_EN.md +0 -219
  104. package/dist/client/assets/index-B4peRpUi.js +0 -134
  105. package/dist/client/assets/index-Bjyoy6lB.css +0 -1
  106. package/dist/server/routes/pipelines.d.ts.map +0 -1
  107. package/dist/server/routes/pipelines.js +0 -315
  108. package/dist/server/routes/pipelines.js.map +0 -1
  109. package/dist/server/services/pipeline-orchestrator.d.ts +0 -43
  110. package/dist/server/services/pipeline-orchestrator.d.ts.map +0 -1
  111. package/dist/server/services/pipeline-orchestrator.js +0 -503
  112. package/dist/server/services/pipeline-orchestrator.js.map +0 -1
@@ -0,0 +1,344 @@
1
+ import { Router } from 'express';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import * as queries from '../db/queries.js';
5
+ import { getDatabase } from '../db/connection.js';
6
+ import { getPlannerImagePaths, cleanupPlannerImages } from './images.js';
7
+ const ALLOWED_IMPORT_STATUSES = new Set(['pending', 'in_progress', 'done']);
8
+ function sanitizeFilenamePart(input) {
9
+ return input.replace(/[^a-zA-Z0-9-_]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'project';
10
+ }
11
+ const router = Router();
12
+ // GET /api/projects/:id/planner - list planner items
13
+ router.get('/projects/:id/planner', (req, res) => {
14
+ try {
15
+ const project = queries.getProjectById(req.params.id);
16
+ if (!project) {
17
+ res.status(404).json({ error: 'Project not found' });
18
+ return;
19
+ }
20
+ const items = queries.getPlannerItemsByProjectId(req.params.id);
21
+ res.json(items);
22
+ }
23
+ catch (err) {
24
+ const message = err instanceof Error ? err.message : 'Unknown error';
25
+ res.status(500).json({ error: message });
26
+ }
27
+ });
28
+ // GET /api/projects/:id/planner/tags - get unique tags
29
+ router.get('/projects/:id/planner/tags', (req, res) => {
30
+ try {
31
+ const project = queries.getProjectById(req.params.id);
32
+ if (!project) {
33
+ res.status(404).json({ error: 'Project not found' });
34
+ return;
35
+ }
36
+ const tags = queries.getPlannerTagsByProjectId(req.params.id);
37
+ res.json(tags);
38
+ }
39
+ catch (err) {
40
+ const message = err instanceof Error ? err.message : 'Unknown error';
41
+ res.status(500).json({ error: message });
42
+ }
43
+ });
44
+ // PUT /api/projects/:id/planner/tags/:name - update tag (color or rename)
45
+ router.put('/projects/:id/planner/tags/:name', (req, res) => {
46
+ try {
47
+ const project = queries.getProjectById(req.params.id);
48
+ if (!project) {
49
+ res.status(404).json({ error: 'Project not found' });
50
+ return;
51
+ }
52
+ const { color, new_name } = req.body;
53
+ const tagName = decodeURIComponent(req.params.name);
54
+ if (new_name && new_name !== tagName) {
55
+ queries.renamePlannerTag(req.params.id, tagName, new_name);
56
+ }
57
+ if (color) {
58
+ queries.upsertPlannerTag(req.params.id, new_name || tagName, color);
59
+ }
60
+ const tags = queries.getPlannerTagsByProjectId(req.params.id);
61
+ res.json(tags);
62
+ }
63
+ catch (err) {
64
+ const message = err instanceof Error ? err.message : 'Unknown error';
65
+ res.status(500).json({ error: message });
66
+ }
67
+ });
68
+ // DELETE /api/projects/:id/planner/tags/:name - delete tag from all items
69
+ router.delete('/projects/:id/planner/tags/:name', (req, res) => {
70
+ try {
71
+ const project = queries.getProjectById(req.params.id);
72
+ if (!project) {
73
+ res.status(404).json({ error: 'Project not found' });
74
+ return;
75
+ }
76
+ queries.deletePlannerTag(req.params.id, decodeURIComponent(req.params.name));
77
+ res.status(204).send();
78
+ }
79
+ catch (err) {
80
+ const message = err instanceof Error ? err.message : 'Unknown error';
81
+ res.status(500).json({ error: message });
82
+ }
83
+ });
84
+ // POST /api/projects/:id/planner - create planner item
85
+ router.post('/projects/:id/planner', (req, res) => {
86
+ try {
87
+ const project = queries.getProjectById(req.params.id);
88
+ if (!project) {
89
+ res.status(404).json({ error: 'Project not found' });
90
+ return;
91
+ }
92
+ const { title, description, tags, due_date, priority } = req.body;
93
+ if (!title) {
94
+ res.status(400).json({ error: 'Title is required' });
95
+ return;
96
+ }
97
+ const item = queries.createPlannerItem(req.params.id, title, description, tags, due_date, priority);
98
+ res.status(201).json(item);
99
+ }
100
+ catch (err) {
101
+ const message = err instanceof Error ? err.message : 'Unknown error';
102
+ res.status(500).json({ error: message });
103
+ }
104
+ });
105
+ // GET /api/planner/:id - get single item
106
+ router.get('/planner/:id', (req, res) => {
107
+ try {
108
+ const item = queries.getPlannerItemById(req.params.id);
109
+ if (!item) {
110
+ res.status(404).json({ error: 'Planner item not found' });
111
+ return;
112
+ }
113
+ res.json(item);
114
+ }
115
+ catch (err) {
116
+ const message = err instanceof Error ? err.message : 'Unknown error';
117
+ res.status(500).json({ error: message });
118
+ }
119
+ });
120
+ // PUT /api/planner/:id - update item
121
+ router.put('/planner/:id', (req, res) => {
122
+ try {
123
+ const existing = queries.getPlannerItemById(req.params.id);
124
+ if (!existing) {
125
+ res.status(404).json({ error: 'Planner item not found' });
126
+ return;
127
+ }
128
+ const { title, description, tags, due_date, status, priority } = req.body;
129
+ const updated = queries.updatePlannerItem(req.params.id, {
130
+ title, description, tags, due_date, status, priority,
131
+ });
132
+ res.json(updated);
133
+ }
134
+ catch (err) {
135
+ const message = err instanceof Error ? err.message : 'Unknown error';
136
+ res.status(500).json({ error: message });
137
+ }
138
+ });
139
+ // DELETE /api/planner/:id - delete item
140
+ router.delete('/planner/:id', (req, res) => {
141
+ try {
142
+ const existing = queries.getPlannerItemById(req.params.id);
143
+ if (!existing) {
144
+ res.status(404).json({ error: 'Planner item not found' });
145
+ return;
146
+ }
147
+ cleanupPlannerImages(req.params.id);
148
+ queries.deletePlannerItem(req.params.id);
149
+ res.status(204).send();
150
+ }
151
+ catch (err) {
152
+ const message = err instanceof Error ? err.message : 'Unknown error';
153
+ res.status(500).json({ error: message });
154
+ }
155
+ });
156
+ // POST /api/planner/:id/convert-to-todo - convert to TODO
157
+ router.post('/planner/:id/convert-to-todo', (req, res) => {
158
+ try {
159
+ const item = queries.getPlannerItemById(req.params.id);
160
+ if (!item) {
161
+ res.status(404).json({ error: 'Planner item not found' });
162
+ return;
163
+ }
164
+ if (item.status === 'moved') {
165
+ res.status(400).json({ error: 'Item already converted' });
166
+ return;
167
+ }
168
+ const { cli_tool, cli_model, max_turns } = req.body;
169
+ const todo = queries.createTodo(item.project_id, item.title, item.description ?? undefined, item.priority, cli_tool, cli_model, undefined, undefined, max_turns);
170
+ // Copy planner images to todo
171
+ if (item.images) {
172
+ const plannerImagePaths = getPlannerImagePaths(item.id);
173
+ if (plannerImagePaths.length > 0) {
174
+ const todoDir = path.resolve(process.cwd(), 'data', 'uploads', todo.id);
175
+ if (!fs.existsSync(todoDir))
176
+ fs.mkdirSync(todoDir, { recursive: true });
177
+ for (const { filename, filePath } of plannerImagePaths) {
178
+ fs.copyFileSync(filePath, path.join(todoDir, filename));
179
+ }
180
+ queries.updateTodo(todo.id, { images: item.images });
181
+ }
182
+ }
183
+ const updatedItem = queries.updatePlannerItem(req.params.id, {
184
+ status: 'moved', converted_type: 'todo', converted_id: todo.id,
185
+ });
186
+ const updatedTodo = queries.getTodoById(todo.id);
187
+ res.status(201).json({ plannerItem: updatedItem, todo: updatedTodo });
188
+ }
189
+ catch (err) {
190
+ const message = err instanceof Error ? err.message : 'Unknown error';
191
+ res.status(500).json({ error: message });
192
+ }
193
+ });
194
+ // POST /api/planner/:id/convert-to-schedule - convert to schedule
195
+ router.post('/planner/:id/convert-to-schedule', (req, res) => {
196
+ try {
197
+ const item = queries.getPlannerItemById(req.params.id);
198
+ if (!item) {
199
+ res.status(404).json({ error: 'Planner item not found' });
200
+ return;
201
+ }
202
+ if (item.status === 'moved') {
203
+ res.status(400).json({ error: 'Item already converted' });
204
+ return;
205
+ }
206
+ const { cron_expression, schedule_type, run_at, cli_tool, cli_model } = req.body;
207
+ const isOnce = schedule_type === 'once';
208
+ if (isOnce && !run_at) {
209
+ res.status(400).json({ error: 'run_at is required for one-time schedules' });
210
+ return;
211
+ }
212
+ if (!isOnce && !cron_expression) {
213
+ res.status(400).json({ error: 'cron_expression is required for recurring schedules' });
214
+ return;
215
+ }
216
+ const schedule = queries.createSchedule(item.project_id, item.title, item.description ?? undefined, isOnce ? '* * * * *' : cron_expression, cli_tool, cli_model, 1, isOnce ? 'once' : 'recurring', isOnce ? run_at : undefined);
217
+ const updatedItem = queries.updatePlannerItem(req.params.id, {
218
+ status: 'moved', converted_type: 'schedule', converted_id: schedule.id,
219
+ });
220
+ res.status(201).json({ plannerItem: updatedItem, schedule });
221
+ }
222
+ catch (err) {
223
+ const message = err instanceof Error ? err.message : 'Unknown error';
224
+ res.status(500).json({ error: message });
225
+ }
226
+ });
227
+ // GET /api/projects/:id/planner/export - download planner as JSON
228
+ router.get('/projects/:id/planner/export', (req, res) => {
229
+ try {
230
+ const project = queries.getProjectById(req.params.id);
231
+ if (!project) {
232
+ res.status(404).json({ error: 'Project not found' });
233
+ return;
234
+ }
235
+ const allItems = queries.getPlannerItemsByProjectId(req.params.id);
236
+ const tags = queries.getPlannerTagsByProjectId(req.params.id);
237
+ const items = allItems
238
+ .filter((item) => item.status !== 'moved')
239
+ .map((item) => {
240
+ let parsedTags = [];
241
+ if (item.tags) {
242
+ try {
243
+ const parsed = JSON.parse(item.tags);
244
+ if (Array.isArray(parsed))
245
+ parsedTags = parsed.filter((t) => typeof t === 'string');
246
+ }
247
+ catch { /* ignore */ }
248
+ }
249
+ return {
250
+ title: item.title,
251
+ description: item.description,
252
+ tags: parsedTags,
253
+ due_date: item.due_date,
254
+ status: item.status,
255
+ priority: item.priority,
256
+ };
257
+ });
258
+ const payload = {
259
+ version: 1,
260
+ exported_at: new Date().toISOString(),
261
+ project_name: project.name,
262
+ items,
263
+ tags: tags.map((t) => ({ name: t.name, color: t.color })),
264
+ };
265
+ const datePart = new Date().toISOString().slice(0, 10).replace(/-/g, '');
266
+ const filename = `planner-${sanitizeFilenamePart(project.name)}-${datePart}.json`;
267
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
268
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
269
+ res.send(JSON.stringify(payload, null, 2));
270
+ }
271
+ catch (err) {
272
+ const message = err instanceof Error ? err.message : 'Unknown error';
273
+ res.status(500).json({ error: message });
274
+ }
275
+ });
276
+ // POST /api/projects/:id/planner/import - import planner JSON
277
+ router.post('/projects/:id/planner/import', (req, res) => {
278
+ try {
279
+ const project = queries.getProjectById(req.params.id);
280
+ if (!project) {
281
+ res.status(404).json({ error: 'Project not found' });
282
+ return;
283
+ }
284
+ const payload = req.body;
285
+ if (!payload || typeof payload !== 'object') {
286
+ res.status(400).json({ error: 'Invalid payload' });
287
+ return;
288
+ }
289
+ if (payload.version !== 1) {
290
+ res.status(400).json({ error: `Unsupported version: ${payload.version}` });
291
+ return;
292
+ }
293
+ if (!Array.isArray(payload.items)) {
294
+ res.status(400).json({ error: 'items must be an array' });
295
+ return;
296
+ }
297
+ for (let i = 0; i < payload.items.length; i++) {
298
+ const item = payload.items[i];
299
+ if (!item || typeof item.title !== 'string' || !item.title.trim()) {
300
+ res.status(400).json({ error: `items[${i}].title is required` });
301
+ return;
302
+ }
303
+ }
304
+ const existingTagNames = new Set(queries.getPlannerTagsByProjectId(req.params.id).map((t) => t.name));
305
+ const db = getDatabase();
306
+ let importedItems = 0;
307
+ let importedTags = 0;
308
+ const runImport = db.transaction(() => {
309
+ if (Array.isArray(payload.tags)) {
310
+ for (const tag of payload.tags) {
311
+ if (!tag || typeof tag.name !== 'string' || !tag.name.trim())
312
+ continue;
313
+ if (existingTagNames.has(tag.name))
314
+ continue;
315
+ const color = typeof tag.color === 'string' && tag.color ? tag.color : 'blue';
316
+ queries.upsertPlannerTag(req.params.id, tag.name, color);
317
+ existingTagNames.add(tag.name);
318
+ importedTags++;
319
+ }
320
+ }
321
+ for (const item of payload.items) {
322
+ const tagsArr = Array.isArray(item.tags) ? item.tags.filter((t) => typeof t === 'string') : [];
323
+ const rawStatus = typeof item.status === 'string' ? item.status : 'pending';
324
+ const status = ALLOWED_IMPORT_STATUSES.has(rawStatus) ? rawStatus : 'pending';
325
+ const priority = typeof item.priority === 'number' ? item.priority : 0;
326
+ const description = typeof item.description === 'string' ? item.description : undefined;
327
+ const dueDate = typeof item.due_date === 'string' ? item.due_date : undefined;
328
+ const created = queries.createPlannerItem(req.params.id, item.title, description, tagsArr.length > 0 ? JSON.stringify(tagsArr) : undefined, dueDate, priority);
329
+ if (status !== 'pending') {
330
+ queries.updatePlannerItem(created.id, { status });
331
+ }
332
+ importedItems++;
333
+ }
334
+ });
335
+ runImport();
336
+ res.status(200).json({ imported_items: importedItems, imported_tags: importedTags });
337
+ }
338
+ catch (err) {
339
+ const message = err instanceof Error ? err.message : 'Unknown error';
340
+ res.status(500).json({ error: message });
341
+ }
342
+ });
343
+ export default router;
344
+ //# sourceMappingURL=planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/server/routes/planner.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/server/routes/projects.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAylBxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../../src/server/routes/projects.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAiuBxB,eAAe,MAAM,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { Router } from 'express';
2
2
  import nodePath from 'path';
3
3
  import fs from 'fs';
4
- import { execFileSync } from 'child_process';
4
+ import { execFileSync, exec } from 'child_process';
5
5
  import os from 'os';
6
6
  import { createProject, getAllProjects, getProjectById, updateProject, deleteProject, syncProjectCliDefaults } from '../db/queries.js';
7
7
  import { worktreeManager } from '../services/worktree-manager.js';
8
+ import { cleanupProjectImages } from './images.js';
8
9
  const router = Router();
9
10
  /**
10
11
  * Validate project path: must be absolute, exist, be a directory,
@@ -111,6 +112,34 @@ router.post('/browse', (req, res) => {
111
112
  res.json({ path: null });
112
113
  }
113
114
  });
115
+ // POST /api/projects/open-folder - open folder in OS file explorer
116
+ router.post('/open-folder', (req, res) => {
117
+ const { path: folderPath } = req.body;
118
+ if (!folderPath || typeof folderPath !== 'string') {
119
+ res.status(400).json({ error: 'path is required' });
120
+ return;
121
+ }
122
+ const resolved = nodePath.resolve(folderPath);
123
+ if (!fs.existsSync(resolved)) {
124
+ res.status(404).json({ error: 'path does not exist' });
125
+ return;
126
+ }
127
+ try {
128
+ if (process.platform === 'win32') {
129
+ exec(`explorer.exe "${resolved}"`);
130
+ }
131
+ else if (process.platform === 'darwin') {
132
+ exec(`open "${resolved}"`);
133
+ }
134
+ else {
135
+ exec(`xdg-open "${resolved}"`);
136
+ }
137
+ res.json({ ok: true });
138
+ }
139
+ catch {
140
+ res.status(500).json({ error: 'failed to open folder' });
141
+ }
142
+ });
114
143
  // POST /api/projects - create project
115
144
  router.post('/', async (req, res) => {
116
145
  try {
@@ -197,6 +226,8 @@ router.put('/:id', (req, res) => {
197
226
  // DELETE /api/projects/:id - delete project
198
227
  router.delete('/:id', (req, res) => {
199
228
  try {
229
+ // Clean up image files before CASCADE deletes DB rows
230
+ cleanupProjectImages(req.params.id);
200
231
  const deleted = deleteProject(req.params.id);
201
232
  if (!deleted) {
202
233
  res.status(404).json({ error: 'Project not found' });
@@ -334,6 +365,60 @@ router.get('/:id/git-refs', async (req, res) => {
334
365
  res.status(500).json({ error: message });
335
366
  }
336
367
  });
368
+ // GET /api/projects/:id/worktrees - list git worktrees
369
+ router.get('/:id/worktrees', async (req, res) => {
370
+ try {
371
+ const project = getProjectById(req.params.id);
372
+ if (!project) {
373
+ res.status(404).json({ error: 'Project not found' });
374
+ return;
375
+ }
376
+ if (!project.is_git_repo) {
377
+ res.status(400).json({ error: 'Not a git repository' });
378
+ return;
379
+ }
380
+ const worktrees = await worktreeManager.listWorktrees(project.path);
381
+ // Exclude the main worktree (same as project path)
382
+ const filtered = worktrees.filter(w => nodePath.resolve(w.path) !== nodePath.resolve(project.path));
383
+ res.json({ worktrees: filtered });
384
+ }
385
+ catch (err) {
386
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
387
+ }
388
+ });
389
+ // POST /api/projects/:id/worktree-cleanup - remove a worktree and its branch
390
+ router.post('/:id/worktree-cleanup', async (req, res) => {
391
+ try {
392
+ const project = getProjectById(req.params.id);
393
+ if (!project) {
394
+ res.status(404).json({ error: 'Project not found' });
395
+ return;
396
+ }
397
+ if (!project.is_git_repo) {
398
+ res.status(400).json({ error: 'Not a git repository' });
399
+ return;
400
+ }
401
+ const { worktreePath, branchName } = req.body;
402
+ if (!worktreePath && !branchName) {
403
+ res.status(400).json({ error: 'worktreePath or branchName required' });
404
+ return;
405
+ }
406
+ // Validate worktree path is under .worktrees
407
+ if (worktreePath) {
408
+ const resolved = nodePath.resolve(worktreePath);
409
+ const worktreeBase = nodePath.resolve(project.path, '.worktrees');
410
+ if (!resolved.startsWith(worktreeBase + nodePath.sep) && resolved !== worktreeBase) {
411
+ res.status(400).json({ error: 'Invalid worktree path' });
412
+ return;
413
+ }
414
+ }
415
+ const result = await worktreeManager.cleanupWorktree(project.path, worktreePath || '', branchName || '');
416
+ res.json({ success: true, ...result });
417
+ }
418
+ catch (err) {
419
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
420
+ }
421
+ });
337
422
  // --- Git action helpers ---
338
423
  function getProjectGitPath(req, res) {
339
424
  const project = getProjectById(req.params.id);
@@ -515,6 +600,42 @@ router.post('/:id/git-merge', async (req, res) => {
515
600
  res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
516
601
  }
517
602
  });
603
+ // POST /api/projects/:id/git-branch-rename
604
+ router.post('/:id/git-branch-rename', async (req, res) => {
605
+ try {
606
+ const dirPath = getProjectGitPath(req, res);
607
+ if (!dirPath)
608
+ return;
609
+ const { oldName, newName } = req.body;
610
+ if (!oldName || typeof oldName !== 'string' || !newName || typeof newName !== 'string') {
611
+ res.status(400).json({ error: 'oldName and newName are required' });
612
+ return;
613
+ }
614
+ await worktreeManager.gitRenameBranch(dirPath, oldName.trim(), newName.trim());
615
+ res.json({ ok: true });
616
+ }
617
+ catch (err) {
618
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
619
+ }
620
+ });
621
+ // POST /api/projects/:id/git-rebase
622
+ router.post('/:id/git-rebase', async (req, res) => {
623
+ try {
624
+ const dirPath = getProjectGitPath(req, res);
625
+ if (!dirPath)
626
+ return;
627
+ const { onto } = req.body;
628
+ if (!onto || typeof onto !== 'string') {
629
+ res.status(400).json({ error: 'onto is required' });
630
+ return;
631
+ }
632
+ const result = await worktreeManager.gitRebase(dirPath, onto.trim());
633
+ res.json({ ok: true, result });
634
+ }
635
+ catch (err) {
636
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
637
+ }
638
+ });
518
639
  // POST /api/projects/:id/git-stash
519
640
  router.post('/:id/git-stash', async (req, res) => {
520
641
  try {
@@ -615,6 +736,43 @@ router.post('/:id/git-tag-delete', async (req, res) => {
615
736
  res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
616
737
  }
617
738
  });
739
+ // GET /api/projects/:id/git-commit-files - get files changed in a specific commit
740
+ router.get('/:id/git-commit-files', async (req, res) => {
741
+ try {
742
+ const dirPath = getProjectGitPath(req, res);
743
+ if (!dirPath)
744
+ return;
745
+ const hash = req.query.hash;
746
+ if (!hash || !/^[0-9a-f]{4,40}$/i.test(hash)) {
747
+ res.status(400).json({ error: 'Valid commit hash is required' });
748
+ return;
749
+ }
750
+ const files = await worktreeManager.getCommitFiles(dirPath, hash);
751
+ res.json({ files });
752
+ }
753
+ catch (err) {
754
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
755
+ }
756
+ });
757
+ // GET /api/projects/:id/git-commit-diff - get diff for a specific commit
758
+ router.get('/:id/git-commit-diff', async (req, res) => {
759
+ try {
760
+ const dirPath = getProjectGitPath(req, res);
761
+ if (!dirPath)
762
+ return;
763
+ const hash = req.query.hash;
764
+ if (!hash || !/^[0-9a-f]{4,40}$/i.test(hash)) {
765
+ res.status(400).json({ error: 'Valid commit hash is required' });
766
+ return;
767
+ }
768
+ const file = req.query.file;
769
+ const diff = await worktreeManager.getCommitDiff(dirPath, hash, file);
770
+ res.json({ diff });
771
+ }
772
+ catch (err) {
773
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Unknown error' });
774
+ }
775
+ });
618
776
  // GET /api/projects/:id/git-diff
619
777
  router.get('/:id/git-diff', async (req, res) => {
620
778
  try {