@astrosheep/keiyaku 2.9.3 → 2.9.5

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/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +14 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +26 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +68 -5
  8. package/build/agents/harness/projection.js +179 -124
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +82 -53
  21. package/build/agents/providers/codex-app-server/events.js +90 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/events.js +4 -3
  36. package/build/agents/providers/pi/registration.js +145 -0
  37. package/build/agents/providers/pi/session-schema.js +6 -0
  38. package/build/agents/providers/pi/terms.js +1 -0
  39. package/build/agents/providers/registration.js +7 -0
  40. package/build/agents/providers/registry.js +76 -0
  41. package/build/agents/selector.js +2 -29
  42. package/build/agents/types.js +10 -0
  43. package/build/artifact-directories.js +22 -0
  44. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  45. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  46. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  47. package/build/cli/commands/akuma/view/handler.js +5 -4
  48. package/build/cli/commands/akuma/view/meta.js +1 -1
  49. package/build/cli/commands/akuma.js +6 -0
  50. package/build/cli/commands/contract/amend/handler.js +8 -2
  51. package/build/cli/commands/contract/amend/meta.js +4 -4
  52. package/build/cli/commands/contract/arc/handler.js +8 -2
  53. package/build/cli/commands/contract/arc/meta.js +4 -4
  54. package/build/cli/commands/contract/audit/handler.js +16 -0
  55. package/build/cli/commands/contract/audit/meta.js +16 -0
  56. package/build/cli/commands/contract/bind/handler.js +4 -3
  57. package/build/cli/commands/contract/bind/meta.js +44 -8
  58. package/build/cli/commands/contract/catalog.js +2 -0
  59. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  60. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  61. package/build/cli/commands/contract/log/handler.js +3 -3
  62. package/build/cli/commands/contract/log/meta.js +1 -1
  63. package/build/cli/commands/contract/petition/handler.js +5 -8
  64. package/build/cli/commands/contract/petition/meta.js +6 -6
  65. package/build/cli/commands/contract/renew/handler.js +20 -6
  66. package/build/cli/commands/contract/renew/meta.js +6 -5
  67. package/build/cli/commands/metadata.js +59 -0
  68. package/build/cli/commands/projection/call/handler.js +6 -5
  69. package/build/cli/commands/projection/call/meta.js +4 -1
  70. package/build/cli/commands/projection/kill/handler.js +16 -32
  71. package/build/cli/commands/projection/kill/meta.js +1 -1
  72. package/build/cli/commands/projection/revive/meta.js +4 -1
  73. package/build/cli/commands/projection/shared.js +28 -0
  74. package/build/cli/commands/projection/status/handler.js +1 -1
  75. package/build/cli/commands/projection/status/meta.js +1 -1
  76. package/build/cli/commands/projection/tell/handler.js +14 -30
  77. package/build/cli/commands/projection/tell/meta.js +4 -1
  78. package/build/cli/commands/projection/wait/handler.js +45 -22
  79. package/build/cli/commands/projection/wait/meta.js +8 -3
  80. package/build/cli/commands/registry.js +2 -54
  81. package/build/cli/commands/shared.js +10 -7
  82. package/build/cli/commands/skills/install/handler.js +2 -5
  83. package/build/cli/commands/skills/install/meta.js +1 -1
  84. package/build/cli/commands/skills/skills/handler.js +1 -1
  85. package/build/cli/commands/skills/skills/meta.js +1 -1
  86. package/build/cli/commands/system/completion/meta.js +1 -1
  87. package/build/cli/commands/system/dump-env/meta.js +1 -1
  88. package/build/cli/commands/system/guide/meta.js +1 -1
  89. package/build/cli/commands/task/add/handler.js +7 -15
  90. package/build/cli/commands/task/add/meta.js +4 -1
  91. package/build/cli/commands/task/catalog.js +2 -2
  92. package/build/cli/commands/task/doctor/meta.js +1 -1
  93. package/build/cli/commands/task/done/handler.js +4 -8
  94. package/build/cli/commands/task/done/meta.js +1 -1
  95. package/build/cli/commands/task/drop/handler.js +3 -6
  96. package/build/cli/commands/task/drop/meta.js +1 -1
  97. package/build/cli/commands/task/ls/handler.js +12 -8
  98. package/build/cli/commands/task/ls/meta.js +3 -3
  99. package/build/cli/commands/task/shared.js +14 -4
  100. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  101. package/build/cli/commands/task/show/meta.js +8 -0
  102. package/build/cli/commands/task/start/handler.js +3 -6
  103. package/build/cli/commands/task/start/meta.js +1 -1
  104. package/build/cli/commands/task/stop/handler.js +4 -8
  105. package/build/cli/commands/task/stop/meta.js +1 -1
  106. package/build/cli/commands/task/task/handler.js +1 -1
  107. package/build/cli/commands/task/task/meta.js +1 -1
  108. package/build/cli/commands/task/update/handler.js +6 -11
  109. package/build/cli/commands/task/update/meta.js +4 -1
  110. package/build/cli/completion.js +1 -1
  111. package/build/cli/flags.js +37 -6
  112. package/build/cli/help.js +7 -14
  113. package/build/cli/index.js +112 -154
  114. package/build/cli/missing-subcommand.js +7 -0
  115. package/build/cli/output-stream-error-policy.js +19 -5
  116. package/build/cli/parse-flags.js +33 -7
  117. package/build/cli/parse-metadata.js +3 -9
  118. package/build/cli/parse-selectors.js +2 -1
  119. package/build/cli/parse.js +35 -5
  120. package/build/cli/projection-address.js +12 -12
  121. package/build/cli/render/address.js +41 -37
  122. package/build/cli/render/arc.js +24 -12
  123. package/build/cli/render/audit.js +121 -0
  124. package/build/cli/render/call.js +11 -5
  125. package/build/cli/render/errors.js +36 -1
  126. package/build/cli/render/format.js +4 -0
  127. package/build/cli/render/kanshi.js +6 -12
  128. package/build/cli/render/kill.js +9 -0
  129. package/build/cli/render/misc.js +58 -13
  130. package/build/cli/render/petition.js +20 -15
  131. package/build/cli/render/projection-activity.js +15 -6
  132. package/build/cli/render/shared.js +60 -21
  133. package/build/cli/render/skills.js +10 -0
  134. package/build/cli/render/status.js +56 -23
  135. package/build/cli/render/success-response.js +78 -0
  136. package/build/cli/render/task.js +66 -57
  137. package/build/cli/render/tell.js +5 -0
  138. package/build/cli/render/terminal-failure.js +63 -0
  139. package/build/cli/render/types.js +1 -0
  140. package/build/cli/render/wait.js +68 -38
  141. package/build/cli/types.js +1 -1
  142. package/build/config/akuma-loader.js +0 -5
  143. package/build/config/env-keys.js +3 -1
  144. package/build/config/env.js +3 -0
  145. package/build/config/provider-policy-ownership.js +3 -32
  146. package/build/config/settings/disease.js +8 -1
  147. package/build/config/settings/loader.js +11 -3
  148. package/build/config/settings/provider-instance-fields.js +51 -0
  149. package/build/config/settings/schema.js +6 -176
  150. package/build/core/addressing.js +66 -16
  151. package/build/core/amend.js +61 -18
  152. package/build/core/arc.js +33 -73
  153. package/build/core/audit.js +650 -0
  154. package/build/core/bind.js +82 -35
  155. package/build/core/call/call.js +15 -9
  156. package/build/core/call/context.js +6 -51
  157. package/build/core/call/execution.js +22 -22
  158. package/build/core/call/prompt.js +14 -1
  159. package/build/core/command-markdown.js +45 -0
  160. package/build/core/context.js +13 -7
  161. package/build/core/contract-view.js +22 -6
  162. package/build/core/draft.js +139 -19
  163. package/build/core/entry.js +3 -4
  164. package/build/core/forfeit.js +5 -2
  165. package/build/core/hints.js +1 -75
  166. package/build/core/identifier-slug.js +66 -0
  167. package/build/core/ids.js +25 -11
  168. package/build/core/ledger.js +20 -68
  169. package/build/core/markdown/lex.js +2 -1
  170. package/build/core/markdown/parser.js +0 -68
  171. package/build/core/markdown/render.js +68 -0
  172. package/build/core/markdown/sections.js +2 -1
  173. package/build/core/outcome-base.js +4 -0
  174. package/build/core/projection/generation/database.js +41 -52
  175. package/build/core/projection/generation/model.js +63 -2
  176. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  177. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +36 -53
  178. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  179. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +9 -7
  180. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  181. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +19 -3
  182. package/build/core/projection/generation/store.js +3 -3
  183. package/build/core/projection/generation/transitions.js +19 -3
  184. package/build/core/projection/index.js +27 -0
  185. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  186. package/build/core/{projection-alias.js → projection/projection-alias.js} +13 -13
  187. package/build/core/projection/projection-coordinate.js +61 -0
  188. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  189. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +37 -78
  190. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  191. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  192. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +16 -2
  193. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  194. package/build/core/{projection-status.js → projection/projection-status.js} +163 -45
  195. package/build/core/projection/projection-terminal-failure.js +106 -0
  196. package/build/core/projection/projection-wait.js +275 -0
  197. package/build/core/projection/projection-wake.js +227 -0
  198. package/build/core/provider-loader-ports.js +4 -0
  199. package/build/core/ref-log.js +65 -40
  200. package/build/core/registry.js +63 -10
  201. package/build/core/renew-session.js +128 -0
  202. package/build/core/renew.js +448 -102
  203. package/build/core/scope.js +272 -68
  204. package/build/core/seal.js +165 -40
  205. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +8 -8
  206. package/build/core/{claim.js → settlement/claim.js} +32 -8
  207. package/build/core/settlement/index.js +19 -0
  208. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +24 -49
  209. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  210. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  211. package/build/core/settlement/petition-io.js +1 -0
  212. package/build/core/{petition-preview.js → settlement/petition-preview.js} +2 -2
  213. package/build/core/{petition.js → settlement/petition.js} +21 -12
  214. package/build/core/settlement/queue-read-model.js +143 -0
  215. package/build/core/settlement/queue-seat-allocation.js +156 -0
  216. package/build/core/settlement/queue.js +6 -0
  217. package/build/core/settlement/settlement.js +288 -0
  218. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  219. package/build/core/settlement/verification.js +59 -0
  220. package/build/core/status/board.js +62 -41
  221. package/build/core/status/ledger-batch.js +158 -0
  222. package/build/core/status/reconciliation.js +10 -4
  223. package/build/core/stored-agent-event.js +6 -2
  224. package/build/core/task/board.js +41 -4
  225. package/build/core/task/commands.js +45 -28
  226. package/build/core/task/document.js +5 -0
  227. package/build/core/task/index.js +12 -0
  228. package/build/core/task/model.js +8 -1
  229. package/build/core/task/query.js +172 -0
  230. package/build/core/task/settlement-git.js +265 -74
  231. package/build/core/task/settlement-policy.js +67 -29
  232. package/build/core/task/source-board.js +1 -0
  233. package/build/core/task/task-contract.js +444 -0
  234. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  235. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  236. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  237. package/build/core/{task-git-store.js → task/task-git-store.js} +90 -19
  238. package/build/core/task/task-repository.js +1 -0
  239. package/build/core/{task-store-repository.js → task/task-store-repository.js} +6 -4
  240. package/build/core/task/task-store.js +1 -0
  241. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  242. package/build/core/task/task.js +7 -0
  243. package/build/core/task/tree.js +37 -0
  244. package/build/core/task/validation.js +5 -2
  245. package/build/core/transcripts.js +15 -3
  246. package/build/core/worktree-path.js +43 -28
  247. package/build/flow-error.js +83 -39
  248. package/build/fs/path-coordinate.js +16 -0
  249. package/build/generated/version.js +3 -2
  250. package/build/git/branches.js +2 -1
  251. package/build/git/core.js +106 -2
  252. package/build/git/diff/parsers.js +23 -13
  253. package/build/git/diff/structured.js +127 -0
  254. package/build/git/path-coordinate.js +17 -0
  255. package/build/git/refs.js +57 -0
  256. package/build/git/streaming-batch.js +301 -0
  257. package/build/git/worktree.js +65 -0
  258. package/build/index.js +9 -9
  259. package/build/keiyaku.js +2 -36
  260. package/build/telemetry/logger.js +4 -2
  261. package/package.json +4 -10
  262. package/skills/keiyaku/SKILL.md +5 -4
  263. package/skills/keiyaku-akuma/SKILL.md +25 -11
  264. package/skills/keiyaku-task/SKILL.md +17 -8
  265. package/skills/keiyaku-workflow/SKILL.md +58 -9
  266. package/build/agents/providers/codex-sdk.js +0 -329
  267. package/build/cli/commands/task/view/meta.js +0 -8
  268. package/build/core/command-io.js +0 -174
  269. package/build/core/path-coordinate.js +0 -27
  270. package/build/core/petition-claim.js +0 -117
  271. package/build/core/petition-run.js +0 -83
  272. package/build/core/projection-coordinate.js +0 -63
  273. package/build/core/projection-wait.js +0 -165
  274. package/build/core/projection-wake.js +0 -136
  275. package/build/core/queue.js +0 -343
  276. package/build/core/task-contract.js +0 -237
  277. package/build/core/task.js +0 -4
  278. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  279. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  280. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  281. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  282. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -0,0 +1,16 @@
