@sudocode-ai/local-server 0.1.7 → 0.1.9

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 (282) hide show
  1. package/README.md +6 -0
  2. package/dist/errors/agent-errors.d.ts +43 -0
  3. package/dist/errors/agent-errors.d.ts.map +1 -0
  4. package/dist/errors/agent-errors.js +69 -0
  5. package/dist/errors/agent-errors.js.map +1 -0
  6. package/dist/execution/adapters/claude-adapter.d.ts +63 -0
  7. package/dist/execution/adapters/claude-adapter.d.ts.map +1 -0
  8. package/dist/execution/adapters/claude-adapter.js +82 -0
  9. package/dist/execution/adapters/claude-adapter.js.map +1 -0
  10. package/dist/execution/adapters/codex-adapter.d.ts +67 -0
  11. package/dist/execution/adapters/codex-adapter.d.ts.map +1 -0
  12. package/dist/execution/adapters/codex-adapter.js +183 -0
  13. package/dist/execution/adapters/codex-adapter.js.map +1 -0
  14. package/dist/execution/adapters/codex-config-builder.d.ts +30 -0
  15. package/dist/execution/adapters/codex-config-builder.d.ts.map +1 -0
  16. package/dist/execution/adapters/codex-config-builder.js +110 -0
  17. package/dist/execution/adapters/codex-config-builder.js.map +1 -0
  18. package/dist/execution/adapters/copilot-adapter.d.ts +94 -0
  19. package/dist/execution/adapters/copilot-adapter.d.ts.map +1 -0
  20. package/dist/execution/adapters/copilot-adapter.js +163 -0
  21. package/dist/execution/adapters/copilot-adapter.js.map +1 -0
  22. package/dist/execution/adapters/copilot-config-builder.d.ts +48 -0
  23. package/dist/execution/adapters/copilot-config-builder.d.ts.map +1 -0
  24. package/dist/execution/adapters/copilot-config-builder.js +125 -0
  25. package/dist/execution/adapters/copilot-config-builder.js.map +1 -0
  26. package/dist/execution/adapters/cursor-adapter.d.ts +66 -0
  27. package/dist/execution/adapters/cursor-adapter.d.ts.map +1 -0
  28. package/dist/execution/adapters/cursor-adapter.js +121 -0
  29. package/dist/execution/adapters/cursor-adapter.js.map +1 -0
  30. package/dist/execution/adapters/cursor-config-builder.d.ts +29 -0
  31. package/dist/execution/adapters/cursor-config-builder.d.ts.map +1 -0
  32. package/dist/execution/adapters/cursor-config-builder.js +49 -0
  33. package/dist/execution/adapters/cursor-config-builder.js.map +1 -0
  34. package/dist/execution/adapters/shared/config-presets.d.ts +102 -0
  35. package/dist/execution/adapters/shared/config-presets.d.ts.map +1 -0
  36. package/dist/execution/adapters/shared/config-presets.js +205 -0
  37. package/dist/execution/adapters/shared/config-presets.js.map +1 -0
  38. package/dist/execution/adapters/shared/config-utils.d.ts +95 -0
  39. package/dist/execution/adapters/shared/config-utils.d.ts.map +1 -0
  40. package/dist/execution/adapters/shared/config-utils.js +163 -0
  41. package/dist/execution/adapters/shared/config-utils.js.map +1 -0
  42. package/dist/execution/adapters/shared/index.d.ts +8 -0
  43. package/dist/execution/adapters/shared/index.d.ts.map +1 -0
  44. package/dist/execution/adapters/shared/index.js +8 -0
  45. package/dist/execution/adapters/shared/index.js.map +1 -0
  46. package/dist/execution/executors/agent-executor-wrapper.d.ts +153 -0
  47. package/dist/execution/executors/agent-executor-wrapper.d.ts.map +1 -0
  48. package/dist/execution/executors/agent-executor-wrapper.js +652 -0
  49. package/dist/execution/executors/agent-executor-wrapper.js.map +1 -0
  50. package/dist/execution/executors/executor-factory.d.ts +95 -0
  51. package/dist/execution/executors/executor-factory.d.ts.map +1 -0
  52. package/dist/execution/executors/executor-factory.js +120 -0
  53. package/dist/execution/executors/executor-factory.js.map +1 -0
  54. package/dist/execution/output/ag-ui-adapter.d.ts +0 -2
  55. package/dist/execution/output/ag-ui-adapter.d.ts.map +1 -1
  56. package/dist/execution/output/ag-ui-adapter.js +0 -2
  57. package/dist/execution/output/ag-ui-adapter.js.map +1 -1
  58. package/dist/execution/output/index.d.ts +0 -3
  59. package/dist/execution/output/index.d.ts.map +1 -1
  60. package/dist/execution/output/index.js +0 -2
  61. package/dist/execution/output/index.js.map +1 -1
  62. package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts +108 -0
  63. package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts.map +1 -0
  64. package/dist/execution/output/normalized-to-ag-ui-adapter.js +321 -0
  65. package/dist/execution/output/normalized-to-ag-ui-adapter.js.map +1 -0
  66. package/dist/execution/process/builders/claude.d.ts +24 -57
  67. package/dist/execution/process/builders/claude.d.ts.map +1 -1
  68. package/dist/execution/process/builders/claude.js +153 -19
  69. package/dist/execution/process/builders/claude.js.map +1 -1
  70. package/dist/execution/transport/ipc-transport-manager.d.ts +74 -0
  71. package/dist/execution/transport/ipc-transport-manager.d.ts.map +1 -0
  72. package/dist/execution/transport/ipc-transport-manager.js +104 -0
  73. package/dist/execution/transport/ipc-transport-manager.js.map +1 -0
  74. package/dist/execution/transport/transport-manager.d.ts.map +1 -1
  75. package/dist/execution/transport/transport-manager.js +3 -0
  76. package/dist/execution/transport/transport-manager.js.map +1 -1
  77. package/dist/execution/worktree/conflict-detector.d.ts +85 -0
  78. package/dist/execution/worktree/conflict-detector.d.ts.map +1 -0
  79. package/dist/execution/worktree/conflict-detector.js +129 -0
  80. package/dist/execution/worktree/conflict-detector.js.map +1 -0
  81. package/dist/execution/worktree/git-cli.d.ts +9 -0
  82. package/dist/execution/worktree/git-cli.d.ts.map +1 -1
  83. package/dist/execution/worktree/git-cli.js +10 -0
  84. package/dist/execution/worktree/git-cli.js.map +1 -1
  85. package/dist/execution/worktree/git-sync-cli.d.ts +198 -0
  86. package/dist/execution/worktree/git-sync-cli.d.ts.map +1 -0
  87. package/dist/execution/worktree/git-sync-cli.js +401 -0
  88. package/dist/execution/worktree/git-sync-cli.js.map +1 -0
  89. package/dist/execution/worktree/manager.d.ts +18 -0
  90. package/dist/execution/worktree/manager.d.ts.map +1 -1
  91. package/dist/execution/worktree/manager.js +9 -3
  92. package/dist/execution/worktree/manager.js.map +1 -1
  93. package/dist/index.d.ts +1 -3
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +124 -229
  96. package/dist/index.js.map +1 -1
  97. package/dist/middleware/project-context.d.ts +37 -0
  98. package/dist/middleware/project-context.d.ts.map +1 -0
  99. package/dist/middleware/project-context.js +91 -0
  100. package/dist/middleware/project-context.js.map +1 -0
  101. package/dist/public/assets/index-DV9Tbujb.css +1 -0
  102. package/dist/public/assets/index-DcDX9-Ad.js +740 -0
  103. package/dist/public/assets/index-DcDX9-Ad.js.map +1 -0
  104. package/dist/public/assets/{react-vendor-ByUx1V_q.js → react-vendor-DiL5hC7l.js} +2 -2
  105. package/dist/public/assets/{react-vendor-ByUx1V_q.js.map → react-vendor-DiL5hC7l.js.map} +1 -1
  106. package/dist/public/assets/ui-vendor-B4WMPEfa.js +54 -0
  107. package/dist/public/assets/ui-vendor-B4WMPEfa.js.map +1 -0
  108. package/dist/public/index.html +4 -4
  109. package/dist/routes/agents.d.ts +3 -0
  110. package/dist/routes/agents.d.ts.map +1 -0
  111. package/dist/routes/agents.js +62 -0
  112. package/dist/routes/agents.js.map +1 -0
  113. package/dist/routes/config.d.ts +3 -0
  114. package/dist/routes/config.d.ts.map +1 -0
  115. package/dist/routes/config.js +25 -0
  116. package/dist/routes/config.js.map +1 -0
  117. package/dist/routes/editors.d.ts +15 -0
  118. package/dist/routes/editors.d.ts.map +1 -0
  119. package/dist/routes/editors.js +98 -0
  120. package/dist/routes/editors.js.map +1 -0
  121. package/dist/routes/executions-stream.d.ts +8 -5
  122. package/dist/routes/executions-stream.d.ts.map +1 -1
  123. package/dist/routes/executions-stream.js +10 -6
  124. package/dist/routes/executions-stream.js.map +1 -1
  125. package/dist/routes/executions.d.ts +6 -10
  126. package/dist/routes/executions.d.ts.map +1 -1
  127. package/dist/routes/executions.js +792 -37
  128. package/dist/routes/executions.js.map +1 -1
  129. package/dist/routes/feedback.d.ts +3 -2
  130. package/dist/routes/feedback.d.ts.map +1 -1
  131. package/dist/routes/feedback.js +12 -10
  132. package/dist/routes/feedback.js.map +1 -1
  133. package/dist/routes/files.d.ts +18 -0
  134. package/dist/routes/files.d.ts.map +1 -0
  135. package/dist/routes/files.js +89 -0
  136. package/dist/routes/files.js.map +1 -0
  137. package/dist/routes/issues.d.ts +3 -2
  138. package/dist/routes/issues.d.ts.map +1 -1
  139. package/dist/routes/issues.js +19 -18
  140. package/dist/routes/issues.js.map +1 -1
  141. package/dist/routes/projects.d.ts +11 -0
  142. package/dist/routes/projects.d.ts.map +1 -0
  143. package/dist/routes/projects.js +447 -0
  144. package/dist/routes/projects.js.map +1 -0
  145. package/dist/routes/relationships.d.ts +3 -2
  146. package/dist/routes/relationships.d.ts.map +1 -1
  147. package/dist/routes/relationships.js +12 -10
  148. package/dist/routes/relationships.js.map +1 -1
  149. package/dist/routes/repo-info.d.ts +3 -0
  150. package/dist/routes/repo-info.d.ts.map +1 -0
  151. package/dist/routes/repo-info.js +203 -0
  152. package/dist/routes/repo-info.js.map +1 -0
  153. package/dist/routes/specs.d.ts +3 -2
  154. package/dist/routes/specs.d.ts.map +1 -1
  155. package/dist/routes/specs.js +19 -18
  156. package/dist/routes/specs.js.map +1 -1
  157. package/dist/routes/version.d.ts +3 -0
  158. package/dist/routes/version.d.ts.map +1 -0
  159. package/dist/routes/version.js +25 -0
  160. package/dist/routes/version.js.map +1 -0
  161. package/dist/services/agent-registry.d.ts +140 -0
  162. package/dist/services/agent-registry.d.ts.map +1 -0
  163. package/dist/services/agent-registry.js +272 -0
  164. package/dist/services/agent-registry.js.map +1 -0
  165. package/dist/services/editor-service.d.ts +57 -0
  166. package/dist/services/editor-service.d.ts.map +1 -0
  167. package/dist/services/editor-service.js +204 -0
  168. package/dist/services/editor-service.js.map +1 -0
  169. package/dist/services/execution-changes-service.d.ts +110 -0
  170. package/dist/services/execution-changes-service.d.ts.map +1 -0
  171. package/dist/services/execution-changes-service.js +700 -0
  172. package/dist/services/execution-changes-service.js.map +1 -0
  173. package/dist/services/execution-lifecycle.d.ts +1 -0
  174. package/dist/services/execution-lifecycle.d.ts.map +1 -1
  175. package/dist/services/execution-lifecycle.js +37 -7
  176. package/dist/services/execution-lifecycle.js.map +1 -1
  177. package/dist/services/execution-logs-store.d.ts +75 -0
  178. package/dist/services/execution-logs-store.d.ts.map +1 -1
  179. package/dist/services/execution-logs-store.js +142 -2
  180. package/dist/services/execution-logs-store.js.map +1 -1
  181. package/dist/services/execution-service.d.ts +82 -59
  182. package/dist/services/execution-service.d.ts.map +1 -1
  183. package/dist/services/execution-service.js +514 -469
  184. package/dist/services/execution-service.js.map +1 -1
  185. package/dist/services/execution-worker-pool.d.ts +116 -0
  186. package/dist/services/execution-worker-pool.d.ts.map +1 -0
  187. package/dist/services/execution-worker-pool.js +326 -0
  188. package/dist/services/execution-worker-pool.js.map +1 -0
  189. package/dist/services/executions.d.ts +3 -0
  190. package/dist/services/executions.d.ts.map +1 -1
  191. package/dist/services/executions.js +11 -17
  192. package/dist/services/executions.js.map +1 -1
  193. package/dist/services/export.d.ts +8 -2
  194. package/dist/services/export.d.ts.map +1 -1
  195. package/dist/services/export.js +29 -23
  196. package/dist/services/export.js.map +1 -1
  197. package/dist/services/file-search/git-ls-files-strategy.d.ts +72 -0
  198. package/dist/services/file-search/git-ls-files-strategy.d.ts.map +1 -0
  199. package/dist/services/file-search/git-ls-files-strategy.js +176 -0
  200. package/dist/services/file-search/git-ls-files-strategy.js.map +1 -0
  201. package/dist/services/file-search/index.d.ts +9 -0
  202. package/dist/services/file-search/index.d.ts.map +1 -0
  203. package/dist/services/file-search/index.js +10 -0
  204. package/dist/services/file-search/index.js.map +1 -0
  205. package/dist/services/file-search/registry.d.ts +97 -0
  206. package/dist/services/file-search/registry.d.ts.map +1 -0
  207. package/dist/services/file-search/registry.js +140 -0
  208. package/dist/services/file-search/registry.js.map +1 -0
  209. package/dist/services/file-search/strategy.d.ts +58 -0
  210. package/dist/services/file-search/strategy.d.ts.map +1 -0
  211. package/dist/services/file-search/strategy.js +8 -0
  212. package/dist/services/file-search/strategy.js.map +1 -0
  213. package/dist/services/project-context.d.ts +69 -0
  214. package/dist/services/project-context.d.ts.map +1 -0
  215. package/dist/services/project-context.js +113 -0
  216. package/dist/services/project-context.js.map +1 -0
  217. package/dist/services/project-manager.d.ts +95 -0
  218. package/dist/services/project-manager.d.ts.map +1 -0
  219. package/dist/services/project-manager.js +388 -0
  220. package/dist/services/project-manager.js.map +1 -0
  221. package/dist/services/project-registry.d.ts +98 -0
  222. package/dist/services/project-registry.d.ts.map +1 -0
  223. package/dist/services/project-registry.js +289 -0
  224. package/dist/services/project-registry.js.map +1 -0
  225. package/dist/services/prompt-resolver.d.ts +97 -0
  226. package/dist/services/prompt-resolver.d.ts.map +1 -0
  227. package/dist/services/prompt-resolver.js +377 -0
  228. package/dist/services/prompt-resolver.js.map +1 -0
  229. package/dist/services/repo-info.d.ts +12 -0
  230. package/dist/services/repo-info.d.ts.map +1 -1
  231. package/dist/services/repo-info.js +46 -0
  232. package/dist/services/repo-info.js.map +1 -1
  233. package/dist/services/version-service.d.ts +14 -0
  234. package/dist/services/version-service.d.ts.map +1 -0
  235. package/dist/services/version-service.js +57 -0
  236. package/dist/services/version-service.js.map +1 -0
  237. package/dist/services/watcher.d.ts +3 -4
  238. package/dist/services/watcher.d.ts.map +1 -1
  239. package/dist/services/watcher.js +18 -35
  240. package/dist/services/watcher.js.map +1 -1
  241. package/dist/services/websocket.d.ts +30 -16
  242. package/dist/services/websocket.d.ts.map +1 -1
  243. package/dist/services/websocket.js +102 -37
  244. package/dist/services/websocket.js.map +1 -1
  245. package/dist/services/worktree-sync-service.d.ts +326 -0
  246. package/dist/services/worktree-sync-service.d.ts.map +1 -0
  247. package/dist/services/worktree-sync-service.js +1091 -0
  248. package/dist/services/worktree-sync-service.js.map +1 -0
  249. package/dist/types/editor.d.ts +49 -0
  250. package/dist/types/editor.d.ts.map +1 -0
  251. package/dist/types/editor.js +50 -0
  252. package/dist/types/editor.js.map +1 -0
  253. package/dist/types/project.d.ts +58 -0
  254. package/dist/types/project.d.ts.map +1 -0
  255. package/dist/types/project.js +10 -0
  256. package/dist/types/project.js.map +1 -0
  257. package/dist/utils/executable-check.d.ts +36 -0
  258. package/dist/utils/executable-check.d.ts.map +1 -0
  259. package/dist/utils/executable-check.js +79 -0
  260. package/dist/utils/executable-check.js.map +1 -0
  261. package/dist/workers/execution-worker.d.ts +18 -0
  262. package/dist/workers/execution-worker.d.ts.map +1 -0
  263. package/dist/workers/execution-worker.js +340 -0
  264. package/dist/workers/execution-worker.js.map +1 -0
  265. package/dist/workers/worker-ipc.d.ts +84 -0
  266. package/dist/workers/worker-ipc.d.ts.map +1 -0
  267. package/dist/workers/worker-ipc.js +29 -0
  268. package/dist/workers/worker-ipc.js.map +1 -0
  269. package/package.json +6 -5
  270. package/dist/execution/output/ag-ui-integration.d.ts +0 -96
  271. package/dist/execution/output/ag-ui-integration.d.ts.map +0 -1
  272. package/dist/execution/output/ag-ui-integration.js +0 -96
  273. package/dist/execution/output/ag-ui-integration.js.map +0 -1
  274. package/dist/execution/output/claude-code-output-processor.d.ts +0 -321
  275. package/dist/execution/output/claude-code-output-processor.d.ts.map +0 -1
  276. package/dist/execution/output/claude-code-output-processor.js +0 -769
  277. package/dist/execution/output/claude-code-output-processor.js.map +0 -1
  278. package/dist/public/assets/index-B3SEMufD.js +0 -580
  279. package/dist/public/assets/index-B3SEMufD.js.map +0 -1
  280. package/dist/public/assets/index-D2YGL3gX.css +0 -1
  281. package/dist/public/assets/ui-vendor-CotR6bx9.js +0 -54
  282. package/dist/public/assets/ui-vendor-CotR6bx9.js.map +0 -1
