@sudocode-ai/local-server 0.1.0 → 0.1.1

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 (115) hide show
  1. package/dist/cli.js +6 -104
  2. package/dist/cli.js.map +1 -7
  3. package/dist/execution/engine/engine.js +10 -0
  4. package/dist/execution/engine/engine.js.map +1 -0
  5. package/dist/execution/engine/simple-engine.js +611 -0
  6. package/dist/execution/engine/simple-engine.js.map +1 -0
  7. package/dist/execution/engine/types.js +10 -0
  8. package/dist/execution/engine/types.js.map +1 -0
  9. package/dist/execution/output/ag-ui-adapter.js +438 -0
  10. package/dist/execution/output/ag-ui-adapter.js.map +1 -0
  11. package/dist/execution/output/ag-ui-integration.js +96 -0
  12. package/dist/execution/output/ag-ui-integration.js.map +1 -0
  13. package/dist/execution/output/claude-code-output-processor.js +769 -0
  14. package/dist/execution/output/claude-code-output-processor.js.map +1 -0
  15. package/dist/execution/output/index.js +15 -0
  16. package/dist/execution/output/index.js.map +1 -0
  17. package/dist/execution/output/types.js +22 -0
  18. package/dist/execution/output/types.js.map +1 -0
  19. package/dist/execution/process/builders/claude.js +59 -0
  20. package/dist/execution/process/builders/claude.js.map +1 -0
  21. package/dist/execution/process/index.js +15 -0
  22. package/dist/execution/process/index.js.map +1 -0
  23. package/dist/execution/process/manager.js +10 -0
  24. package/dist/execution/process/manager.js.map +1 -0
  25. package/dist/execution/process/simple-manager.js +336 -0
  26. package/dist/execution/process/simple-manager.js.map +1 -0
  27. package/dist/execution/process/types.js +10 -0
  28. package/dist/execution/process/types.js.map +1 -0
  29. package/dist/execution/process/utils.js +97 -0
  30. package/dist/execution/process/utils.js.map +1 -0
  31. package/dist/execution/resilience/circuit-breaker.js +291 -0
  32. package/dist/execution/resilience/circuit-breaker.js.map +1 -0
  33. package/dist/execution/resilience/executor.js +10 -0
  34. package/dist/execution/resilience/executor.js.map +1 -0
  35. package/dist/execution/resilience/index.js +15 -0
  36. package/dist/execution/resilience/index.js.map +1 -0
  37. package/dist/execution/resilience/resilient-executor.js +261 -0
  38. package/dist/execution/resilience/resilient-executor.js.map +1 -0
  39. package/dist/execution/resilience/retry.js +234 -0
  40. package/dist/execution/resilience/retry.js.map +1 -0
  41. package/dist/execution/resilience/types.js +30 -0
  42. package/dist/execution/resilience/types.js.map +1 -0
  43. package/dist/execution/transport/event-buffer.js +208 -0
  44. package/dist/execution/transport/event-buffer.js.map +1 -0
  45. package/dist/execution/transport/index.js +10 -0
  46. package/dist/execution/transport/index.js.map +1 -0
  47. package/dist/execution/transport/sse-transport.js +282 -0
  48. package/dist/execution/transport/sse-transport.js.map +1 -0
  49. package/dist/execution/transport/transport-manager.js +231 -0
  50. package/dist/execution/transport/transport-manager.js.map +1 -0
  51. package/dist/execution/workflow/index.js +13 -0
  52. package/dist/execution/workflow/index.js.map +1 -0
  53. package/dist/execution/workflow/linear-orchestrator.js +683 -0
  54. package/dist/execution/workflow/linear-orchestrator.js.map +1 -0
  55. package/dist/execution/workflow/memory-storage.js +68 -0
  56. package/dist/execution/workflow/memory-storage.js.map +1 -0
  57. package/dist/execution/workflow/orchestrator.js +9 -0
  58. package/dist/execution/workflow/orchestrator.js.map +1 -0
  59. package/dist/execution/workflow/types.js +9 -0
  60. package/dist/execution/workflow/types.js.map +1 -0
  61. package/dist/execution/workflow/utils.js +152 -0
  62. package/dist/execution/workflow/utils.js.map +1 -0
  63. package/dist/execution/worktree/config.js +280 -0
  64. package/dist/execution/worktree/config.js.map +1 -0
  65. package/dist/execution/worktree/git-cli.js +189 -0
  66. package/dist/execution/worktree/git-cli.js.map +1 -0
  67. package/dist/execution/worktree/index.js +15 -0
  68. package/dist/execution/worktree/index.js.map +1 -0
  69. package/dist/execution/worktree/manager.js +452 -0
  70. package/dist/execution/worktree/manager.js.map +1 -0
  71. package/dist/execution/worktree/types.js +42 -0
  72. package/dist/execution/worktree/types.js.map +1 -0
  73. package/dist/index.js +356 -104
  74. package/dist/index.js.map +1 -7
  75. package/dist/routes/executions-stream.js +55 -0
  76. package/dist/routes/executions-stream.js.map +1 -0
  77. package/dist/routes/executions.js +267 -0
  78. package/dist/routes/executions.js.map +1 -0
  79. package/dist/routes/feedback.js +329 -0
  80. package/dist/routes/feedback.js.map +1 -0
  81. package/dist/routes/issues.js +280 -0
  82. package/dist/routes/issues.js.map +1 -0
  83. package/dist/routes/relationships.js +308 -0
  84. package/dist/routes/relationships.js.map +1 -0
  85. package/dist/routes/specs.js +270 -0
  86. package/dist/routes/specs.js.map +1 -0
  87. package/dist/services/db.js +85 -0
  88. package/dist/services/db.js.map +1 -0
  89. package/dist/services/execution-lifecycle.js +286 -0
  90. package/dist/services/execution-lifecycle.js.map +1 -0
  91. package/dist/services/execution-service.js +676 -0
  92. package/dist/services/execution-service.js.map +1 -0
  93. package/dist/services/executions.js +164 -0
  94. package/dist/services/executions.js.map +1 -0
  95. package/dist/services/export.js +106 -0
  96. package/dist/services/export.js.map +1 -0
  97. package/dist/services/feedback.js +54 -0
  98. package/dist/services/feedback.js.map +1 -0
  99. package/dist/services/issues.js +35 -0
  100. package/dist/services/issues.js.map +1 -0
  101. package/dist/services/prompt-template-engine.js +212 -0
  102. package/dist/services/prompt-template-engine.js.map +1 -0
  103. package/dist/services/prompt-templates.js +236 -0
  104. package/dist/services/prompt-templates.js.map +1 -0
  105. package/dist/services/relationships.js +42 -0
  106. package/dist/services/relationships.js.map +1 -0
  107. package/dist/services/specs.js +35 -0
  108. package/dist/services/specs.js.map +1 -0
  109. package/dist/services/watcher.js +69 -0
  110. package/dist/services/watcher.js.map +1 -0
  111. package/dist/services/websocket.js +389 -0
  112. package/dist/services/websocket.js.map +1 -0
  113. package/dist/utils/sudocode-dir.js +9 -0
  114. package/dist/utils/sudocode-dir.js.map +1 -0
  115. package/package.json +4 -6
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Specs API routes (mapped to /api/specs)
3
+ */
4
+ import { Router } from "express";
5
+ import { getAllSpecs, getSpecById, createNewSpec, updateExistingSpec, deleteExistingSpec, } from "../services/specs.js";
6
+ import { generateSpecId } from "@sudocode-ai/cli/dist/id-generator.js";
7
+ import { broadcastSpecUpdate } from "../services/websocket.js";
8
+ import { getSudocodeDir } from "../utils/sudocode-dir.js";
9
+ import { triggerExport, syncEntityToMarkdown } from "../services/export.js";
10
+ import * as path from "path";
11
+ export function createSpecsRouter(db) {
12
+ const router = Router();
13
+ /**
14
+ * GET /api/specs - List all specs
15
+ */
16
+ router.get("/", (req, res) => {
17
+ try {
18
+ // Parse query parameters for filtering
19
+ const options = {};
20
+ if (req.query.priority) {
21
+ options.priority = parseInt(req.query.priority, 10);
22
+ }
23
+ // Default to excluding archived unless explicitly specified
24
+ options.archived =
25
+ req.query.archived !== undefined
26
+ ? req.query.archived === "true"
27
+ : false;
28
+ if (req.query.limit) {
29
+ options.limit = parseInt(req.query.limit, 10);
30
+ }
31
+ if (req.query.offset) {
32
+ options.offset = parseInt(req.query.offset, 10);
33
+ }
34
+ const specs = getAllSpecs(db, options);
35
+ res.json({
36
+ success: true,
37
+ data: specs,
38
+ });
39
+ }
40
+ catch (error) {
41
+ console.error("Error listing specs:", error);
42
+ res.status(500).json({
43
+ success: false,
44
+ data: null,
45
+ error_data: error instanceof Error ? error.message : String(error),
46
+ message: "Failed to list specs",
47
+ });
48
+ }
49
+ });
50
+ /**
51
+ * GET /api/specs/:id - Get a specific spec
52
+ */
53
+ router.get("/:id", (req, res) => {
54
+ try {
55
+ const { id } = req.params;
56
+ const spec = getSpecById(db, id);
57
+ if (!spec) {
58
+ res.status(404).json({
59
+ success: false,
60
+ data: null,
61
+ message: `Spec not found: ${id}`,
62
+ });
63
+ return;
64
+ }
65
+ res.json({
66
+ success: true,
67
+ data: spec,
68
+ });
69
+ }
70
+ catch (error) {
71
+ console.error("Error getting spec:", error);
72
+ res.status(500).json({
73
+ success: false,
74
+ data: null,
75
+ error_data: error instanceof Error ? error.message : String(error),
76
+ message: "Failed to get spec",
77
+ });
78
+ }
79
+ });
80
+ /**
81
+ * POST /api/specs - Create a new spec
82
+ */
83
+ router.post("/", (req, res) => {
84
+ try {
85
+ const { title, content, priority, parent_id } = req.body;
86
+ // Validate required fields
87
+ if (!title || typeof title !== "string") {
88
+ res.status(400).json({
89
+ success: false,
90
+ data: null,
91
+ message: "Title is required and must be a string",
92
+ });
93
+ return;
94
+ }
95
+ if (title.length > 500) {
96
+ res.status(400).json({
97
+ success: false,
98
+ data: null,
99
+ message: "Title must be 500 characters or less",
100
+ });
101
+ return;
102
+ }
103
+ // Generate new spec ID
104
+ const outputDir = getSudocodeDir();
105
+ const id = generateSpecId(db, outputDir);
106
+ // Generate file path for the spec
107
+ const file_path = path.join(outputDir, "specs", `${id}.md`);
108
+ // Create spec using CLI operation
109
+ const spec = createNewSpec(db, {
110
+ id,
111
+ title,
112
+ file_path,
113
+ content: content || "",
114
+ priority: priority !== undefined ? priority : 2,
115
+ parent_id: parent_id || undefined,
116
+ });
117
+ // Trigger export to JSONL files
118
+ triggerExport(db);
119
+ // Sync this specific spec to its markdown file (don't wait for it)
120
+ syncEntityToMarkdown(db, spec.id, "spec").catch((error) => {
121
+ console.error(`Failed to sync spec ${spec.id} to markdown:`, error);
122
+ });
123
+ // Broadcast spec creation to WebSocket clients
124
+ broadcastSpecUpdate(spec.id, "created", spec);
125
+ res.status(201).json({
126
+ success: true,
127
+ data: spec,
128
+ });
129
+ }
130
+ catch (error) {
131
+ console.error("Error creating spec:", error);
132
+ res.status(500).json({
133
+ success: false,
134
+ data: null,
135
+ error_data: error instanceof Error ? error.message : String(error),
136
+ message: "Failed to create spec",
137
+ });
138
+ }
139
+ });
140
+ /**
141
+ * PUT /api/specs/:id - Update an existing spec
142
+ */
143
+ router.put("/:id", (req, res) => {
144
+ try {
145
+ const { id } = req.params;
146
+ const { title, content, priority, parent_id, archived } = req.body;
147
+ // Validate that at least one field is provided
148
+ if (title === undefined &&
149
+ content === undefined &&
150
+ priority === undefined &&
151
+ parent_id === undefined &&
152
+ archived === undefined) {
153
+ res.status(400).json({
154
+ success: false,
155
+ data: null,
156
+ message: "At least one field must be provided for update",
157
+ });
158
+ return;
159
+ }
160
+ // Validate title length if provided
161
+ if (title !== undefined &&
162
+ typeof title === "string" &&
163
+ title.length > 500) {
164
+ res.status(400).json({
165
+ success: false,
166
+ data: null,
167
+ message: "Title must be 500 characters or less",
168
+ });
169
+ return;
170
+ }
171
+ // Build update input
172
+ const updateInput = {};
173
+ if (title !== undefined)
174
+ updateInput.title = title;
175
+ if (content !== undefined)
176
+ updateInput.content = content;
177
+ if (priority !== undefined)
178
+ updateInput.priority = priority;
179
+ if (parent_id !== undefined)
180
+ updateInput.parent_id = parent_id;
181
+ if (archived !== undefined) {
182
+ updateInput.archived = archived;
183
+ updateInput.archived_at = archived ? new Date().toISOString() : null;
184
+ }
185
+ // Update spec using CLI operation
186
+ const spec = updateExistingSpec(db, id, updateInput);
187
+ // Trigger export to JSONL files
188
+ triggerExport(db);
189
+ // Sync this specific spec to its markdown file (don't wait for it)
190
+ syncEntityToMarkdown(db, spec.id, "spec").catch((error) => {
191
+ console.error(`Failed to sync spec ${spec.id} to markdown:`, error);
192
+ });
193
+ // Broadcast spec update to WebSocket clients
194
+ broadcastSpecUpdate(spec.id, "updated", spec);
195
+ res.json({
196
+ success: true,
197
+ data: spec,
198
+ });
199
+ }
200
+ catch (error) {
201
+ console.error("Error updating spec:", error);
202
+ // Handle "not found" errors
203
+ if (error instanceof Error && error.message.includes("not found")) {
204
+ res.status(404).json({
205
+ success: false,
206
+ data: null,
207
+ message: error.message,
208
+ });
209
+ return;
210
+ }
211
+ res.status(500).json({
212
+ success: false,
213
+ data: null,
214
+ error_data: error instanceof Error ? error.message : String(error),
215
+ message: "Failed to update spec",
216
+ });
217
+ }
218
+ });
219
+ /**
220
+ * DELETE /api/specs/:id - Delete a spec
221
+ */
222
+ router.delete("/:id", (req, res) => {
223
+ try {
224
+ const { id } = req.params;
225
+ // Check if spec exists first
226
+ const existingSpec = getSpecById(db, id);
227
+ if (!existingSpec) {
228
+ res.status(404).json({
229
+ success: false,
230
+ data: null,
231
+ message: `Spec not found: ${id}`,
232
+ });
233
+ return;
234
+ }
235
+ // Delete spec using CLI operation
236
+ const deleted = deleteExistingSpec(db, id);
237
+ if (deleted) {
238
+ // Trigger export to JSONL files
239
+ triggerExport(db);
240
+ // Broadcast spec deletion to WebSocket clients
241
+ broadcastSpecUpdate(id, "deleted", { id });
242
+ res.json({
243
+ success: true,
244
+ data: {
245
+ id,
246
+ deleted: true,
247
+ },
248
+ });
249
+ }
250
+ else {
251
+ res.status(500).json({
252
+ success: false,
253
+ data: null,
254
+ message: "Failed to delete spec",
255
+ });
256
+ }
257
+ }
258
+ catch (error) {
259
+ console.error("Error deleting spec:", error);
260
+ res.status(500).json({
261
+ success: false,
262
+ data: null,
263
+ error_data: error instanceof Error ? error.message : String(error),
264
+ message: "Failed to delete spec",
265
+ });
266
+ }
267
+ });
268
+ return router;
269
+ }
270
+ //# sourceMappingURL=specs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs.js","sourceRoot":"","sources":["../../src/routes/specs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Database service for sudocode server
3
+ * Uses shared schema from @sudocode-ai/types
4
+ */
5
+ import Database from "better-sqlite3";
6
+ import * as path from "path";
7
+ import * as fs from "fs";
8
+ import { EXECUTIONS_TABLE, EXECUTIONS_INDEXES, PROMPT_TEMPLATES_TABLE, PROMPT_TEMPLATES_INDEXES, EXECUTION_LOGS_TABLE, EXECUTION_LOGS_INDEXES, } from "@sudocode-ai/types/schema";
9
+ import { initializeDefaultTemplates } from "./prompt-templates.js";
10
+ /**
11
+ * Initialize database with CLI schema + server extensions
12
+ */
13
+ export function initDatabase(config) {
14
+ const { path: dbPath, readOnly = false } = config;
15
+ // Ensure directory exists
16
+ const dir = path.dirname(dbPath);
17
+ if (!fs.existsSync(dir)) {
18
+ fs.mkdirSync(dir, { recursive: true });
19
+ }
20
+ // Open database
21
+ const db = new Database(dbPath, {
22
+ readonly: readOnly,
23
+ fileMustExist: false,
24
+ });
25
+ // Don't modify schema if read-only
26
+ if (readOnly) {
27
+ return db;
28
+ }
29
+ // Configure database
30
+ db.pragma("journal_mode = WAL");
31
+ db.pragma("foreign_keys = ON");
32
+ db.pragma("synchronous = NORMAL");
33
+ db.pragma("temp_store = MEMORY");
34
+ // Create server-specific tables
35
+ db.exec(EXECUTIONS_TABLE);
36
+ db.exec(PROMPT_TEMPLATES_TABLE);
37
+ db.exec(EXECUTION_LOGS_TABLE);
38
+ // Create indexes
39
+ db.exec(EXECUTIONS_INDEXES);
40
+ db.exec(PROMPT_TEMPLATES_INDEXES);
41
+ db.exec(EXECUTION_LOGS_INDEXES);
42
+ // Initialize default prompt templates
43
+ initializeDefaultTemplates(db);
44
+ return db;
45
+ }
46
+ /**
47
+ * Check if database has CLI tables
48
+ */
49
+ export function hasCliTables(db) {
50
+ const result = db
51
+ .prepare(`
52
+ SELECT COUNT(*) as count
53
+ FROM sqlite_master
54
+ WHERE type='table'
55
+ AND name IN ('specs', 'issues', 'relationships', 'tags')
56
+ `)
57
+ .get();
58
+ return result.count === 4;
59
+ }
60
+ /**
61
+ * Get database info
62
+ */
63
+ export function getDatabaseInfo(db) {
64
+ const tables = db
65
+ .prepare(`
66
+ SELECT name
67
+ FROM sqlite_master
68
+ WHERE type='table'
69
+ ORDER BY name
70
+ `)
71
+ .all();
72
+ const version = db.prepare("PRAGMA user_version").get();
73
+ return {
74
+ tables: tables.map((t) => t.name),
75
+ version: version.user_version,
76
+ hasCliTables: hasCliTables(db),
77
+ };
78
+ }
79
+ /**
80
+ * Close database connection
81
+ */
82
+ export function closeDatabase(db) {
83
+ db.close();
84
+ }
85
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/services/db.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAUnE;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB;IACjD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC;IAElD,0BAA0B;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,gBAAgB;IAChB,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE;QAC9B,QAAQ,EAAE,QAAQ;QAClB,aAAa,EAAE,KAAK;KACrB,CAAC,CAAC;IAEH,mCAAmC;IACnC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,qBAAqB;IACrB,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAEjC,gCAAgC;IAChC,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC1B,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAChC,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAE9B,iBAAiB;IACjB,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC5B,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAClC,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAEhC,sCAAsC;IACtC,0BAA0B,CAAC,EAAE,CAAC,CAAC;IAE/B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,EAAqB;IAChD,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CACN;;;;;GAKH,CACE;SACA,GAAG,EAAuB,CAAC;IAE9B,OAAO,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,EAAqB;IACnD,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CACN;;;;;GAKH,CACE;SACA,GAAG,EAAwB,CAAC;IAE/B,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAEpD,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,OAAO,EAAE,OAAO,CAAC,YAAY;QAC7B,YAAY,EAAE,YAAY,CAAC,EAAE,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,EAAqB;IACjD,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC"}
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Execution Lifecycle Service
3
+ *
4
+ * Centralized service for managing execution lifecycle with worktree integration.
5
+ * Coordinates between WorktreeManager and execution database services.
6
+ *
7
+ * @module services/execution-lifecycle
8
+ */
9
+ import path from "path";
10
+ import { WorktreeManager, } from "../execution/worktree/manager.js";
11
+ import { getWorktreeConfig } from "../execution/worktree/config.js";
12
+ import { createExecution, getExecution } from "./executions.js";
13
+ import { randomUUID } from "crypto";
14
+ /**
15
+ * ExecutionLifecycleService
16
+ *
17
+ * Manages the full lifecycle of executions with worktree support:
18
+ * - Creating executions with isolated worktrees
19
+ * - Cleaning up executions and associated worktrees
20
+ * - Handling orphaned worktrees
21
+ */
22
+ export class ExecutionLifecycleService {
23
+ worktreeManager;
24
+ db;
25
+ repoPath;
26
+ /**
27
+ * Create a new ExecutionLifecycleService
28
+ *
29
+ * @param db - Database instance
30
+ * @param repoPath - Path to the git repository
31
+ * @param worktreeManager - Optional worktree manager (defaults to new instance)
32
+ */
33
+ constructor(db, repoPath, worktreeManager) {
34
+ this.db = db;
35
+ this.repoPath = repoPath;
36
+ // Load config and create worktree manager if not provided
37
+ if (worktreeManager) {
38
+ this.worktreeManager = worktreeManager;
39
+ }
40
+ else {
41
+ const config = getWorktreeConfig(repoPath);
42
+ this.worktreeManager = new WorktreeManager(config);
43
+ }
44
+ }
45
+ /**
46
+ * Create an execution with an isolated worktree
47
+ *
48
+ * Creates a worktree first, then creates the execution record.
49
+ * If worktree creation fails, no execution is created.
50
+ * If execution creation fails, the worktree is cleaned up.
51
+ *
52
+ * @param params - Execution creation parameters
53
+ * @returns Execution with worktree information
54
+ * @throws Error if creation fails
55
+ */
56
+ async createExecutionWithWorktree(params) {
57
+ const { issueId, issueTitle, agentType, targetBranch, repoPath } = params;
58
+ // Validation 1: Check for existing active execution for this issue
59
+ const existingExecution = this.db
60
+ .prepare(`SELECT id FROM executions
61
+ WHERE issue_id = ?
62
+ AND status = 'running'
63
+ AND worktree_path IS NOT NULL`)
64
+ .get(issueId);
65
+ if (existingExecution) {
66
+ throw new Error(`Active execution already exists for issue ${issueId}: ${existingExecution.id}`);
67
+ }
68
+ // Validation 2: Validate git repository
69
+ const isValidRepo = await this.worktreeManager.isValidRepo(repoPath);
70
+ if (!isValidRepo) {
71
+ throw new Error(`Not a git repository: ${repoPath}`);
72
+ }
73
+ // Validation 3: Validate target branch exists
74
+ const branches = await this.worktreeManager.listBranches(repoPath);
75
+ if (!branches.includes(targetBranch)) {
76
+ throw new Error(`Target branch does not exist: ${targetBranch}`);
77
+ }
78
+ const config = this.worktreeManager.getConfig();
79
+ // Generate execution ID
80
+ const executionId = randomUUID();
81
+ // Determine branch name based on autoCreateBranches setting
82
+ let branchName;
83
+ if (config.autoCreateBranches) {
84
+ // Generate branch name: {branchPrefix}/{execution-id}/{sanitized-issue-title}
85
+ const sanitizedTitle = sanitizeForBranchName(issueTitle);
86
+ branchName = `${config.branchPrefix}/${executionId.substring(0, 8)}/${sanitizedTitle}`;
87
+ }
88
+ else {
89
+ // Use target branch directly when not auto-creating branches
90
+ branchName = targetBranch;
91
+ }
92
+ // Generate worktree path: {repoPath}/{worktreeStoragePath}/{execution-id}
93
+ const worktreePath = path.join(repoPath, config.worktreeStoragePath, executionId);
94
+ let worktreeCreated = false;
95
+ try {
96
+ // Step 1: Create worktree
97
+ await this.worktreeManager.createWorktree({
98
+ repoPath,
99
+ branchName,
100
+ worktreePath,
101
+ baseBranch: targetBranch,
102
+ createBranch: config.autoCreateBranches,
103
+ });
104
+ worktreeCreated = true;
105
+ // Step 2: Create execution record in database
106
+ const execution = createExecution(this.db, {
107
+ id: executionId,
108
+ issue_id: issueId,
109
+ agent_type: agentType,
110
+ mode: params.mode,
111
+ prompt: params.prompt,
112
+ config: params.config,
113
+ target_branch: targetBranch,
114
+ branch_name: branchName,
115
+ worktree_path: worktreePath,
116
+ });
117
+ return {
118
+ execution,
119
+ worktreePath,
120
+ branchName,
121
+ };
122
+ }
123
+ catch (error) {
124
+ // If worktree was created but execution creation failed, cleanup worktree
125
+ if (worktreeCreated) {
126
+ try {
127
+ await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
128
+ }
129
+ catch (cleanupError) {
130
+ // Log cleanup error but throw original error
131
+ console.error(`Failed to cleanup worktree after execution creation failure:`, cleanupError);
132
+ }
133
+ }
134
+ // Re-throw the original error
135
+ throw error;
136
+ }
137
+ }
138
+ /**
139
+ * Check if an execution should be cleaned up based on its config
140
+ *
141
+ * @param executionId - ID of execution to check
142
+ * @returns true if should cleanup, false otherwise
143
+ */
144
+ shouldCleanupExecution(executionId) {
145
+ const execution = getExecution(this.db, executionId);
146
+ if (!execution) {
147
+ return false;
148
+ }
149
+ // Check if cleanupMode is set to 'manual'
150
+ if (execution.config) {
151
+ try {
152
+ const config = JSON.parse(execution.config);
153
+ if (config.cleanupMode === "manual" || config.cleanupMode === "never") {
154
+ return false;
155
+ }
156
+ }
157
+ catch (error) {
158
+ console.error(`Failed to parse execution config for ${executionId}:`, error);
159
+ // Default to cleanup on parse error
160
+ }
161
+ }
162
+ return true;
163
+ }
164
+ /**
165
+ * Clean up an execution and its associated worktree
166
+ *
167
+ * Removes the worktree from filesystem and git metadata.
168
+ * Branch deletion is controlled by autoDeleteBranches config.
169
+ * Respects the cleanupMode configuration from execution config.
170
+ *
171
+ * IMPORTANT: The worktree_path is NEVER cleared from the database.
172
+ * This allows follow-up executions to find and reuse the same worktree path.
173
+ * The filesystem worktree is deleted, but the path remains in the DB as a historical record.
174
+ *
175
+ * @param executionId - ID of execution to cleanup
176
+ * @throws Error if cleanup fails
177
+ */
178
+ async cleanupExecution(executionId) {
179
+ // Check if we should cleanup based on config
180
+ if (!this.shouldCleanupExecution(executionId)) {
181
+ return;
182
+ }
183
+ // Get execution from database
184
+ const execution = getExecution(this.db, executionId);
185
+ if (!execution) {
186
+ // Execution doesn't exist, nothing to cleanup
187
+ return;
188
+ }
189
+ // If execution has a worktree path, clean up the filesystem worktree
190
+ // but KEEP the worktree_path in the database for follow-up executions
191
+ if (execution.worktree_path) {
192
+ try {
193
+ await this.worktreeManager.cleanupWorktree(execution.worktree_path, this.repoPath);
194
+ // NOTE: We do NOT set worktree_path to null in the database
195
+ // Follow-up executions need this path to recreate the worktree
196
+ }
197
+ catch (error) {
198
+ // Log error but don't fail - cleanup is best-effort
199
+ console.error(`Failed to cleanup worktree for execution ${executionId}:`, error);
200
+ }
201
+ }
202
+ }
203
+ /**
204
+ * Clean up orphaned worktrees
205
+ *
206
+ * Finds worktrees that are registered in git but don't have
207
+ * corresponding execution records, or vice versa.
208
+ * Also cleans up worktrees for finished executions (completed/failed/stopped).
209
+ */
210
+ async cleanupOrphanedWorktrees() {
211
+ const repoPath = this.repoPath;
212
+ const config = this.worktreeManager.getConfig();
213
+ try {
214
+ // List all worktrees from git
215
+ const worktrees = await this.worktreeManager.listWorktrees(repoPath);
216
+ // Filter to worktrees in our storage path
217
+ const managedWorktrees = worktrees.filter((w) => w.path.includes(config.worktreeStoragePath));
218
+ // For each managed worktree, check if it has a corresponding execution
219
+ for (const worktree of managedWorktrees) {
220
+ const worktreePath = worktree.path;
221
+ // Try to extract execution ID from path
222
+ const executionId = path.basename(worktreePath);
223
+ // Check if execution exists in database
224
+ const execution = getExecution(this.db, executionId);
225
+ if (!execution) {
226
+ // Orphaned worktree - cleanup
227
+ console.log(`Cleaning up orphaned worktree: ${worktreePath} (no execution found)`);
228
+ try {
229
+ await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
230
+ }
231
+ catch (error) {
232
+ console.error(`Failed to cleanup orphaned worktree ${worktreePath}:`, error);
233
+ }
234
+ }
235
+ else if (execution.status === "completed" ||
236
+ execution.status === "failed" ||
237
+ execution.status === "stopped") {
238
+ // Execution is finished but worktree still exists
239
+ // Check if we should cleanup based on execution config
240
+ if (!this.shouldCleanupExecution(executionId)) {
241
+ console.log(`Skipping cleanup for finished execution ${executionId} (manual cleanup mode)`);
242
+ continue;
243
+ }
244
+ console.log(`Cleaning up worktree for finished execution ${executionId} (status: ${execution.status})`);
245
+ try {
246
+ await this.worktreeManager.cleanupWorktree(worktreePath, repoPath);
247
+ // NOTE: We do NOT set worktree_path to null in the database
248
+ // Follow-up executions need this path to recreate the worktree
249
+ }
250
+ catch (error) {
251
+ console.error(`Failed to cleanup worktree for finished execution ${executionId}:`, error);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ catch (error) {
257
+ console.error(`Failed to cleanup orphaned worktrees in ${repoPath}:`, error);
258
+ }
259
+ }
260
+ }
261
+ /**
262
+ * Sanitize a string to be safe for use in git branch names
263
+ *
264
+ * - Converts to lowercase
265
+ * - Replaces spaces and slashes with hyphens
266
+ * - Removes special characters
267
+ * - Limits length to 50 characters
268
+ *
269
+ * @param str - String to sanitize
270
+ * @returns Sanitized string safe for branch names
271
+ */
272
+ export function sanitizeForBranchName(str) {
273
+ return (str
274
+ .toLowerCase()
275
+ // Replace spaces and slashes with hyphens
276
+ .replace(/[\s/]+/g, "-")
277
+ // Remove special characters (keep alphanumeric, hyphens, underscores)
278
+ .replace(/[^a-z0-9\-_]/g, "")
279
+ // Remove consecutive hyphens
280
+ .replace(/-+/g, "-")
281
+ // Remove leading/trailing hyphens
282
+ .replace(/^-+|-+$/g, "")
283
+ // Limit length
284
+ .substring(0, 50));
285
+ }
286
+ //# sourceMappingURL=execution-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-lifecycle.js","sourceRoot":"","sources":["../../src/services/execution-lifecycle.ts"],"names":[],"mappings":"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"}