1
+ import * as path from "node:path";
2
+ /**
3
+ * Keep one host-native spelling before using a coordinate as a filesystem cwd.
4
+ * Git for Windows may emit or receive native, slash-normalized, or MSYS drive
5
+ * paths for the same location.
6
+ */
7
+ export function normalizeFilesystemCoordinate(raw, platform = process.platform) {
8
+ const value = raw.trim();
9
+ if (platform !== "win32")
10
+ return path.normalize(value);
11
+ const msysDrive = value.match(/^\/([A-Za-z])(?:\/|$)(.*)$/);
12
+ if (msysDrive)
13
+ return path.win32.normalize(`${msysDrive[1].toUpperCase()}:/${msysDrive[2]}`);
14
+ const normalized = path.win32.normalize(value);
15
+ return normalized.replace(/^([a-z]):/, (_, drive) => `${drive.toUpperCase()}:`);
16
+ }
@@ -1,2 +1,3 @@
1
- // Auto-generated by scripts/generate-version.mjs
2
- export const VERSION = "2.9.2";
1
+ // Generated into build output by scripts/build.mjs
2
+ export const VERSION = "2.9.5";
3
+ export const GIT_HASH = "ade1510";
@@ -1,9 +1,10 @@
1
1
  import { simpleGit } from "simple-git";