@@ -0,0 +1,700 @@
1
+ /**
2
+ * Execution Changes Service
3
+ *
4
+ * Calculates code changes (file list + diff statistics) from execution commits.
5
+ * Supports 3 scenarios:
6
+ * - Committed changes (commit-to-commit diff)
7
+ * - Uncommitted changes (working tree diff)
8
+ * - No changes
9
+ *
10
+ * @module services/execution-changes-service
11
+ */
12
+ import { execSync } from "child_process";
13
+ import { getExecution } from "./executions.js";
14
+ import { existsSync } from "fs";
15
+ /**
16
+ * Service for calculating code changes from execution commits
17
+ */
18
+ export class ExecutionChangesService {
19
+ db;
20
+ repoPath;
21
+ constructor(db, repoPath) {
22
+ this.db = db;
23
+ this.repoPath = repoPath;
24
+ }
25
+ /**
26
+ * Get code changes for an execution
27
+ *
28
+ * For execution chains (follow-ups), automatically calculates accumulated changes
29
+ * from the root execution to the current execution.
30
+ *
31
+ * @param executionId - Execution ID
32
+ * @returns ExecutionChangesResult with both captured and current states
33
+ */
34
+ async getChanges(executionId) {
35
+ // 1. Load execution from database
36
+ const execution = getExecution(this.db, executionId);
37
+ if (!execution) {
38
+ console.log(`[ExecutionChangesService] Execution not found: ${executionId}`);
39
+ return {
40
+ available: false,
41
+ reason: "incomplete_execution",
42
+ };
43
+ }
44
+ console.log(`[ExecutionChangesService] Execution ${executionId}:`, {
45
+ status: execution.status,
46
+ before_commit: execution.before_commit,
47
+ after_commit: execution.after_commit,
48
+ parent_execution_id: execution.parent_execution_id,
49
+ });
50
+ // 2. Validate status (must have started executing)
51
+ // Allow running, completed, stopped, failed, cancelled - reject pending, preparing, paused
52
+ const validStatuses = ["running", "completed", "stopped", "failed", "cancelled"];
53
+ if (!validStatuses.includes(execution.status)) {
54
+ console.log(`[ExecutionChangesService] Execution not started: ${execution.status}`);
55
+ return {
56
+ available: false,
57
+ reason: "incomplete_execution",
58
+ };
59
+ }
60
+ // 3. Find root execution (for execution chains)
61
+ const rootExecution = this.getRootExecution(execution);
62
+ const beforeCommit = rootExecution.before_commit || execution.before_commit;
63
+ console.log(`[ExecutionChangesService] Root execution ${rootExecution.id}:`, {
64
+ before_commit: rootExecution.before_commit,
65
+ after_commit: rootExecution.after_commit,
66
+ });
67
+ console.log(`[ExecutionChangesService] Computed beforeCommit: ${beforeCommit}`);
68
+ // 4. Validate before_commit exists (required for calculating any changes)
69
+ if (!beforeCommit) {
70
+ console.log(`[ExecutionChangesService] Missing before_commit - cannot calculate changes`);
71
+ return {
72
+ available: false,
73
+ reason: "missing_commits",
74
+ };
75
+ }
76
+ // 5. Get branch and worktree information
77
+ const branchName = execution.branch_name;
78
+ const executionMode = execution.mode;
79
+ const worktreeExists = execution.worktree_path
80
+ ? existsSync(execution.worktree_path)
81
+ : false;
82
+ // 6. Check if branch exists and get current HEAD
83
+ // Always check the main repo for branch info (branches exist in main repo, not just worktrees)
84
+ let branchExists = false;
85
+ let currentBranchHead = null;
86
+ if (branchName) {
87
+ const branchInfo = this.getBranchInfo(branchName, this.repoPath);
88
+ branchExists = branchInfo.exists;
89
+ currentBranchHead = branchInfo.head;
90
+ }
91
+ // 7. Compute captured state (from root to current execution)
92
+ let captured;
93
+ let uncommittedSnapshot;
94
+ const hasCommittedChanges = beforeCommit &&
95
+ execution.after_commit &&
96
+ execution.after_commit !== beforeCommit;
97
+ if (hasCommittedChanges) {
98
+ // Captured: committed changes (requires beforeCommit from root)
99
+ const capturedResult = await this.getCommittedChanges(beforeCommit, execution.after_commit);
100
+ if (!capturedResult.available) {
101
+ // Preserve specific error reason (commits_not_found, etc.)
102
+ return capturedResult;
103
+ }
104
+ if (capturedResult.changes) {
105
+ captured = {
106
+ files: capturedResult.changes.files,
107
+ summary: capturedResult.changes.summary,
108
+ commitRange: capturedResult.commitRange,
109
+ uncommitted: false,
110
+ };
111
+ }
112
+ // Additionally check for uncommitted changes on top of committed changes
113
+ const uncommittedResult = await this.getUncommittedChanges(execution.worktree_path);
114
+ if (uncommittedResult.available && uncommittedResult.changes && uncommittedResult.changes.files.length > 0) {
115
+ uncommittedSnapshot = {
116
+ files: uncommittedResult.changes.files,
117
+ summary: uncommittedResult.changes.summary,
118
+ commitRange: null,
119
+ uncommitted: true,
120
+ };
121
+ }
122
+ }
123
+ else {
124
+ // Captured: uncommitted changes only (or no changes at completion)
125
+ const capturedResult = await this.getUncommittedChanges(execution.worktree_path);
126
+ if (!capturedResult.available) {
127
+ // Preserve specific error reason (worktree_deleted_with_uncommitted_changes, etc.)
128
+ return capturedResult;
129
+ }
130
+ if (capturedResult.changes) {
131
+ captured = {
132
+ files: capturedResult.changes.files,
133
+ summary: capturedResult.changes.summary,
134
+ commitRange: null,
135
+ uncommitted: true,
136
+ };
137
+ }
138
+ }
139
+ // 8. Compute current state (if branch exists and differs from captured)
140
+ // Requires beforeCommit to calculate diff
141
+ // Note: Use main repo for git operations (worktree may be deleted)
142
+ let current;
143
+ let additionalCommits = 0;
144
+ if (branchExists && currentBranchHead && beforeCommit) {
145
+ // Check if current HEAD is different from captured after_commit
146
+ const isDifferent = currentBranchHead !== execution.after_commit;
147
+ if (isDifferent && execution.after_commit) {
148
+ // Count commits between captured and current
149
+ // Always use main repo path (worktree may be deleted)
150
+ additionalCommits = this.countCommitsBetween(execution.after_commit, currentBranchHead, this.repoPath);
151
+ // Compute current changes (from root to current HEAD)
152
+ const currentResult = await this.getCommittedChanges(beforeCommit, currentBranchHead);
153
+ if (currentResult.available && currentResult.changes) {
154
+ current = {
155
+ files: currentResult.changes.files,
156
+ summary: currentResult.changes.summary,
157
+ commitRange: {
158
+ before: beforeCommit,
159
+ after: currentBranchHead,
160
+ },
161
+ uncommitted: false,
162
+ };
163
+ }
164
+ }
165
+ }
166
+ // 9. Calculate commits ahead of target branch (for merge action visibility)
167
+ let commitsAhead = 0;
168
+ if (branchExists && currentBranchHead && execution.target_branch) {
169
+ commitsAhead = this.countCommitsAhead(execution.target_branch, currentBranchHead, this.repoPath);
170
+ }
171
+ // 10. Return result with both states
172
+ if (!captured) {
173
+ return {
174
+ available: false,
175
+ reason: "git_error",
176
+ };
177
+ }
178
+ return {
179
+ available: true,
180
+ captured,
181
+ uncommittedSnapshot,
182
+ current,
183
+ branchName,
184
+ branchExists,
185
+ worktreeExists,
186
+ executionMode,
187
+ additionalCommits,
188
+ commitsAhead,
189
+ // Legacy compatibility
190
+ changes: captured,
191
+ commitRange: captured.commitRange,
192
+ uncommitted: captured.uncommitted,
193
+ };
194
+ }
195
+ /**
196
+ * Get root execution by traversing parent_execution_id chain
197
+ *
198
+ * For execution chains (follow-ups), this finds the original root execution.
199
+ * This allows us to calculate accumulated changes from the start of the chain.
200
+ */
201
+ getRootExecution(execution) {
202
+ let current = execution;
203
+ let maxDepth = 100; // Safety limit to prevent infinite loops
204
+ let depth = 0;
205
+ while (current.parent_execution_id && depth < maxDepth) {
206
+ const parent = getExecution(this.db, current.parent_execution_id);
207
+ if (!parent) {
208
+ // Parent not found, return current as root
209
+ break;
210
+ }
211
+ current = parent;
212
+ depth++;
213
+ }
214
+ return current;
215
+ }
216
+ /**
217
+ * Get committed changes (commit-to-commit diff)
218
+ * Scenario A: after_commit exists and differs from before_commit
219
+ */
220
+ async getCommittedChanges(beforeCommit, afterCommit) {
221
+ try {
222
+ // Verify commits exist in repo
223
+ try {
224
+ execSync(`git cat-file -t ${beforeCommit}`, {
225
+ cwd: this.repoPath,
226
+ encoding: "utf-8",
227
+ stdio: "pipe",
228
+ });
229
+ execSync(`git cat-file -t ${afterCommit}`, {
230
+ cwd: this.repoPath,
231
+ encoding: "utf-8",
232
+ stdio: "pipe",
233
+ });
234
+ }
235
+ catch (error) {
236
+ return {
237
+ available: false,
238
+ reason: "commits_not_found",
239
+ };
240
+ }
241
+ // Get diff statistics
242
+ const files = await this.calculateDiff(beforeCommit, afterCommit, this.repoPath);
243
+ return {
244
+ available: true,
245
+ uncommitted: false,
246
+ commitRange: {
247
+ before: beforeCommit,
248
+ after: afterCommit,
249
+ },
250
+ changes: {
251
+ files,
252
+ summary: this.calculateSummary(files),
253
+ commitRange: {
254
+ before: beforeCommit,
255
+ after: afterCommit,
256
+ },
257
+ uncommitted: false,
258
+ },
259
+ };
260
+ }
261
+ catch (error) {
262
+ console.error("[ExecutionChangesService] Error getting committed changes:", error);
263
+ return {
264
+ available: false,
265
+ reason: "git_error",
266
+ };
267
+ }
268
+ }
269
+ /**
270
+ * Get uncommitted changes (working tree diff)
271
+ * Scenario B: after_commit is null or equals before_commit
272
+ */
273
+ async getUncommittedChanges(worktreePath) {
274
+ // Determine the working directory to check
275
+ const workDir = worktreePath || this.repoPath;
276
+ // Check if worktree still exists
277
+ if (worktreePath && !existsSync(worktreePath)) {
278
+ return {
279
+ available: false,
280
+ reason: "worktree_deleted_with_uncommitted_changes",
281
+ };
282
+ }
283
+ try {
284
+ // Get uncommitted changes relative to HEAD
285
+ const files = await this.calculateDiff(null, null, workDir);
286
+ // If no files changed, return empty result
287
+ if (files.length === 0) {
288
+ return {
289
+ available: true,
290
+ uncommitted: true,
291
+ commitRange: null,
292
+ changes: {
293
+ files: [],
294
+ summary: {
295
+ totalFiles: 0,
296
+ totalAdditions: 0,
297
+ totalDeletions: 0,
298
+ },
299
+ commitRange: null,
300
+ uncommitted: true,
301
+ },
302
+ };
303
+ }
304
+ return {
305
+ available: true,
306
+ uncommitted: true,
307
+ commitRange: null,
308
+ changes: {
309
+ files,
310
+ summary: this.calculateSummary(files),
311
+ commitRange: null,
312
+ uncommitted: true,
313
+ },
314
+ };
315
+ }
316
+ catch (error) {
317
+ console.error("[ExecutionChangesService] Error getting uncommitted changes:", error);
318
+ return {
319
+ available: false,
320
+ reason: "git_error",
321
+ };
322
+ }
323
+ }
324
+ /**
325
+ * Calculate diff statistics using git commands
326
+ *
327
+ * @param beforeCommit - Before commit SHA (null for uncommitted)
328
+ * @param afterCommit - After commit SHA (null for uncommitted)
329
+ * @param workDir - Working directory for git commands
330
+ * @returns Array of file change statistics
331
+ */
332
+ async calculateDiff(beforeCommit, afterCommit, workDir) {
333
+ // Build git diff command based on scenario
334
+ let numstatCmd;
335
+ let nameStatusCmd;
336
+ if (beforeCommit && afterCommit) {
337
+ // Committed changes: commit-to-commit diff
338
+ numstatCmd = `git diff --numstat --find-renames ${beforeCommit}..${afterCommit}`;
339
+ nameStatusCmd = `git diff --name-status --find-renames ${beforeCommit}..${afterCommit}`;
340
+ }
341
+ else {
342
+ // Uncommitted changes: working tree diff
343
+ numstatCmd = `git diff --numstat --find-renames HEAD`;
344
+ nameStatusCmd = `git diff --name-status --find-renames HEAD`;
345
+ }
346
+ // Execute git diff --numstat
347
+ const numstatOutput = execSync(numstatCmd, {
348
+ cwd: workDir,
349
+ encoding: "utf-8",
350
+ stdio: "pipe",
351
+ }).trim();
352
+ // Execute git diff --name-status
353
+ const nameStatusOutput = execSync(nameStatusCmd, {
354
+ cwd: workDir,
355
+ encoding: "utf-8",
356
+ stdio: "pipe",
357
+ }).trim();
358
+ // Parse outputs
359
+ const numstatData = this.parseNumstat(numstatOutput);
360
+ const statusData = this.parseNameStatus(nameStatusOutput);
361
+ // Combine numstat and status data
362
+ let files = this.combineFileData(numstatData, statusData);
363
+ // For uncommitted changes, also include untracked files
364
+ if (!beforeCommit && !afterCommit) {
365
+ const untrackedFiles = this.getUntrackedFiles(workDir);
366
+ files = files.concat(untrackedFiles);
367
+ }
368
+ return files;
369
+ }
370
+ /**
371
+ * Parse git diff --numstat output
372
+ *
373
+ * Format: "additions\tdeletions\tfilepath"
374
+ * Binary files: "-\t-\tfilepath"
375
+ */
376
+ parseNumstat(output) {
377
+ const data = new Map();
378
+ if (!output) {
379
+ return data;
380
+ }
381
+ const lines = output.split("\n");
382
+ for (const line of lines) {
383
+ if (!line.trim())
384
+ continue;
385
+ const parts = line.split("\t");
386
+ if (parts.length < 3)
387
+ continue;
388
+ const additions = parts[0] === "-" ? 0 : parseInt(parts[0], 10);
389
+ const deletions = parts[1] === "-" ? 0 : parseInt(parts[1], 10);
390
+ const filePath = parts.slice(2).join("\t"); // Handle filenames with tabs
391
+ data.set(filePath, { additions, deletions });
392
+ }
393
+ return data;
394
+ }
395
+ /**
396
+ * Parse git diff --name-status output
397
+ *
398
+ * Format: "STATUS\tfilepath"
399
+ * Renamed files: "R100\toldpath\tnewpath"
400
+ */
401
+ parseNameStatus(output) {
402
+ const data = new Map();
403
+ if (!output) {
404
+ return data;
405
+ }
406
+ const lines = output.split("\n");
407
+ for (const line of lines) {
408
+ if (!line.trim())
409
+ continue;
410
+ const parts = line.split("\t");
411
+ if (parts.length < 2)
412
+ continue;
413
+ const statusCode = parts[0];
414
+ let status;
415
+ if (statusCode.startsWith("A")) {
416
+ status = "A";
417
+ }
418
+ else if (statusCode.startsWith("M")) {
419
+ status = "M";
420
+ }
421
+ else if (statusCode.startsWith("D")) {
422
+ status = "D";
423
+ }
424
+ else if (statusCode.startsWith("R")) {
425
+ status = "R";
426
+ }
427
+ else {
428
+ // Default to modified for unknown status
429
+ status = "M";
430
+ }
431
+ // For renamed files, use the new path (last part)
432
+ const filePath = parts[parts.length - 1];
433
+ data.set(filePath, status);
434
+ }
435
+ return data;
436
+ }
437
+ /**
438
+ * Combine numstat and name-status data into FileChangeStat array
439
+ */
440
+ combineFileData(numstatData, statusData) {
441
+ const files = [];
442
+ // Iterate through all files from numstat
443
+ for (const [path, { additions, deletions }] of numstatData) {
444
+ const status = statusData.get(path) || "M"; // Default to modified
445
+ files.push({
446
+ path,
447
+ additions,
448
+ deletions,
449
+ status,
450
+ });
451
+ }
452
+ return files;
453
+ }
454
+ /**
455
+ * Calculate summary statistics from file changes
456
+ */
457
+ calculateSummary(files) {
458
+ return {
459
+ totalFiles: files.length,
460
+ totalAdditions: files.reduce((sum, file) => sum + file.additions, 0),
461
+ totalDeletions: files.reduce((sum, file) => sum + file.deletions, 0),
462
+ };
463
+ }
464
+ /**
465
+ * Get branch information (existence and current HEAD)
466
+ */
467
+ getBranchInfo(branchName, workDir) {
468
+ try {
469
+ // Try to get the commit SHA for the branch
470
+ const head = execSync(`git rev-parse ${branchName}`, {
471
+ cwd: workDir,
472
+ encoding: "utf-8",
473
+ stdio: "pipe",
474
+ }).trim();
475
+ return {
476
+ exists: true,
477
+ head,
478
+ };
479
+ }
480
+ catch (error) {
481
+ // Branch doesn't exist
482
+ return {
483
+ exists: false,
484
+ head: null,
485
+ };
486
+ }
487
+ }
488
+ /**
489
+ * Count commits between two commit SHAs
490
+ */
491
+ countCommitsBetween(fromCommit, toCommit, workDir) {
492
+ try {
493
+ const output = execSync(`git rev-list --count ${fromCommit}..${toCommit}`, {
494
+ cwd: workDir,
495
+ encoding: "utf-8",
496
+ stdio: "pipe",
497
+ }).trim();
498
+ return parseInt(output, 10) || 0;
499
+ }
500
+ catch (error) {
501
+ console.error("[ExecutionChangesService] Error counting commits:", error);
502
+ return 0;
503
+ }
504
+ }
505
+ /**
506
+ * Count commits the branch is ahead of target branch
507
+ * Uses merge-base to find the common ancestor
508
+ */
509
+ countCommitsAhead(targetBranch, branchHead, workDir) {
510
+ try {
511
+ // Find the merge base between target branch and the current branch
512
+ const mergeBase = execSync(`git merge-base ${targetBranch} ${branchHead}`, {
513
+ cwd: workDir,
514
+ encoding: "utf-8",
515
+ stdio: "pipe",
516
+ }).trim();
517
+ // Count commits from merge base to branch head
518
+ const output = execSync(`git rev-list --count ${mergeBase}..${branchHead}`, {
519
+ cwd: workDir,
520
+ encoding: "utf-8",
521
+ stdio: "pipe",
522
+ }).trim();
523
+ return parseInt(output, 10) || 0;
524
+ }
525
+ catch (error) {
526
+ console.error("[ExecutionChangesService] Error counting commits ahead:", error);
527
+ return 0;
528
+ }
529
+ }
530
+ /**
531
+ * Get untracked files (respecting .gitignore)
532
+ */
533
+ getUntrackedFiles(workDir) {
534
+ try {
535
+ // Get untracked files, excluding .gitignore patterns
536
+ const output = execSync("git ls-files --others --exclude-standard", {
537
+ cwd: workDir,
538
+ encoding: "utf-8",
539
+ stdio: "pipe",
540
+ }).trim();
541
+ if (!output) {
542
+ return [];
543
+ }
544
+ const files = [];
545
+ const lines = output.split("\n");
546
+ for (const filePath of lines) {
547
+ if (!filePath.trim())
548
+ continue;
549
+ // Count lines in the file for additions
550
+ const fullPath = `${workDir}/${filePath}`;
551
+ let additions = 0;
552
+ try {
553
+ if (existsSync(fullPath)) {
554
+ const content = require("fs").readFileSync(fullPath, "utf-8");
555
+ additions = content.split("\n").length;
556
+ }
557
+ }
558
+ catch (error) {
559
+ // If we can't read the file (binary, etc.), just set additions to 1
560
+ additions = 1;
561
+ }
562
+ files.push({
563
+ path: filePath,
564
+ additions,
565
+ deletions: 0,
566
+ status: "A", // Untracked files are "Added"
567
+ });
568
+ }
569
+ return files;
570
+ }
571
+ catch (error) {
572
+ console.error("[ExecutionChangesService] Error getting untracked files:", error);
573
+ return [];
574
+ }
575
+ }
576
+ /**
577
+ * Get diff content for a specific file
578
+ *
579
+ * @param executionId - Execution ID
580
+ * @param filePath - Path to the file
581
+ * @returns Object with oldContent and newContent
582
+ */
583
+ async getFileDiff(executionId, filePath) {
584
+ try {
585
+ // 1. Load execution from database
586
+ const execution = getExecution(this.db, executionId);
587
+ if (!execution) {
588
+ return { success: false, error: "Execution not found" };
589
+ }
590
+ // 2. Find root execution for before_commit
591
+ const rootExecution = this.getRootExecution(execution);
592
+ const beforeCommit = rootExecution.before_commit || execution.before_commit;
593
+ const afterCommit = execution.after_commit;
594
+ if (!beforeCommit) {
595
+ return { success: false, error: "Missing before_commit" };
596
+ }
597
+ let oldContent = "";
598
+ let newContent = "";
599
+ // Normalize file path - remove leading './' if present
600
+ const normalizedPath = filePath.startsWith('./') ? filePath.slice(2) : filePath;
601
+ console.log(`[ExecutionChangesService] Getting diff for ${filePath}:`, {
602
+ beforeCommit,
603
+ afterCommit,
604
+ worktreePath: execution.worktree_path,
605
+ normalizedPath,
606
+ });
607
+ // 3. Get old content (from before_commit)
608
+ try {
609
+ const cmd = `git show ${beforeCommit}:${normalizedPath}`;
610
+ console.log(`[ExecutionChangesService] Running: ${cmd}`);
611
+ oldContent = execSync(cmd, {
612
+ cwd: this.repoPath,
613
+ encoding: "utf-8",
614
+ stdio: "pipe",
615
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large files
616
+ });
617
+ console.log(`[ExecutionChangesService] Old content length: ${oldContent.length}`);
618
+ }
619
+ catch (error) {
620
+ // File might not exist in before_commit (new file)
621
+ console.log(`[ExecutionChangesService] Failed to get old content:`, error instanceof Error ? error.message : error);
622
+ oldContent = "";
623
+ }
624
+ // 4. Get new content (depends on whether we have committed or uncommitted changes)
625
+ if (afterCommit && afterCommit !== beforeCommit) {
626
+ // Committed changes - get from after_commit
627
+ console.log(`[ExecutionChangesService] Fetching new content from after_commit: ${afterCommit}`);
628
+ try {
629
+ const cmd = `git show ${afterCommit}:${normalizedPath}`;
630
+ console.log(`[ExecutionChangesService] Running: ${cmd}`);
631
+ newContent = execSync(cmd, {
632
+ cwd: this.repoPath,
633
+ encoding: "utf-8",
634
+ stdio: "pipe",
635
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large files
636
+ });
637
+ console.log(`[ExecutionChangesService] New content length: ${newContent.length}`);
638
+ }
639
+ catch (error) {
640
+ // File might have been deleted
641
+ console.log(`[ExecutionChangesService] Failed to get new content:`, error instanceof Error ? error.message : error);
642
+ newContent = "";
643
+ }
644
+ }
645
+ else if (execution.worktree_path && existsSync(execution.worktree_path)) {
646
+ // Uncommitted changes - get from working tree
647
+ console.log(`[ExecutionChangesService] Fetching new content from worktree: ${execution.worktree_path}`);
648
+ try {
649
+ const { readFileSync } = await import("fs");
650
+ const { join } = await import("path");
651
+ const fullPath = join(execution.worktree_path, normalizedPath);
652
+ console.log(`[ExecutionChangesService] Checking file: ${fullPath}`);
653
+ if (existsSync(fullPath)) {
654
+ newContent = readFileSync(fullPath, "utf-8");
655
+ console.log(`[ExecutionChangesService] New content length: ${newContent.length}`);
656
+ }
657
+ else {
658
+ // File might have been deleted
659
+ console.log(`[ExecutionChangesService] File deleted in worktree`);
660
+ newContent = "";
661
+ }
662
+ }
663
+ catch (error) {
664
+ return { success: false, error: `Failed to read file: ${error}` };
665
+ }
666
+ }
667
+ else {
668
+ // No after_commit and no worktree - use HEAD
669
+ console.log(`[ExecutionChangesService] Fetching new content from HEAD`);
670
+ try {
671
+ newContent = execSync(`git show HEAD:${normalizedPath}`, {
672
+ cwd: this.repoPath,
673
+ encoding: "utf-8",
674
+ stdio: "pipe",
675
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large files
676
+ });
677
+ console.log(`[ExecutionChangesService] New content length: ${newContent.length}`);
678
+ }
679
+ catch (error) {
680
+ console.log(`[ExecutionChangesService] File deleted at HEAD`);
681
+ newContent = "";
682
+ }
683
+ }
684
+ console.log(`[ExecutionChangesService] Returning diff - oldContent: ${oldContent.length} chars, newContent: ${newContent.length} chars`);
685
+ return {
686
+ success: true,
687
+ oldContent,
688
+ newContent,
689
+ };
690
+ }
691
+ catch (error) {
692
+ console.error("[ExecutionChangesService] Error getting file diff:", error);
693
+ return {
694
+ success: false,
695
+ error: error instanceof Error ? error.message : String(error),
696
+ };
697
+ }
698
+ }
699
+ }
700
+ //# sourceMappingURL=execution-changes-service.js.map