2
2
  import { FlowError } from "../flow-error.js";
3
+ import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
3
4
  import { createGit, errorContainsAnyPattern, isGitExecutableUnavailable, NOT_GIT_REPOSITORY_PATTERNS, wrapGitError, } from "./core.js";
4
5
  export async function observeGitRepository(cwd) {
5
6
  try {
6
- const git = simpleGit(cwd);
7
+ const git = simpleGit(normalizeFilesystemCoordinate(cwd));
7
8
  git.env("GIT_TERMINAL_PROMPT", "0");
8
9
  git.env("GCM_INTERACTIVE", "Never");
9
10
  git.env("GIT_MERGE_AUTOEDIT", "no");
package/build/git/core.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { simpleGit } from "simple-git";
3
+ import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
2
4
  import { getConfig } from "../config/env.js";
3
5
  import { appendDebugBlock } from "../telemetry/debug-log.js";
4
6
  const GIT_ERROR_COMPACT_MAX_CHARS = 4000;
@@ -38,6 +40,14 @@ export function errorContainsAnyPattern(err, patterns) {
38
40
  const text = collectGitErrorText(err);
39
41
  return patterns.some((pattern) => text.includes(pattern));
40
42
  }
43
+ /** Parse `git rev-list` stdout into ordered object ids. Sole shared line parser. */
44
+ export function parseRevListOutput(output) {
45
+ return output
46
+ .trim()
47
+ .split(/\r?\n/)
48
+ .map((line) => line.trim())
49
+ .filter((line) => line.length > 0);
50
+ }
41
51
  export function wrapGitError(commandLabel, err, cwd) {
42
52
  const source = (err ?? {});
43
53
  const message = source.message ?? String(err);
@@ -57,9 +67,31 @@ export function wrapGitError(commandLabel, err, cwd) {
57
67
  }
58
68
  return wrapped;
59
69
  }
70
+ /** Configured Git timeout, with a bootstrap-safe default before env loads. */
71
+ export function resolveGitTimeoutMs() {
72
+ try {
73
+ return getConfig().git.timeoutMs;
74
+ }
75
+ catch {
76
+ // Git coordinates and early probes remain usable before config bootstrap.
77
+ return 60_000;
78
+ }
79
+ }
80
+ /** Non-interactive Git process environment shared by one-shot and streaming gates. */
81
+ export function gitProcessEnv(extra) {
82
+ return {
83
+ ...process.env,
84
+ ...extra,
85
+ GIT_TERMINAL_PROMPT: "0",
86
+ GCM_INTERACTIVE: "Never",
87
+ GIT_MERGE_AUTOEDIT: "no",
88
+ LC_ALL: "C",
89
+ PATH: extra?.PATH ?? process.env.PATH ?? "",
90
+ };
91
+ }
60
92
  export function createGit(cwd) {
61
- const timeoutMs = getConfig().git.timeoutMs;
62
- const git = simpleGit(cwd, {
93
+ const timeoutMs = resolveGitTimeoutMs();
94
+ const git = simpleGit(normalizeFilesystemCoordinate(cwd), {
63
95
  timeout: { block: timeoutMs, stdErr: true, stdOut: true },
64
96
  });
65
97
  git.env("GIT_TERMINAL_PROMPT", "0");
@@ -69,3 +101,75 @@ export function createGit(cwd) {
69
101
  git.env("PATH", process.env.PATH ?? "");
70
102
  return git;
71
103
  }
104
+ /**
105
+ * Asynchronous one-shot Git process boundary that preserves stdout/stderr/status facts.
106
+ * Prefer `createGit` when non-zero exits are fatal; use this when callers need
107
+ * soft status codes or stdin (simple-git drops empty-stderr non-zero exits).
108
+ * Long-lived multi-request stdin/stdout sessions use `openGitStreamingBatch`.
109
+ */
110
+ export async function runGitProcess(cwd, args, options) {
111
+ const timeoutMs = resolveGitTimeoutMs();
112
+ const env = gitProcessEnv(options?.env);
113
+ return await new Promise((resolve) => {
114
+ const child = spawn("git", [...args], {
115
+ cwd: normalizeFilesystemCoordinate(cwd),
116
+ env,
117
+ shell: false,
118
+ stdio: ["pipe", "pipe", "pipe"],
119
+ });
120
+ const stdoutChunks = [];
121
+ const stderrChunks = [];
122
+ let settled = false;
123
+ let timedOut = false;
124
+ const finish = (result) => {
125
+ if (settled)
126
+ return;
127
+ settled = true;
128
+ clearTimeout(timeout);
129
+ resolve(result);
130
+ };
131
+ const timeout = setTimeout(() => {
132
+ timedOut = true;
133
+ child.kill("SIGKILL");
134
+ }, timeoutMs);
135
+ if (options?.input !== undefined) {
136
+ child.stdin.end(options.input, "utf8");
137
+ }
138
+ else {
139
+ child.stdin.end();
140
+ }
141
+ child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
142
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
143
+ child.on("error", (error) => {
144
+ finish({
145
+ stdout: Buffer.concat(stdoutChunks).toString("utf8"),
146
+ stderr: Buffer.concat(stderrChunks).toString("utf8"),
147
+ status: null,
148
+ error,
149
+ });
150
+ });
151
+ child.on("close", (code, signal) => {
152
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8");
153
+ const stderr = Buffer.concat(stderrChunks).toString("utf8");
154
+ if (timedOut) {
155
+ finish({
156
+ stdout,
157
+ stderr,
158
+ status: null,
159
+ error: new Error("git timed out"),
160
+ });
161
+ return;
162
+ }
163
+ if (signal) {
164
+ finish({
165
+ stdout,
166
+ stderr,
167
+ status: null,
168
+ error: new Error(`git exited from signal ${signal}`),
169
+ });
170
+ return;
171
+ }
172
+ finish({ stdout, stderr, status: code });
173
+ });
174
+ });
175
+ }
@@ -32,8 +32,10 @@ export function parseUnifiedHunks(section) {
32
32
  hunks.push({
33
33
  header: current[0] ?? "",
34
34
  lines: current.slice(1),
35
- newStart: parsed.start,
36
- newEnd: parsed.end,
35
+ oldStart: parsed.oldStart,
36
+ oldEnd: parsed.oldEnd,
37
+ newStart: parsed.newStart,
38
+ newEnd: parsed.newEnd,
37
39
  });
38
40
  }
39
41
  }
@@ -49,25 +51,33 @@ export function parseUnifiedHunks(section) {
49
51
  hunks.push({
50
52
  header: current[0] ?? "",
51
53
  lines: current.slice(1),
52
- newStart: parsed.start,
53
- newEnd: parsed.end,
54
+ oldStart: parsed.oldStart,
55
+ oldEnd: parsed.oldEnd,
56
+ newStart: parsed.newStart,
57
+ newEnd: parsed.newEnd,
54
58
  });
55
59
  }
56
60
  }
57
61
  return { prelude, hunks };
58
62
  }
59
- function parseHunkRange(hunkHeader) {
60
- const match = hunkHeader.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
63
+ export function parseHunkRange(hunkHeader) {
64
+ const match = hunkHeader.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
61
65
  if (!match)
62
66
  return null;
63
- const start = Number.parseInt(match[1] ?? "", 10);
64
- const countRaw = match[2];
65
- const count = countRaw ? Number.parseInt(countRaw, 10) : 1;
66
- if (!Number.isFinite(start) || !Number.isFinite(count))
67
+ const oldStart = Number.parseInt(match[1] ?? "", 10);
68
+ const oldLines = match[2] === undefined ? 1 : Number.parseInt(match[2], 10);
69
+ const newStart = Number.parseInt(match[3] ?? "", 10);
70
+ const newLines = match[4] === undefined ? 1 : Number.parseInt(match[4], 10);
71
+ if (![oldStart, oldLines, newStart, newLines].every(Number.isFinite))
67
72
  return null;
68
- if (count <= 0)
69
- return { start, end: start - 1 };
70
- return { start, end: start + count - 1 };
73
+ return {
74
+ oldStart,
75
+ oldLines,
76
+ oldEnd: oldLines <= 0 ? oldStart - 1 : oldStart + oldLines - 1,
77
+ newStart,
78
+ newLines,
79
+ newEnd: newLines <= 0 ? newStart - 1 : newStart + newLines - 1,
80
+ };
71
81
  }
72
82
  export function isDeletedDiffSection(section) {
73
83
  return section.some((line) => line.startsWith("deleted file mode ") || line.startsWith("+++ /dev/null"));
@@ -0,0 +1,127 @@
1
+ import { createGit, wrapGitError } from "../core.js";
2
+ import { diffPathspecArgs } from "./pathspec.js";
3
+ import { parseHunkRange, parseUnifiedHunks, splitDiffByFile } from "./parsers.js";
4
+ function parseRawDiff(output) {
5
+ const fields = output.split("\0");
6
+ const files = [];
7
+ let index = 0;
8
+ while (index < fields.length) {
9
+ const header = fields[index++];
10
+ if (!header)
11
+ continue;
12
+ const statusToken = header.trim().split(/\s+/).at(-1) ?? "";
13
+ const status = statusToken[0];
14
+ if (status !== "A" && status !== "M" && status !== "D" && status !== "R" && status !== "C" && status !== "T" && status !== "U")
15
+ continue;
16
+ const firstPath = fields[index++] ?? "";
17
+ if (status === "R" || status === "C") {
18
+ const path = fields[index++] ?? "";
19
+ files.push({
20
+ oldPath: firstPath,
21
+ path,
22
+ status,
23
+ similarity: Number.parseInt(statusToken.slice(1), 10) || 0,
24
+ });
25
+ }
26
+ else {
27
+ files.push({ path: firstPath, status });
28
+ }
29
+ }
30
+ return files;
31
+ }
32
+ function withoutTerminalPatchDelimiter(patch) {
33
+ return patch.endsWith("\n") ? patch.slice(0, -1) : patch;
34
+ }
35
+ function parseNumstat(output) {
36
+ const fields = output.split("\0");
37
+ const stats = [];
38
+ let index = 0;
39
+ while (index < fields.length) {
40
+ const record = fields[index++];
41
+ if (!record)
42
+ continue;
43
+ const [added = "0", removed = "0", path = ""] = record.split("\t");
44
+ const binary = added === "-" || removed === "-";
45
+ if (path) {
46
+ stats.push({ path, insertions: binary ? 0 : Number.parseInt(added, 10) || 0, deletions: binary ? 0 : Number.parseInt(removed, 10) || 0, binary });
47
+ continue;
48
+ }
49
+ const oldPath = fields[index++] ?? "";
50
+ const newPath = fields[index++] ?? "";
51
+ stats.push({ path: newPath, oldPath, insertions: binary ? 0 : Number.parseInt(added, 10) || 0, deletions: binary ? 0 : Number.parseInt(removed, 10) || 0, binary });
52
+ }
53
+ return stats;
54
+ }
55
+ function structuredLines(raw, oldStart, newStart) {
56
+ let oldLine = oldStart;
57
+ let newLine = newStart;
58
+ const lines = [];
59
+ for (const line of raw) {
60
+ if (line.startsWith("\"))
61
+ continue;
62
+ if (line.startsWith("+")) {
63
+ lines.push({ kind: "addition", content: line.slice(1), oldLine: null, newLine: newLine++ });
64
+ }
65
+ else if (line.startsWith("-")) {
66
+ lines.push({ kind: "deletion", content: line.slice(1), oldLine: oldLine++, newLine: null });
67
+ }
68
+ else {
69
+ lines.push({ kind: "context", content: line.startsWith(" ") ? line.slice(1) : line, oldLine: oldLine++, newLine: newLine++ });
70
+ }
71
+ }
72
+ return lines;
73
+ }
74
+ function parsePatchHunks(section) {
75
+ return parseUnifiedHunks([...section]).hunks.flatMap((hunk, index) => {
76
+ const range = parseHunkRange(hunk.header);
77
+ if (!range)
78
+ return [];
79
+ return [{
80
+ index,
81
+ header: hunk.header,
82
+ oldStart: range.oldStart,
83
+ oldLines: range.oldLines,
84
+ newStart: range.newStart,
85
+ newLines: range.newLines,
86
+ lines: structuredLines(hunk.lines, range.oldStart, range.newStart),
87
+ }];
88
+ });
89
+ }
90
+ export async function readStructuredDiff(cwd, from, to, options = {}) {
91
+ const git = createGit(cwd);
92
+ const range = `${from}..${to}`;
93
+ const pathspec = diffPathspecArgs({ excludePaths: options.excludePaths });
94
+ try {
95
+ const [raw, numstat, patch] = await Promise.all([
96
+ git.raw(["diff", "--raw", "-z", "--find-renames", range, ...pathspec]),
97
+ git.raw(["diff", "--numstat", "-z", "--find-renames", range, ...pathspec]),
98
+ git.raw(["diff", "--no-color", "--no-ext-diff", "--unified=3", "--find-renames", range, ...pathspec]),
99
+ ]);
100
+ const rawFiles = parseRawDiff(raw);
101
+ const stats = parseNumstat(numstat);
102
+ const sections = splitDiffByFile(withoutTerminalPatchDelimiter(patch));
103
+ const files = rawFiles.map((file, index) => {
104
+ const stat = stats[index];
105
+ return {
106
+ ...file,
107
+ insertions: stat?.insertions ?? 0,
108
+ deletions: stat?.deletions ?? 0,
109
+ binary: stat?.binary ?? false,
110
+ hunks: parsePatchHunks(sections[index] ?? []),
111
+ };
112
+ });
113
+ return {
114
+ from,
115
+ to,
116
+ files,
117
+ stat: {
118
+ filesChanged: files.length,
119
+ insertions: files.reduce((sum, file) => sum + file.insertions, 0),
120
+ deletions: files.reduce((sum, file) => sum + file.deletions, 0),
121
+ },
122
+ };
123
+ }
124
+ catch (error) {
125
+ throw wrapGitError(`diff --find-renames ${range}`, error, cwd);
126
+ }
127
+ }
@@ -0,0 +1,17 @@
1
+ import * as fs from "node:fs";
2
+ import * as fsPromises from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import { normalizeFilesystemCoordinate } from "../fs/path-coordinate.js";
5
+ export function normalizeGitPath(raw, platform = process.platform) {
6
+ return normalizeFilesystemCoordinate(raw, platform);
7
+ }
8
+ export function gitCommonRootPath(raw, platform = process.platform) {
9
+ const paths = platform === "win32" ? path.win32 : path.posix;
10
+ return paths.dirname(normalizeFilesystemCoordinate(raw, platform));
11
+ }
12
+ export async function resolveGitCommonRoot(raw) {
13
+ return await fsPromises.realpath(gitCommonRootPath(raw));
14
+ }
15
+ export function resolveGitCommonRootSync(raw) {
16
+ return fs.realpathSync(gitCommonRootPath(raw));
17
+ }
@@ -0,0 +1,57 @@
1
+ import { createGit, errorContainsAnyPattern, MISSING_HEAD_PATTERNS, wrapGitError } from "./core.js";
2
+ const ZERO_OBJECT_ID = "0000000000000000000000000000000000000000";
3
+ async function resolveRefOrNull(cwd, ref) {
4
+ const git = createGit(cwd);
5
+ try {
6
+ return (await git.raw(["rev-parse", "--verify", ref])).trim();
7
+ }
8
+ catch (error) {
9
+ if (errorContainsAnyPattern(error, MISSING_HEAD_PATTERNS))
10
+ return null;
11
+ throw wrapGitError(`rev-parse ${ref}`, error, cwd);
12
+ }
13
+ }
14
+ /** Create an immutable recovery ref, accepting an earlier identical attempt. */
15
+ export async function createRecoveryRef(cwd, ref, objectId) {
16
+ const existing = await resolveRefOrNull(cwd, ref);
17
+ if (existing === objectId)
18
+ return;
19
+ if (existing !== null) {
20
+ throw new Error(`recovery ref ${ref} already resolves to ${existing}, not ${objectId}`);
21
+ }
22
+ const git = createGit(cwd);
23
+ try {
24
+ await git.raw(["update-ref", ref, objectId, ZERO_OBJECT_ID]);
25
+ }
26
+ catch (error) {
27
+ // Another identical attempt may have won the create race.
28
+ if (await resolveRefOrNull(cwd, ref) === objectId)
29
+ return;
30
+ throw wrapGitError(`update-ref ${ref}`, error, cwd);
31
+ }
32
+ }
33
+ /** Remove only the exact attempt ref, returning truthful non-fatal cleanup facts. */
34
+ export async function deleteRecoveryRef(cwd, ref, objectId) {
35
+ try {
36
+ const existing = await resolveRefOrNull(cwd, ref);
37
+ if (existing === null)
38
+ return { ref, status: "already-absent" };
39
+ if (existing !== objectId) {
40
+ return {
41
+ ref,
42
+ status: "retained",
43
+ diagnostic: `ref resolves to ${existing}, not expected object ${objectId}`,
44
+ };
45
+ }
46
+ const git = createGit(cwd);
47
+ await git.raw(["update-ref", "-d", ref, objectId]);
48
+ return { ref, status: "removed" };
49
+ }
50
+ catch (error) {
51
+ return {
52
+ ref,
53
+ status: "retained",
54
+ diagnostic: wrapGitError(`update-ref -d ${ref}`, error, cwd).message,
55
+ };
56
+ }
57
+ }