apex-code 0.0.1-alpha.9 → 0.0.4

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 (513) hide show
  1. package/CHANGELOG.md +150 -2
  2. package/README.md +9 -8
  3. package/dist/cli/args.d.ts +25 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +43 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/mcp-command.d.ts +9 -0
  8. package/dist/cli/mcp-command.d.ts.map +1 -0
  9. package/dist/cli/mcp-command.js +53 -0
  10. package/dist/cli/mcp-command.js.map +1 -0
  11. package/dist/cli/project-trust.d.ts.map +1 -1
  12. package/dist/cli/project-trust.js +3 -1
  13. package/dist/cli/project-trust.js.map +1 -1
  14. package/dist/cli/startup-ui.d.ts.map +1 -1
  15. package/dist/cli/startup-ui.js +2 -1
  16. package/dist/cli/startup-ui.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +94 -5
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +24 -5
  22. package/dist/config.js.map +1 -1
  23. package/dist/core/agent-session-services.d.ts +3 -0
  24. package/dist/core/agent-session-services.d.ts.map +1 -1
  25. package/dist/core/agent-session-services.js +1 -0
  26. package/dist/core/agent-session-services.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +157 -8
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +565 -22
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts.map +1 -1
  32. package/dist/core/bash-executor.js +7 -0
  33. package/dist/core/bash-executor.js.map +1 -1
  34. package/dist/core/checkpoints/git-checkpoints.d.ts +39 -0
  35. package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
  36. package/dist/core/checkpoints/git-checkpoints.js +251 -0
  37. package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
  38. package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
  39. package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
  40. package/dist/core/checkpoints/session-checkpoints.js +36 -0
  41. package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
  42. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  43. package/dist/core/compaction/branch-summarization.js +4 -3
  44. package/dist/core/compaction/branch-summarization.js.map +1 -1
  45. package/dist/core/compaction/compaction.d.ts +5 -0
  46. package/dist/core/compaction/compaction.d.ts.map +1 -1
  47. package/dist/core/compaction/compaction.js +19 -5
  48. package/dist/core/compaction/compaction.js.map +1 -1
  49. package/dist/core/delegation/runtime.d.ts +6 -5
  50. package/dist/core/delegation/runtime.d.ts.map +1 -1
  51. package/dist/core/delegation/runtime.js +4 -3
  52. package/dist/core/delegation/runtime.js.map +1 -1
  53. package/dist/core/exec.d.ts.map +1 -1
  54. package/dist/core/exec.js +5 -2
  55. package/dist/core/exec.js.map +1 -1
  56. package/dist/core/extensions/index.d.ts +1 -1
  57. package/dist/core/extensions/index.d.ts.map +1 -1
  58. package/dist/core/extensions/index.js.map +1 -1
  59. package/dist/core/extensions/loader.d.ts.map +1 -1
  60. package/dist/core/extensions/loader.js +8 -2
  61. package/dist/core/extensions/loader.js.map +1 -1
  62. package/dist/core/extensions/runner.d.ts +5 -0
  63. package/dist/core/extensions/runner.d.ts.map +1 -1
  64. package/dist/core/extensions/runner.js +45 -1
  65. package/dist/core/extensions/runner.js.map +1 -1
  66. package/dist/core/extensions/source-runtime.d.ts +14 -0
  67. package/dist/core/extensions/source-runtime.d.ts.map +1 -0
  68. package/dist/core/extensions/source-runtime.js +56 -0
  69. package/dist/core/extensions/source-runtime.js.map +1 -0
  70. package/dist/core/extensions/types.d.ts +22 -2
  71. package/dist/core/extensions/types.d.ts.map +1 -1
  72. package/dist/core/extensions/types.js.map +1 -1
  73. package/dist/core/formatter-lifecycle.d.ts +69 -0
  74. package/dist/core/formatter-lifecycle.d.ts.map +1 -0
  75. package/dist/core/formatter-lifecycle.js +360 -0
  76. package/dist/core/formatter-lifecycle.js.map +1 -0
  77. package/dist/core/hooks/command-handler.d.ts +19 -0
  78. package/dist/core/hooks/command-handler.d.ts.map +1 -0
  79. package/dist/core/hooks/command-handler.js +112 -0
  80. package/dist/core/hooks/command-handler.js.map +1 -0
  81. package/dist/core/hooks/http-handler.d.ts +10 -0
  82. package/dist/core/hooks/http-handler.d.ts.map +1 -0
  83. package/dist/core/hooks/http-handler.js +36 -0
  84. package/dist/core/hooks/http-handler.js.map +1 -0
  85. package/dist/core/hooks/loader.d.ts +29 -0
  86. package/dist/core/hooks/loader.d.ts.map +1 -0
  87. package/dist/core/hooks/loader.js +139 -0
  88. package/dist/core/hooks/loader.js.map +1 -0
  89. package/dist/core/hooks/runtime.d.ts +13 -0
  90. package/dist/core/hooks/runtime.d.ts.map +1 -0
  91. package/dist/core/hooks/runtime.js +63 -0
  92. package/dist/core/hooks/runtime.js.map +1 -0
  93. package/dist/core/hooks/types.d.ts +83 -0
  94. package/dist/core/hooks/types.d.ts.map +1 -0
  95. package/dist/core/hooks/types.js +24 -0
  96. package/dist/core/hooks/types.js.map +1 -0
  97. package/dist/core/mcp/config.d.ts +16 -0
  98. package/dist/core/mcp/config.d.ts.map +1 -0
  99. package/dist/core/mcp/config.js +166 -0
  100. package/dist/core/mcp/config.js.map +1 -0
  101. package/dist/core/mcp/connector.d.ts +35 -0
  102. package/dist/core/mcp/connector.d.ts.map +1 -0
  103. package/dist/core/mcp/connector.js +105 -0
  104. package/dist/core/mcp/connector.js.map +1 -0
  105. package/dist/core/mcp/contract.d.ts +19 -0
  106. package/dist/core/mcp/contract.d.ts.map +1 -0
  107. package/dist/core/mcp/contract.js +89 -0
  108. package/dist/core/mcp/contract.js.map +1 -0
  109. package/dist/core/mcp/mcp-tool.d.ts +32 -0
  110. package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
  111. package/dist/core/mcp/mcp-tool.js +171 -0
  112. package/dist/core/mcp/mcp-tool.js.map +1 -0
  113. package/dist/core/mcp/metadata-cache.d.ts +29 -0
  114. package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
  115. package/dist/core/mcp/metadata-cache.js +99 -0
  116. package/dist/core/mcp/metadata-cache.js.map +1 -0
  117. package/dist/core/mcp/oauth/authorize.d.ts +21 -0
  118. package/dist/core/mcp/oauth/authorize.d.ts.map +1 -0
  119. package/dist/core/mcp/oauth/authorize.js +32 -0
  120. package/dist/core/mcp/oauth/authorize.js.map +1 -0
  121. package/dist/core/mcp/oauth/discover.d.ts +27 -0
  122. package/dist/core/mcp/oauth/discover.d.ts.map +1 -0
  123. package/dist/core/mcp/oauth/discover.js +116 -0
  124. package/dist/core/mcp/oauth/discover.js.map +1 -0
  125. package/dist/core/mcp/oauth/flow.d.ts +31 -0
  126. package/dist/core/mcp/oauth/flow.d.ts.map +1 -0
  127. package/dist/core/mcp/oauth/flow.js +175 -0
  128. package/dist/core/mcp/oauth/flow.js.map +1 -0
  129. package/dist/core/mcp/oauth/mcp-token.d.ts +51 -0
  130. package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -0
  131. package/dist/core/mcp/oauth/mcp-token.js +129 -0
  132. package/dist/core/mcp/oauth/mcp-token.js.map +1 -0
  133. package/dist/core/mcp/oauth/pkce.d.ts +12 -0
  134. package/dist/core/mcp/oauth/pkce.d.ts.map +1 -0
  135. package/dist/core/mcp/oauth/pkce.js +11 -0
  136. package/dist/core/mcp/oauth/pkce.js.map +1 -0
  137. package/dist/core/mcp/oauth/register.d.ts +16 -0
  138. package/dist/core/mcp/oauth/register.d.ts.map +1 -0
  139. package/dist/core/mcp/oauth/register.js +42 -0
  140. package/dist/core/mcp/oauth/register.js.map +1 -0
  141. package/dist/core/mcp/oauth/token-client.d.ts +33 -0
  142. package/dist/core/mcp/oauth/token-client.d.ts.map +1 -0
  143. package/dist/core/mcp/oauth/token-client.js +65 -0
  144. package/dist/core/mcp/oauth/token-client.js.map +1 -0
  145. package/dist/core/mcp/runtime.d.ts +20 -0
  146. package/dist/core/mcp/runtime.d.ts.map +1 -0
  147. package/dist/core/mcp/runtime.js +37 -0
  148. package/dist/core/mcp/runtime.js.map +1 -0
  149. package/dist/core/mcp/schema.d.ts +22 -0
  150. package/dist/core/mcp/schema.d.ts.map +1 -0
  151. package/dist/core/mcp/schema.js +20 -0
  152. package/dist/core/mcp/schema.js.map +1 -0
  153. package/dist/core/mcp/server-manager.d.ts +43 -0
  154. package/dist/core/mcp/server-manager.d.ts.map +1 -0
  155. package/dist/core/mcp/server-manager.js +142 -0
  156. package/dist/core/mcp/server-manager.js.map +1 -0
  157. package/dist/core/mcp/types.d.ts +100 -0
  158. package/dist/core/mcp/types.d.ts.map +1 -0
  159. package/dist/core/mcp/types.js +8 -0
  160. package/dist/core/mcp/types.js.map +1 -0
  161. package/dist/core/permissions/gate.d.ts.map +1 -1
  162. package/dist/core/permissions/gate.js +45 -4
  163. package/dist/core/permissions/gate.js.map +1 -1
  164. package/dist/core/permissions/operations.d.ts +20 -0
  165. package/dist/core/permissions/operations.d.ts.map +1 -0
  166. package/dist/core/permissions/operations.js +9 -0
  167. package/dist/core/permissions/operations.js.map +1 -0
  168. package/dist/core/permissions/policy-command.d.ts +42 -0
  169. package/dist/core/permissions/policy-command.d.ts.map +1 -0
  170. package/dist/core/permissions/policy-command.js +44 -0
  171. package/dist/core/permissions/policy-command.js.map +1 -0
  172. package/dist/core/permissions/responder.d.ts +49 -2
  173. package/dist/core/permissions/responder.d.ts.map +1 -1
  174. package/dist/core/permissions/responder.js +20 -8
  175. package/dist/core/permissions/responder.js.map +1 -1
  176. package/dist/core/permissions/rules.d.ts.map +1 -1
  177. package/dist/core/permissions/rules.js +11 -1
  178. package/dist/core/permissions/rules.js.map +1 -1
  179. package/dist/core/permissions/store.d.ts +18 -0
  180. package/dist/core/permissions/store.d.ts.map +1 -1
  181. package/dist/core/permissions/store.js +89 -8
  182. package/dist/core/permissions/store.js.map +1 -1
  183. package/dist/core/policy-executor.d.ts +47 -0
  184. package/dist/core/policy-executor.d.ts.map +1 -0
  185. package/dist/core/policy-executor.js +228 -0
  186. package/dist/core/policy-executor.js.map +1 -0
  187. package/dist/core/policy-loader.d.ts +72 -0
  188. package/dist/core/policy-loader.d.ts.map +1 -0
  189. package/dist/core/policy-loader.js +195 -0
  190. package/dist/core/policy-loader.js.map +1 -0
  191. package/dist/core/project-trust.d.ts +1 -1
  192. package/dist/core/project-trust.d.ts.map +1 -1
  193. package/dist/core/project-trust.js.map +1 -1
  194. package/dist/core/sandbox/bwrap-arguments.d.ts +41 -0
  195. package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
  196. package/dist/core/sandbox/bwrap-arguments.js +136 -0
  197. package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
  198. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  199. package/dist/core/sandbox/child-entry.js +3 -1
  200. package/dist/core/sandbox/child-entry.js.map +1 -1
  201. package/dist/core/sandbox/cli-launch.d.ts +84 -4
  202. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  203. package/dist/core/sandbox/cli-launch.js +115 -16
  204. package/dist/core/sandbox/cli-launch.js.map +1 -1
  205. package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
  206. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  207. package/dist/core/sandbox/cli-supervisor.js +50 -2
  208. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  209. package/dist/core/sandbox/full-access.d.ts +28 -0
  210. package/dist/core/sandbox/full-access.d.ts.map +1 -0
  211. package/dist/core/sandbox/full-access.js +63 -0
  212. package/dist/core/sandbox/full-access.js.map +1 -0
  213. package/dist/core/sandbox/git-identity.d.ts +57 -0
  214. package/dist/core/sandbox/git-identity.d.ts.map +1 -0
  215. package/dist/core/sandbox/git-identity.js +78 -0
  216. package/dist/core/sandbox/git-identity.js.map +1 -0
  217. package/dist/core/sandbox/host-approval.d.ts +42 -0
  218. package/dist/core/sandbox/host-approval.d.ts.map +1 -0
  219. package/dist/core/sandbox/host-approval.js +99 -0
  220. package/dist/core/sandbox/host-approval.js.map +1 -0
  221. package/dist/core/sandbox/linux-backend.d.ts +14 -0
  222. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  223. package/dist/core/sandbox/linux-backend.js +158 -80
  224. package/dist/core/sandbox/linux-backend.js.map +1 -1
  225. package/dist/core/sandbox/macos-backend.d.ts +5 -0
  226. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  227. package/dist/core/sandbox/macos-backend.js +164 -5
  228. package/dist/core/sandbox/macos-backend.js.map +1 -1
  229. package/dist/core/sandbox/network-proxy.d.ts +19 -0
  230. package/dist/core/sandbox/network-proxy.d.ts.map +1 -1
  231. package/dist/core/sandbox/network-proxy.js +93 -19
  232. package/dist/core/sandbox/network-proxy.js.map +1 -1
  233. package/dist/core/sandbox/policy.d.ts +8 -0
  234. package/dist/core/sandbox/policy.d.ts.map +1 -1
  235. package/dist/core/sandbox/policy.js +29 -12
  236. package/dist/core/sandbox/policy.js.map +1 -1
  237. package/dist/core/sandbox/profiles.d.ts +25 -0
  238. package/dist/core/sandbox/profiles.d.ts.map +1 -0
  239. package/dist/core/sandbox/profiles.js +23 -0
  240. package/dist/core/sandbox/profiles.js.map +1 -0
  241. package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
  242. package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
  243. package/dist/core/sandbox/rpc/command-client.js +86 -0
  244. package/dist/core/sandbox/rpc/command-client.js.map +1 -0
  245. package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
  246. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
  247. package/dist/core/sandbox/rpc/command-proxy.js +120 -0
  248. package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
  249. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -1
  250. package/dist/core/sandbox/rpc/credential-proxy.js +2 -96
  251. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -1
  252. package/dist/core/sandbox/rpc/framing.d.ts +35 -0
  253. package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
  254. package/dist/core/sandbox/rpc/framing.js +114 -0
  255. package/dist/core/sandbox/rpc/framing.js.map +1 -0
  256. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +47 -0
  257. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
  258. package/dist/core/sandbox/rpc/git-credential-helper.js +222 -0
  259. package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
  260. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +66 -0
  261. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
  262. package/dist/core/sandbox/rpc/git-credential-proxy.js +209 -0
  263. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
  264. package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
  265. package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
  266. package/dist/core/sandbox/supervisor-temp.js +21 -0
  267. package/dist/core/sandbox/supervisor-temp.js.map +1 -0
  268. package/dist/core/sandbox/supervisor.d.ts +12 -1
  269. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  270. package/dist/core/sandbox/supervisor.js +15 -1
  271. package/dist/core/sandbox/supervisor.js.map +1 -1
  272. package/dist/core/sandbox/terminal-handoff.d.ts +68 -0
  273. package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
  274. package/dist/core/sandbox/terminal-handoff.js +221 -0
  275. package/dist/core/sandbox/terminal-handoff.js.map +1 -0
  276. package/dist/core/sandbox/terminal-size.d.ts +13 -0
  277. package/dist/core/sandbox/terminal-size.d.ts.map +1 -1
  278. package/dist/core/sandbox/terminal-size.js +23 -2
  279. package/dist/core/sandbox/terminal-size.js.map +1 -1
  280. package/dist/core/sdk.d.ts +22 -0
  281. package/dist/core/sdk.d.ts.map +1 -1
  282. package/dist/core/sdk.js +53 -1
  283. package/dist/core/sdk.js.map +1 -1
  284. package/dist/core/session-manager.d.ts +13 -1
  285. package/dist/core/session-manager.d.ts.map +1 -1
  286. package/dist/core/session-manager.js +32 -5
  287. package/dist/core/session-manager.js.map +1 -1
  288. package/dist/core/settings-manager.d.ts +163 -1
  289. package/dist/core/settings-manager.d.ts.map +1 -1
  290. package/dist/core/settings-manager.js +107 -0
  291. package/dist/core/settings-manager.js.map +1 -1
  292. package/dist/core/slash-commands.d.ts.map +1 -1
  293. package/dist/core/slash-commands.js +2 -0
  294. package/dist/core/slash-commands.js.map +1 -1
  295. package/dist/core/tools/background-shell.d.ts +39 -0
  296. package/dist/core/tools/background-shell.d.ts.map +1 -0
  297. package/dist/core/tools/background-shell.js +81 -0
  298. package/dist/core/tools/background-shell.js.map +1 -0
  299. package/dist/core/tools/bash-command-segments.d.ts.map +1 -1
  300. package/dist/core/tools/bash-command-segments.js +2 -0
  301. package/dist/core/tools/bash-command-segments.js.map +1 -1
  302. package/dist/core/tools/bash.d.ts +30 -15
  303. package/dist/core/tools/bash.d.ts.map +1 -1
  304. package/dist/core/tools/bash.js +228 -12
  305. package/dist/core/tools/bash.js.map +1 -1
  306. package/dist/core/tools/contract-snapshot.d.ts +58 -0
  307. package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
  308. package/dist/core/tools/contract-snapshot.js +51 -0
  309. package/dist/core/tools/contract-snapshot.js.map +1 -0
  310. package/dist/core/tools/contract.d.ts +53 -5
  311. package/dist/core/tools/contract.d.ts.map +1 -1
  312. package/dist/core/tools/contract.js +22 -2
  313. package/dist/core/tools/contract.js.map +1 -1
  314. package/dist/core/tools/delegate.d.ts +6 -4
  315. package/dist/core/tools/delegate.d.ts.map +1 -1
  316. package/dist/core/tools/delegate.js +21 -11
  317. package/dist/core/tools/delegate.js.map +1 -1
  318. package/dist/core/tools/edit-diff.d.ts +48 -0
  319. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  320. package/dist/core/tools/edit-diff.js +215 -3
  321. package/dist/core/tools/edit-diff.js.map +1 -1
  322. package/dist/core/tools/edit.d.ts +8 -0
  323. package/dist/core/tools/edit.d.ts.map +1 -1
  324. package/dist/core/tools/edit.js +53 -7
  325. package/dist/core/tools/edit.js.map +1 -1
  326. package/dist/core/tools/find.d.ts.map +1 -1
  327. package/dist/core/tools/find.js +5 -2
  328. package/dist/core/tools/find.js.map +1 -1
  329. package/dist/core/tools/grep.d.ts.map +1 -1
  330. package/dist/core/tools/grep.js +5 -2
  331. package/dist/core/tools/grep.js.map +1 -1
  332. package/dist/core/tools/index.d.ts +10 -1
  333. package/dist/core/tools/index.d.ts.map +1 -1
  334. package/dist/core/tools/index.js +3 -0
  335. package/dist/core/tools/index.js.map +1 -1
  336. package/dist/core/tools/ls.d.ts.map +1 -1
  337. package/dist/core/tools/ls.js +5 -2
  338. package/dist/core/tools/ls.js.map +1 -1
  339. package/dist/core/tools/lsp.d.ts +19 -1
  340. package/dist/core/tools/lsp.d.ts.map +1 -1
  341. package/dist/core/tools/lsp.js +163 -10
  342. package/dist/core/tools/lsp.js.map +1 -1
  343. package/dist/core/tools/path-permission.d.ts +3 -0
  344. package/dist/core/tools/path-permission.d.ts.map +1 -1
  345. package/dist/core/tools/path-permission.js +16 -4
  346. package/dist/core/tools/path-permission.js.map +1 -1
  347. package/dist/core/tools/path-utils.d.ts +27 -2
  348. package/dist/core/tools/path-utils.d.ts.map +1 -1
  349. package/dist/core/tools/path-utils.js +221 -4
  350. package/dist/core/tools/path-utils.js.map +1 -1
  351. package/dist/core/tools/read.d.ts.map +1 -1
  352. package/dist/core/tools/read.js +20 -11
  353. package/dist/core/tools/read.js.map +1 -1
  354. package/dist/core/tools/test.d.ts +54 -9
  355. package/dist/core/tools/test.d.ts.map +1 -1
  356. package/dist/core/tools/test.js +239 -19
  357. package/dist/core/tools/test.js.map +1 -1
  358. package/dist/core/tools/write.d.ts +7 -0
  359. package/dist/core/tools/write.d.ts.map +1 -1
  360. package/dist/core/tools/write.js +64 -9
  361. package/dist/core/tools/write.js.map +1 -1
  362. package/dist/core/verification-lifecycle.d.ts +77 -0
  363. package/dist/core/verification-lifecycle.d.ts.map +1 -0
  364. package/dist/core/verification-lifecycle.js +146 -0
  365. package/dist/core/verification-lifecycle.js.map +1 -0
  366. package/dist/core/workspace/artifacts.d.ts +45 -0
  367. package/dist/core/workspace/artifacts.d.ts.map +1 -0
  368. package/dist/core/workspace/artifacts.js +165 -0
  369. package/dist/core/workspace/artifacts.js.map +1 -0
  370. package/dist/core/workspace/comparison.d.ts +27 -0
  371. package/dist/core/workspace/comparison.d.ts.map +1 -0
  372. package/dist/core/workspace/comparison.js +94 -0
  373. package/dist/core/workspace/comparison.js.map +1 -0
  374. package/dist/core/workspace/git-observer.d.ts +48 -0
  375. package/dist/core/workspace/git-observer.d.ts.map +1 -0
  376. package/dist/core/workspace/git-observer.js +346 -0
  377. package/dist/core/workspace/git-observer.js.map +1 -0
  378. package/dist/core/workspace/projection.d.ts +3 -0
  379. package/dist/core/workspace/projection.d.ts.map +1 -0
  380. package/dist/core/workspace/projection.js +56 -0
  381. package/dist/core/workspace/projection.js.map +1 -0
  382. package/dist/core/workspace/state.d.ts +133 -0
  383. package/dist/core/workspace/state.d.ts.map +1 -0
  384. package/dist/core/workspace/state.js +96 -0
  385. package/dist/core/workspace/state.js.map +1 -0
  386. package/dist/extensions/llama/client.d.ts +6 -0
  387. package/dist/extensions/llama/client.d.ts.map +1 -1
  388. package/dist/extensions/llama/client.js +7 -0
  389. package/dist/extensions/llama/client.js.map +1 -1
  390. package/dist/extensions/llama/provider.d.ts +3 -1
  391. package/dist/extensions/llama/provider.d.ts.map +1 -1
  392. package/dist/extensions/llama/provider.js +27 -6
  393. package/dist/extensions/llama/provider.js.map +1 -1
  394. package/dist/index.d.ts +4 -1
  395. package/dist/index.d.ts.map +1 -1
  396. package/dist/index.js +3 -0
  397. package/dist/index.js.map +1 -1
  398. package/dist/main.d.ts +3 -1
  399. package/dist/main.d.ts.map +1 -1
  400. package/dist/main.js +41 -9
  401. package/dist/main.js.map +1 -1
  402. package/dist/modes/acp/server.d.ts +55 -0
  403. package/dist/modes/acp/server.d.ts.map +1 -0
  404. package/dist/modes/acp/server.js +244 -0
  405. package/dist/modes/acp/server.js.map +1 -0
  406. package/dist/modes/acp/translate.d.ts +37 -0
  407. package/dist/modes/acp/translate.d.ts.map +1 -0
  408. package/dist/modes/acp/translate.js +50 -0
  409. package/dist/modes/acp/translate.js.map +1 -0
  410. package/dist/modes/index.d.ts +1 -0
  411. package/dist/modes/index.d.ts.map +1 -1
  412. package/dist/modes/index.js +1 -0
  413. package/dist/modes/index.js.map +1 -1
  414. package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
  415. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/assistant-message.js +182 -23
  417. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  418. package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
  419. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/bash-execution.js +20 -0
  421. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  422. package/dist/modes/interactive/components/custom-editor.d.ts +9 -4
  423. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/custom-editor.js +20 -9
  425. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  426. package/dist/modes/interactive/components/extension-selector.d.ts +7 -1
  427. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/extension-selector.js +7 -3
  429. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  430. package/dist/modes/interactive/components/footer.d.ts +19 -0
  431. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/footer.js +70 -10
  433. package/dist/modes/interactive/components/footer.js.map +1 -1
  434. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/model-selector.js +6 -6
  436. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  437. package/dist/modes/interactive/components/permission-preview.d.ts +15 -0
  438. package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -0
  439. package/dist/modes/interactive/components/permission-preview.js +33 -0
  440. package/dist/modes/interactive/components/permission-preview.js.map +1 -0
  441. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  442. package/dist/modes/interactive/components/session-selector.js +2 -2
  443. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  444. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  445. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  446. package/dist/modes/interactive/components/settings-selector.js +10 -0
  447. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  448. package/dist/modes/interactive/components/tool-execution.d.ts +23 -1
  449. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/tool-execution.js +64 -10
  451. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  452. package/dist/modes/interactive/interactive-mode.d.ts +34 -0
  453. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  454. package/dist/modes/interactive/interactive-mode.js +231 -26
  455. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  456. package/dist/modes/interactive/theme/theme.d.ts +12 -0
  457. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  458. package/dist/modes/interactive/theme/theme.js +15 -5
  459. package/dist/modes/interactive/theme/theme.js.map +1 -1
  460. package/dist/modes/print-mode.d.ts.map +1 -1
  461. package/dist/modes/print-mode.js +27 -12
  462. package/dist/modes/print-mode.js.map +1 -1
  463. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  464. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  465. package/dist/modes/rpc/rpc-client.js +7 -0
  466. package/dist/modes/rpc/rpc-client.js.map +1 -1
  467. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  468. package/dist/modes/rpc/rpc-mode.js +3 -0
  469. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  470. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  471. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  472. package/dist/modes/rpc/rpc-types.js.map +1 -1
  473. package/dist/package-manager-cli.d.ts.map +1 -1
  474. package/dist/package-manager-cli.js +0 -6
  475. package/dist/package-manager-cli.js.map +1 -1
  476. package/dist/utils/shell.d.ts.map +1 -1
  477. package/dist/utils/shell.js +6 -4
  478. package/dist/utils/shell.js.map +1 -1
  479. package/dist/utils/version-check.d.ts.map +1 -1
  480. package/dist/utils/version-check.js +6 -4
  481. package/dist/utils/version-check.js.map +1 -1
  482. package/docs/compaction.md +4 -0
  483. package/docs/custom-provider.md +7 -7
  484. package/docs/environment-variables.md +1 -1
  485. package/docs/extensions.md +18 -0
  486. package/docs/keybindings.md +1 -1
  487. package/docs/quickstart.md +1 -1
  488. package/docs/rpc.md +23 -0
  489. package/docs/sdk.md +25 -0
  490. package/docs/settings.md +12 -4
  491. package/docs/terminal-setup.md +12 -0
  492. package/docs/usage.md +4 -3
  493. package/examples/extensions/built-in-tool-renderer.ts +8 -4
  494. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  495. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  496. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  497. package/examples/extensions/git-checkpoint.ts +24 -36
  498. package/examples/extensions/gondolin/package-lock.json +2 -2
  499. package/examples/extensions/gondolin/package.json +1 -1
  500. package/examples/extensions/minimal-mode.ts +2 -2
  501. package/examples/extensions/permission-gate.ts +1 -1
  502. package/examples/extensions/plan-mode/index.ts +1 -1
  503. package/examples/extensions/rpc-demo.ts +1 -1
  504. package/examples/extensions/sandbox/package-lock.json +2 -2
  505. package/examples/extensions/sandbox/package.json +1 -1
  506. package/examples/extensions/with-deps/package-lock.json +2 -2
  507. package/examples/extensions/with-deps/package.json +1 -1
  508. package/npm-shrinkwrap.json +194 -46
  509. package/package.json +14 -12
  510. package/dist/server/create-harness.d.ts +0 -26
  511. package/dist/server/create-harness.d.ts.map +0 -1
  512. package/dist/server/create-harness.js +0 -107
  513. package/dist/server/create-harness.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-observer.d.ts","sourceRoot":"","sources":["../../../src/core/workspace/git-observer.ts"],"names":[],"mappings":"AAIA,OAAO,EAIN,KAAK,oBAAoB,EACzB,MAAM,YAAY,CAAC;AAoBpB,MAAM,WAAW,iBAAiB;IACjC,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAmLD;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IAClC,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC9B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,GACC,kBAAkB,CA2BpB;AAED,wBAAsB,mBAAmB,CACxC,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAyI/B","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { lstatSync, readFileSync, readlinkSync, realpathSync } from \"node:fs\";\nimport { dirname, isAbsolute, win32 as pathWin32, relative, sep } from \"node:path\";\nimport {\n\tWORKSPACE_RECORD_VERSION,\n\ttype WorkspaceStateCoverage,\n\ttype WorkspaceStatePath,\n\ttype WorkspaceStateRecord,\n} from \"./state.ts\";\n\n/**\n * Read-only Git workspace observation (spec 2026-09-01-harness-correctness-\n * and-workspace-state.md § 1).\n *\n * The adapter never mutates repository state: `HEAD`, the current branch, the\n * index, the worktree, the stash, and local config are only read. Optional\n * index locks are disabled (`GIT_OPTIONAL_LOCKS=0`) so even `git status`\n * cannot refresh the on-disk index. It never walks the workspace; all path\n * knowledge comes from git. Symlinks are hashed as link text (what git\n * stores), never followed. Submodule (gitlink) paths carry no worktree hash.\n */\n\nconst DEFAULT_MAX_PATHS = 200;\nconst DEFAULT_MAX_HASH_BYTES = 5 * 1024 * 1024;\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst GIT_CONFIG = [\"-c\", \"core.autocrlf=false\", \"-c\", \"status.renames=true\"];\nconst UNMERGED_XY = new Set([\"DD\", \"AU\", \"UD\", \"UA\", \"DU\", \"AA\", \"UU\"]);\n\nexport interface GitObserveOptions {\n\t/** Compute per-path content hashes of present files (default true). */\n\thashPaths?: boolean;\n\t/** Maximum number of reported paths (default 200). */\n\tmaxPaths?: number;\n\t/** Skip hashing files larger than this many bytes (default 5 MiB). */\n\tmaxHashBytes?: number;\n\t/** Per-command timeout in milliseconds (default 10 000). */\n\ttimeoutMs?: number;\n\t/** Cancels the observation; the record reports `failed`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Repository-relative path prefixes to omit from `paths` (exact match or\n\t * directory prefix). Used for harness bookkeeping that lives inside the\n\t * workspace — e.g. the session directory — which is not workspace state.\n\t */\n\texcludePaths?: string[];\n}\n\ninterface GitRunResult {\n\tok: boolean;\n\tcode: number | null;\n\tstdout: Buffer;\n\tstderr: string;\n\ttimedOut: boolean;\n\tspawnError: boolean;\n}\n\nfunction runGit(cwd: string, args: string[], timeoutMs: number, signal?: AbortSignal): Promise<GitRunResult> {\n\treturn new Promise((resolve) => {\n\t\tlet stdout = Buffer.alloc(0);\n\t\tlet stderr = \"\";\n\t\tlet timedOut = false;\n\t\tlet settled = false;\n\t\tlet child: ChildProcess;\n\t\tconst finish = (code: number | null, spawnError: boolean) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve({\n\t\t\t\tok: !spawnError && !timedOut && code === 0,\n\t\t\t\tcode,\n\t\t\t\tstdout,\n\t\t\t\tstderr,\n\t\t\t\ttimedOut,\n\t\t\t\tspawnError,\n\t\t\t});\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tchild?.kill(\"SIGKILL\");\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tchild?.kill(\"SIGKILL\");\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tchild = spawn(\"git\", [...GIT_CONFIG, ...args], {\n\t\t\t\tcwd,\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t// Observation must leave the on-disk index untouched.\n\t\t\t\tenv: { ...process.env, GIT_OPTIONAL_LOCKS: \"0\" },\n\t\t\t});\n\t\t} catch {\n\t\t\tresolve({ ok: false, code: null, stdout, stderr: \"\", timedOut: false, spawnError: true });\n\t\t\treturn;\n\t\t}\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstdout = Buffer.concat([stdout, chunk]);\n\t\t});\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.on(\"error\", () => finish(null, true));\n\t\tchild.on(\"close\", (code) => finish(code, false));\n\t});\n}\n\nfunction emptyCoverage(): WorkspaceStateCoverage {\n\treturn {\n\t\ttracked: false,\n\t\tstaged: false,\n\t\tunstaged: false,\n\t\tuntracked: false,\n\t\tignored: false,\n\t\thashes: false,\n\t\tpatch: false,\n\t};\n}\n\nfunction fullCoverage(): WorkspaceStateCoverage {\n\treturn {\n\t\ttracked: true,\n\t\tstaged: true,\n\t\tunstaged: true,\n\t\tuntracked: true,\n\t\tignored: true,\n\t\thashes: true,\n\t\tpatch: false,\n\t};\n}\n\ninterface ParsedStatusPath {\n\tx: string;\n\ty: string;\n\tpath: string;\n\tpreviousPath?: string;\n}\n\nfunction parsePorcelainZ(stdout: Buffer): ParsedStatusPath[] {\n\tconst fields = stdout.toString(\"utf-8\").split(\"\\0\");\n\tconst parsed: ParsedStatusPath[] = [];\n\tlet i = 0;\n\twhile (i < fields.length) {\n\t\tconst record = fields[i++];\n\t\tif (!record || record.length < 4) continue;\n\t\tconst entry: ParsedStatusPath = { x: record[0], y: record[1], path: record.slice(3) };\n\t\tif (entry.x === \"R\" || entry.x === \"C\" || entry.y === \"R\" || entry.y === \"C\") {\n\t\t\tentry.previousPath = fields[i++];\n\t\t}\n\t\tparsed.push(entry);\n\t}\n\treturn parsed;\n}\n\nfunction classify(entry: ParsedStatusPath): {\n\tkind: WorkspaceStatePath[\"kind\"];\n\tstaged: boolean;\n\tunstaged: boolean;\n\tignored: boolean;\n} {\n\tconst { x, y } = entry;\n\tif (x === \"?\" && y === \"?\") {\n\t\treturn { kind: \"untracked\", staged: false, unstaged: false, ignored: false };\n\t}\n\tif (x === \"!\" && y === \"!\") {\n\t\treturn { kind: \"untracked\", staged: false, unstaged: false, ignored: true };\n\t}\n\tif (UNMERGED_XY.has(`${x}${y}`)) {\n\t\treturn {\n\t\t\tkind: x === \"D\" && y === \"D\" ? \"deleted\" : \"modified\",\n\t\t\tstaged: true,\n\t\t\tunstaged: true,\n\t\t\tignored: false,\n\t\t};\n\t}\n\tlet kind: WorkspaceStatePath[\"kind\"] = \"modified\";\n\tif (x === \"A\" || y === \"A\" || x === \"C\" || y === \"C\") kind = \"added\";\n\telse if (x === \"D\" || y === \"D\") kind = \"deleted\";\n\telse if (x === \"R\" || y === \"R\") kind = \"renamed\";\n\treturn {\n\t\tkind,\n\t\tstaged: x !== \" \" && x !== \"?\" && x !== \"!\",\n\t\tunstaged: y !== \" \" && y !== \"?\" && y !== \"!\",\n\t\tignored: false,\n\t};\n}\n\n/** Hash one workspace path: regular files by bytes, symlinks by link text. */\nfunction hashWorkspacePath(\n\ttoplevel: string,\n\tpath: string,\n\tmaxBytes: number,\n): { hash?: string; skipped: \"size\" | \"error\" | false } {\n\ttry {\n\t\tconst full = `${toplevel}/${path}`;\n\t\tconst stat = lstatSync(full);\n\t\tif (stat.isFile()) {\n\t\t\tif (stat.size > maxBytes) return { skipped: \"size\" };\n\t\t\treturn { hash: `sha256:${createHash(\"sha256\").update(readFileSync(full)).digest(\"hex\")}`, skipped: false };\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\treturn {\n\t\t\t\thash: `sha256:${createHash(\"sha256\").update(readlinkSync(full)).digest(\"hex\")}`,\n\t\t\t\tskipped: false,\n\t\t\t};\n\t\t}\n\t\t// Directories (submodule gitlinks) have no worktree content to hash;\n\t\t// that is expected, not a coverage gap.\n\t\tif (stat.isDirectory()) return { skipped: false };\n\t\treturn { skipped: \"error\" };\n\t} catch {\n\t\treturn { skipped: \"error\" };\n\t}\n}\n\nfunction prefixRelative(prefix: string, path: string): string {\n\tif (prefix === \"\" || prefix === \".\") return path;\n\treturn `${prefix}/${path}`;\n}\n\n/**\n * Observe a Git workspace without mutating it. Returns a version-1\n * `WorkspaceStateRecord`: `unsupported` for non-Git workspaces, `failed`\n * when git could not run or the observation was cancelled, and `incomplete`\n * when a limit truncated the capture. `observed` never implies more than\n * `coverage` states.\n */\nexport interface ToplevelComparison {\n\t/** Whether the physically resolved workspace root sits under the toplevel. */\n\tinside: boolean;\n\t/** Root path relative to the toplevel; strip this prefix from porcelain paths. */\n\tprefix: string;\n\t/** Physically resolved toplevel; base for absolute-path work like hashing. */\n\ttoplevelReal: string;\n}\n\n/**\n * Decide containment of the workspace root inside git's toplevel after both\n * sides are physically resolved. Windows spellings diverge from what Node's\n * realpath returns for the same directory (short 8.3 names, drive-letter\n * case), so the win32 branch resolves through the native resolver, folds\n * case for the comparison, and uses win32 path semantics. The platform and\n * resolver are injectable so tests can exercise those branches anywhere.\n */\nexport function compareToplevel(\n\tworkspaceRoot: string,\n\ttoplevel: string,\n\toptions?: {\n\t\tplatform?: NodeJS.Platform;\n\t\trealpath?: (path: string) => string;\n\t},\n): ToplevelComparison {\n\tconst platform = options?.platform ?? process.platform;\n\tconst isWin32 = platform === \"win32\";\n\tconst realpath =\n\t\toptions?.realpath ??\n\t\t(isWin32\n\t\t\t? (p: string) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn realpathSync.native(p);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn realpathSync(p);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t: realpathSync);\n\tconst rootReal = realpath(workspaceRoot);\n\tconst toplevelReal = realpath(toplevel);\n\tconst pathMod = isWin32 ? pathWin32 : { relative, sep, dirname };\n\tconst fold = (p: string) => (isWin32 ? p.toLowerCase() : p);\n\tconst prefix = pathMod.relative(toplevelReal, rootReal);\n\tconst inside = !(prefix === \"..\" || prefix.startsWith(`..${pathMod.sep}`) || isAbsolute(prefix));\n\tif (isWin32 && inside) {\n\t\t// Recompute the strip prefix under case folding so a divergent drive or\n\t\t// directory case still yields the clean \"\" or relative form.\n\t\tconst folded = pathMod.relative(fold(toplevelReal), fold(rootReal));\n\t\treturn { inside, prefix: folded, toplevelReal };\n\t}\n\treturn { inside, prefix, toplevelReal };\n}\n\nexport async function observeWorkspaceGit(\n\tworkspaceRoot: string,\n\toptions?: GitObserveOptions,\n): Promise<WorkspaceStateRecord> {\n\tconst maxPaths = options?.maxPaths ?? DEFAULT_MAX_PATHS;\n\tconst maxHashBytes = options?.maxHashBytes ?? DEFAULT_MAX_HASH_BYTES;\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst hashPaths = options?.hashPaths ?? true;\n\tconst signal = options?.signal;\n\tconst excludePaths = (options?.excludePaths ?? [])\n\t\t.map((p) => p.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\"))\n\t\t.filter((p) => p.length > 0);\n\tconst isExcluded = (repoPath: string): boolean =>\n\t\texcludePaths.some((ex) => repoPath === ex || repoPath.startsWith(`${ex}/`));\n\n\tconst warnings: string[] = [];\n\tconst failed = (message: string): WorkspaceStateRecord => ({\n\t\tversion: WORKSPACE_RECORD_VERSION,\n\t\tobservationId: randomUUID(),\n\t\tstatus: \"failed\",\n\t\tbackend: \"git\",\n\t\tworkspaceRoot,\n\t\tcapturedAt: new Date().toISOString(),\n\t\tcoverage: emptyCoverage(),\n\t\tpaths: [],\n\t\twarnings: [message],\n\t});\n\n\tif (signal?.aborted) {\n\t\treturn failed(\"workspace observation cancelled\");\n\t}\n\n\tconst head = await runGit(workspaceRoot, [\"rev-parse\", \"--show-toplevel\"], timeoutMs, signal);\n\tif (head.spawnError) return failed(\"git could not be executed\");\n\tif (head.timedOut) return failed(\"workspace observation timed out\");\n\tif (signal?.aborted) return failed(\"workspace observation cancelled\");\n\tif (!head.ok) {\n\t\tif (/not a git repository/i.test(head.stderr) || head.code === 128) {\n\t\t\treturn {\n\t\t\t\t...failed(\"not a git repository\"),\n\t\t\t\tstatus: \"unsupported\",\n\t\t\t};\n\t\t}\n\t\treturn failed(\"git rev-parse failed\");\n\t}\n\t// git resolves the toplevel physically (macOS /tmp -> /private/tmp,\n\t// Windows drive-letter case and short names), so containment is decided\n\t// through compareToplevel; otherwise a divergent spelling of the same\n\t// directory would fail the observation or corrupt every reported path.\n\tconst toplevel = head.stdout.toString(\"utf-8\").trim();\n\tconst comparison = compareToplevel(workspaceRoot, toplevel);\n\tif (!comparison.inside) {\n\t\treturn failed(\"workspace root is outside the repository toplevel\");\n\t}\n\tconst insidePrefix = comparison.prefix;\n\n\tconst headCommit = await runGit(workspaceRoot, [\"rev-parse\", \"--verify\", \"HEAD\"], timeoutMs, signal);\n\tconst mergeHead = await runGit(workspaceRoot, [\"rev-parse\", \"-q\", \"--verify\", \"MERGE_HEAD\"], timeoutMs, signal);\n\tif (signal?.aborted) return failed(\"workspace observation cancelled\");\n\tif (mergeHead.ok) warnings.push(\"merge in progress\");\n\tconst branch = await runGit(workspaceRoot, [\"branch\", \"--show-current\"], timeoutMs, signal);\n\n\tconst headCommitValue = headCommit.ok ? headCommit.stdout.toString(\"utf-8\").trim() : undefined;\n\tif (!headCommit.ok) warnings.push(\"unborn HEAD (no commits)\");\n\tconst branchValue = branch.ok ? branch.stdout.toString(\"utf-8\").trim() : \"\";\n\tif (branchValue === \"\") warnings.push(\"detached HEAD\");\n\n\t// Index baseline.\n\tconst index = await runGit(workspaceRoot, [\"ls-files\", \"--stage\", \"-z\"], timeoutMs, signal);\n\tif (!index.ok) return failed(\"git ls-files failed\");\n\tconst indexDigest = `sha256:${createHash(\"sha256\").update(index.stdout).digest(\"hex\")}`;\n\n\t// Staged / unstaged / untracked / ignored change sets.\n\tconst status = await runGit(\n\t\tworkspaceRoot,\n\t\t[\"status\", \"--porcelain\", \"-z\", \"--untracked-files=all\", \"--ignored=matching\"],\n\t\ttimeoutMs,\n\t\tsignal,\n\t);\n\tif (!status.ok) return failed(\"git status failed\");\n\n\tconst paths: WorkspaceStatePath[] = [];\n\tlet truncated = false;\n\tlet skippedHashes = 0;\n\tfor (const entry of parsePorcelainZ(status.stdout)) {\n\t\tif (isExcluded(entry.path) || (entry.previousPath !== undefined && isExcluded(entry.previousPath))) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (paths.length >= maxPaths) {\n\t\t\ttruncated = true;\n\t\t\twarnings.push(`path list truncated at ${maxPaths} paths`);\n\t\t\tbreak;\n\t\t}\n\t\tconst { kind, staged, unstaged, ignored } = classify(entry);\n\t\tconst workspacePath: WorkspaceStatePath = {\n\t\t\tpath: prefixRelative(insidePrefix, entry.path),\n\t\t\tkind,\n\t\t\tstaged,\n\t\t\tunstaged,\n\t\t\t...(ignored ? { ignored: true } : {}),\n\t\t\t...(entry.previousPath ? { previousPath: prefixRelative(insidePrefix, entry.previousPath) } : {}),\n\t\t};\n\t\tif (hashPaths && kind !== \"deleted\") {\n\t\t\tconst { hash, skipped } = hashWorkspacePath(comparison.toplevelReal, entry.path, maxHashBytes);\n\t\t\tif (hash) workspacePath.contentHash = hash;\n\t\t\telse if (skipped) skippedHashes++;\n\t\t}\n\t\tpaths.push(workspacePath);\n\t}\n\tif (skippedHashes > 0) warnings.push(`content hashing skipped for ${skippedHashes} path(s)`);\n\n\t// Digest over exactly the paths this record includes, so a later\n\t// comparison can cheaply detect that the covered state moved.\n\tconst digestInput = paths\n\t\t.map(\n\t\t\t(p) =>\n\t\t\t\t`${p.path}\\0${p.kind}\\0${p.staged ? \"S\" : \"\"}${p.unstaged ? \"U\" : \"\"}${p.ignored ? \"I\" : \"\"}\\0${p.contentHash ?? \"\"}\\n`,\n\t\t)\n\t\t.sort()\n\t\t.join(\"\");\n\n\treturn {\n\t\tversion: WORKSPACE_RECORD_VERSION,\n\t\tobservationId: randomUUID(),\n\t\tstatus: truncated || skippedHashes > 0 ? \"incomplete\" : \"observed\",\n\t\tbackend: \"git\",\n\t\tworkspaceRoot,\n\t\tcapturedAt: new Date().toISOString(),\n\t\t// `hashes` reflects the configured policy: a disabled hash pass is a\n\t\t// coverage statement, not an incomplete capture.\n\t\tcoverage: { ...fullCoverage(), hashes: hashPaths },\n\t\tbase: {\n\t\t\t...(headCommitValue ? { headCommit: headCommitValue } : {}),\n\t\t\t...(branchValue ? { branch: branchValue } : {}),\n\t\t\tindexDigest,\n\t\t\t...(truncated ? {} : { worktreeDigest: `sha256:${createHash(\"sha256\").update(digestInput).digest(\"hex\")}` }),\n\t\t},\n\t\tpaths,\n\t\twarnings,\n\t};\n}\n"]}
@@ -0,0 +1,346 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { lstatSync, readFileSync, readlinkSync, realpathSync } from "node:fs";
4
+ import { dirname, isAbsolute, win32 as pathWin32, relative, sep } from "node:path";
5
+ import { WORKSPACE_RECORD_VERSION, } from "./state.js";
6
+ /**
7
+ * Read-only Git workspace observation (spec 2026-09-01-harness-correctness-
8
+ * and-workspace-state.md § 1).
9
+ *
10
+ * The adapter never mutates repository state: `HEAD`, the current branch, the
11
+ * index, the worktree, the stash, and local config are only read. Optional
12
+ * index locks are disabled (`GIT_OPTIONAL_LOCKS=0`) so even `git status`
13
+ * cannot refresh the on-disk index. It never walks the workspace; all path
14
+ * knowledge comes from git. Symlinks are hashed as link text (what git
15
+ * stores), never followed. Submodule (gitlink) paths carry no worktree hash.
16
+ */
17
+ const DEFAULT_MAX_PATHS = 200;
18
+ const DEFAULT_MAX_HASH_BYTES = 5 * 1024 * 1024;
19
+ const DEFAULT_TIMEOUT_MS = 10_000;
20
+ const GIT_CONFIG = ["-c", "core.autocrlf=false", "-c", "status.renames=true"];
21
+ const UNMERGED_XY = new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
22
+ function runGit(cwd, args, timeoutMs, signal) {
23
+ return new Promise((resolve) => {
24
+ let stdout = Buffer.alloc(0);
25
+ let stderr = "";
26
+ let timedOut = false;
27
+ let settled = false;
28
+ let child;
29
+ const finish = (code, spawnError) => {
30
+ if (settled)
31
+ return;
32
+ settled = true;
33
+ clearTimeout(timer);
34
+ signal?.removeEventListener("abort", onAbort);
35
+ resolve({
36
+ ok: !spawnError && !timedOut && code === 0,
37
+ code,
38
+ stdout,
39
+ stderr,
40
+ timedOut,
41
+ spawnError,
42
+ });
43
+ };
44
+ const onAbort = () => {
45
+ child?.kill("SIGKILL");
46
+ };
47
+ const timer = setTimeout(() => {
48
+ timedOut = true;
49
+ child?.kill("SIGKILL");
50
+ }, timeoutMs);
51
+ try {
52
+ child = spawn("git", [...GIT_CONFIG, ...args], {
53
+ cwd,
54
+ shell: false,
55
+ stdio: ["ignore", "pipe", "pipe"],
56
+ // Observation must leave the on-disk index untouched.
57
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
58
+ });
59
+ }
60
+ catch {
61
+ resolve({ ok: false, code: null, stdout, stderr: "", timedOut: false, spawnError: true });
62
+ return;
63
+ }
64
+ if (signal) {
65
+ if (signal.aborted)
66
+ onAbort();
67
+ else
68
+ signal.addEventListener("abort", onAbort, { once: true });
69
+ }
70
+ child.stdout?.on("data", (chunk) => {
71
+ stdout = Buffer.concat([stdout, chunk]);
72
+ });
73
+ child.stderr?.on("data", (chunk) => {
74
+ stderr += chunk.toString("utf-8");
75
+ });
76
+ child.on("error", () => finish(null, true));
77
+ child.on("close", (code) => finish(code, false));
78
+ });
79
+ }
80
+ function emptyCoverage() {
81
+ return {
82
+ tracked: false,
83
+ staged: false,
84
+ unstaged: false,
85
+ untracked: false,
86
+ ignored: false,
87
+ hashes: false,
88
+ patch: false,
89
+ };
90
+ }
91
+ function fullCoverage() {
92
+ return {
93
+ tracked: true,
94
+ staged: true,
95
+ unstaged: true,
96
+ untracked: true,
97
+ ignored: true,
98
+ hashes: true,
99
+ patch: false,
100
+ };
101
+ }
102
+ function parsePorcelainZ(stdout) {
103
+ const fields = stdout.toString("utf-8").split("\0");
104
+ const parsed = [];
105
+ let i = 0;
106
+ while (i < fields.length) {
107
+ const record = fields[i++];
108
+ if (!record || record.length < 4)
109
+ continue;
110
+ const entry = { x: record[0], y: record[1], path: record.slice(3) };
111
+ if (entry.x === "R" || entry.x === "C" || entry.y === "R" || entry.y === "C") {
112
+ entry.previousPath = fields[i++];
113
+ }
114
+ parsed.push(entry);
115
+ }
116
+ return parsed;
117
+ }
118
+ function classify(entry) {
119
+ const { x, y } = entry;
120
+ if (x === "?" && y === "?") {
121
+ return { kind: "untracked", staged: false, unstaged: false, ignored: false };
122
+ }
123
+ if (x === "!" && y === "!") {
124
+ return { kind: "untracked", staged: false, unstaged: false, ignored: true };
125
+ }
126
+ if (UNMERGED_XY.has(`${x}${y}`)) {
127
+ return {
128
+ kind: x === "D" && y === "D" ? "deleted" : "modified",
129
+ staged: true,
130
+ unstaged: true,
131
+ ignored: false,
132
+ };
133
+ }
134
+ let kind = "modified";
135
+ if (x === "A" || y === "A" || x === "C" || y === "C")
136
+ kind = "added";
137
+ else if (x === "D" || y === "D")
138
+ kind = "deleted";
139
+ else if (x === "R" || y === "R")
140
+ kind = "renamed";
141
+ return {
142
+ kind,
143
+ staged: x !== " " && x !== "?" && x !== "!",
144
+ unstaged: y !== " " && y !== "?" && y !== "!",
145
+ ignored: false,
146
+ };
147
+ }
148
+ /** Hash one workspace path: regular files by bytes, symlinks by link text. */
149
+ function hashWorkspacePath(toplevel, path, maxBytes) {
150
+ try {
151
+ const full = `${toplevel}/${path}`;
152
+ const stat = lstatSync(full);
153
+ if (stat.isFile()) {
154
+ if (stat.size > maxBytes)
155
+ return { skipped: "size" };
156
+ return { hash: `sha256:${createHash("sha256").update(readFileSync(full)).digest("hex")}`, skipped: false };
157
+ }
158
+ if (stat.isSymbolicLink()) {
159
+ return {
160
+ hash: `sha256:${createHash("sha256").update(readlinkSync(full)).digest("hex")}`,
161
+ skipped: false,
162
+ };
163
+ }
164
+ // Directories (submodule gitlinks) have no worktree content to hash;
165
+ // that is expected, not a coverage gap.
166
+ if (stat.isDirectory())
167
+ return { skipped: false };
168
+ return { skipped: "error" };
169
+ }
170
+ catch {
171
+ return { skipped: "error" };
172
+ }
173
+ }
174
+ function prefixRelative(prefix, path) {
175
+ if (prefix === "" || prefix === ".")
176
+ return path;
177
+ return `${prefix}/${path}`;
178
+ }
179
+ /**
180
+ * Decide containment of the workspace root inside git's toplevel after both
181
+ * sides are physically resolved. Windows spellings diverge from what Node's
182
+ * realpath returns for the same directory (short 8.3 names, drive-letter
183
+ * case), so the win32 branch resolves through the native resolver, folds
184
+ * case for the comparison, and uses win32 path semantics. The platform and
185
+ * resolver are injectable so tests can exercise those branches anywhere.
186
+ */
187
+ export function compareToplevel(workspaceRoot, toplevel, options) {
188
+ const platform = options?.platform ?? process.platform;
189
+ const isWin32 = platform === "win32";
190
+ const realpath = options?.realpath ??
191
+ (isWin32
192
+ ? (p) => {
193
+ try {
194
+ return realpathSync.native(p);
195
+ }
196
+ catch {
197
+ return realpathSync(p);
198
+ }
199
+ }
200
+ : realpathSync);
201
+ const rootReal = realpath(workspaceRoot);
202
+ const toplevelReal = realpath(toplevel);
203
+ const pathMod = isWin32 ? pathWin32 : { relative, sep, dirname };
204
+ const fold = (p) => (isWin32 ? p.toLowerCase() : p);
205
+ const prefix = pathMod.relative(toplevelReal, rootReal);
206
+ const inside = !(prefix === ".." || prefix.startsWith(`..${pathMod.sep}`) || isAbsolute(prefix));
207
+ if (isWin32 && inside) {
208
+ // Recompute the strip prefix under case folding so a divergent drive or
209
+ // directory case still yields the clean "" or relative form.
210
+ const folded = pathMod.relative(fold(toplevelReal), fold(rootReal));
211
+ return { inside, prefix: folded, toplevelReal };
212
+ }
213
+ return { inside, prefix, toplevelReal };
214
+ }
215
+ export async function observeWorkspaceGit(workspaceRoot, options) {
216
+ const maxPaths = options?.maxPaths ?? DEFAULT_MAX_PATHS;
217
+ const maxHashBytes = options?.maxHashBytes ?? DEFAULT_MAX_HASH_BYTES;
218
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
219
+ const hashPaths = options?.hashPaths ?? true;
220
+ const signal = options?.signal;
221
+ const excludePaths = (options?.excludePaths ?? [])
222
+ .map((p) => p.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, ""))
223
+ .filter((p) => p.length > 0);
224
+ const isExcluded = (repoPath) => excludePaths.some((ex) => repoPath === ex || repoPath.startsWith(`${ex}/`));
225
+ const warnings = [];
226
+ const failed = (message) => ({
227
+ version: WORKSPACE_RECORD_VERSION,
228
+ observationId: randomUUID(),
229
+ status: "failed",
230
+ backend: "git",
231
+ workspaceRoot,
232
+ capturedAt: new Date().toISOString(),
233
+ coverage: emptyCoverage(),
234
+ paths: [],
235
+ warnings: [message],
236
+ });
237
+ if (signal?.aborted) {
238
+ return failed("workspace observation cancelled");
239
+ }
240
+ const head = await runGit(workspaceRoot, ["rev-parse", "--show-toplevel"], timeoutMs, signal);
241
+ if (head.spawnError)
242
+ return failed("git could not be executed");
243
+ if (head.timedOut)
244
+ return failed("workspace observation timed out");
245
+ if (signal?.aborted)
246
+ return failed("workspace observation cancelled");
247
+ if (!head.ok) {
248
+ if (/not a git repository/i.test(head.stderr) || head.code === 128) {
249
+ return {
250
+ ...failed("not a git repository"),
251
+ status: "unsupported",
252
+ };
253
+ }
254
+ return failed("git rev-parse failed");
255
+ }
256
+ // git resolves the toplevel physically (macOS /tmp -> /private/tmp,
257
+ // Windows drive-letter case and short names), so containment is decided
258
+ // through compareToplevel; otherwise a divergent spelling of the same
259
+ // directory would fail the observation or corrupt every reported path.
260
+ const toplevel = head.stdout.toString("utf-8").trim();
261
+ const comparison = compareToplevel(workspaceRoot, toplevel);
262
+ if (!comparison.inside) {
263
+ return failed("workspace root is outside the repository toplevel");
264
+ }
265
+ const insidePrefix = comparison.prefix;
266
+ const headCommit = await runGit(workspaceRoot, ["rev-parse", "--verify", "HEAD"], timeoutMs, signal);
267
+ const mergeHead = await runGit(workspaceRoot, ["rev-parse", "-q", "--verify", "MERGE_HEAD"], timeoutMs, signal);
268
+ if (signal?.aborted)
269
+ return failed("workspace observation cancelled");
270
+ if (mergeHead.ok)
271
+ warnings.push("merge in progress");
272
+ const branch = await runGit(workspaceRoot, ["branch", "--show-current"], timeoutMs, signal);
273
+ const headCommitValue = headCommit.ok ? headCommit.stdout.toString("utf-8").trim() : undefined;
274
+ if (!headCommit.ok)
275
+ warnings.push("unborn HEAD (no commits)");
276
+ const branchValue = branch.ok ? branch.stdout.toString("utf-8").trim() : "";
277
+ if (branchValue === "")
278
+ warnings.push("detached HEAD");
279
+ // Index baseline.
280
+ const index = await runGit(workspaceRoot, ["ls-files", "--stage", "-z"], timeoutMs, signal);
281
+ if (!index.ok)
282
+ return failed("git ls-files failed");
283
+ const indexDigest = `sha256:${createHash("sha256").update(index.stdout).digest("hex")}`;
284
+ // Staged / unstaged / untracked / ignored change sets.
285
+ const status = await runGit(workspaceRoot, ["status", "--porcelain", "-z", "--untracked-files=all", "--ignored=matching"], timeoutMs, signal);
286
+ if (!status.ok)
287
+ return failed("git status failed");
288
+ const paths = [];
289
+ let truncated = false;
290
+ let skippedHashes = 0;
291
+ for (const entry of parsePorcelainZ(status.stdout)) {
292
+ if (isExcluded(entry.path) || (entry.previousPath !== undefined && isExcluded(entry.previousPath))) {
293
+ continue;
294
+ }
295
+ if (paths.length >= maxPaths) {
296
+ truncated = true;
297
+ warnings.push(`path list truncated at ${maxPaths} paths`);
298
+ break;
299
+ }
300
+ const { kind, staged, unstaged, ignored } = classify(entry);
301
+ const workspacePath = {
302
+ path: prefixRelative(insidePrefix, entry.path),
303
+ kind,
304
+ staged,
305
+ unstaged,
306
+ ...(ignored ? { ignored: true } : {}),
307
+ ...(entry.previousPath ? { previousPath: prefixRelative(insidePrefix, entry.previousPath) } : {}),
308
+ };
309
+ if (hashPaths && kind !== "deleted") {
310
+ const { hash, skipped } = hashWorkspacePath(comparison.toplevelReal, entry.path, maxHashBytes);
311
+ if (hash)
312
+ workspacePath.contentHash = hash;
313
+ else if (skipped)
314
+ skippedHashes++;
315
+ }
316
+ paths.push(workspacePath);
317
+ }
318
+ if (skippedHashes > 0)
319
+ warnings.push(`content hashing skipped for ${skippedHashes} path(s)`);
320
+ // Digest over exactly the paths this record includes, so a later
321
+ // comparison can cheaply detect that the covered state moved.
322
+ const digestInput = paths
323
+ .map((p) => `${p.path}\0${p.kind}\0${p.staged ? "S" : ""}${p.unstaged ? "U" : ""}${p.ignored ? "I" : ""}\0${p.contentHash ?? ""}\n`)
324
+ .sort()
325
+ .join("");
326
+ return {
327
+ version: WORKSPACE_RECORD_VERSION,
328
+ observationId: randomUUID(),
329
+ status: truncated || skippedHashes > 0 ? "incomplete" : "observed",
330
+ backend: "git",
331
+ workspaceRoot,
332
+ capturedAt: new Date().toISOString(),
333
+ // `hashes` reflects the configured policy: a disabled hash pass is a
334
+ // coverage statement, not an incomplete capture.
335
+ coverage: { ...fullCoverage(), hashes: hashPaths },
336
+ base: {
337
+ ...(headCommitValue ? { headCommit: headCommitValue } : {}),
338
+ ...(branchValue ? { branch: branchValue } : {}),
339
+ indexDigest,
340
+ ...(truncated ? {} : { worktreeDigest: `sha256:${createHash("sha256").update(digestInput).digest("hex")}` }),
341
+ },
342
+ paths,
343
+ warnings,
344
+ };
345
+ }
346
+ //# sourceMappingURL=git-observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-observer.js","sourceRoot":"","sources":["../../../src/core/workspace/git-observer.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { lstatSync, readFileSync, readlinkSync, realpathSync } from \"node:fs\";\nimport { dirname, isAbsolute, win32 as pathWin32, relative, sep } from \"node:path\";\nimport {\n\tWORKSPACE_RECORD_VERSION,\n\ttype WorkspaceStateCoverage,\n\ttype WorkspaceStatePath,\n\ttype WorkspaceStateRecord,\n} from \"./state.ts\";\n\n/**\n * Read-only Git workspace observation (spec 2026-09-01-harness-correctness-\n * and-workspace-state.md § 1).\n *\n * The adapter never mutates repository state: `HEAD`, the current branch, the\n * index, the worktree, the stash, and local config are only read. Optional\n * index locks are disabled (`GIT_OPTIONAL_LOCKS=0`) so even `git status`\n * cannot refresh the on-disk index. It never walks the workspace; all path\n * knowledge comes from git. Symlinks are hashed as link text (what git\n * stores), never followed. Submodule (gitlink) paths carry no worktree hash.\n */\n\nconst DEFAULT_MAX_PATHS = 200;\nconst DEFAULT_MAX_HASH_BYTES = 5 * 1024 * 1024;\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst GIT_CONFIG = [\"-c\", \"core.autocrlf=false\", \"-c\", \"status.renames=true\"];\nconst UNMERGED_XY = new Set([\"DD\", \"AU\", \"UD\", \"UA\", \"DU\", \"AA\", \"UU\"]);\n\nexport interface GitObserveOptions {\n\t/** Compute per-path content hashes of present files (default true). */\n\thashPaths?: boolean;\n\t/** Maximum number of reported paths (default 200). */\n\tmaxPaths?: number;\n\t/** Skip hashing files larger than this many bytes (default 5 MiB). */\n\tmaxHashBytes?: number;\n\t/** Per-command timeout in milliseconds (default 10 000). */\n\ttimeoutMs?: number;\n\t/** Cancels the observation; the record reports `failed`. */\n\tsignal?: AbortSignal;\n\t/**\n\t * Repository-relative path prefixes to omit from `paths` (exact match or\n\t * directory prefix). Used for harness bookkeeping that lives inside the\n\t * workspace — e.g. the session directory — which is not workspace state.\n\t */\n\texcludePaths?: string[];\n}\n\ninterface GitRunResult {\n\tok: boolean;\n\tcode: number | null;\n\tstdout: Buffer;\n\tstderr: string;\n\ttimedOut: boolean;\n\tspawnError: boolean;\n}\n\nfunction runGit(cwd: string, args: string[], timeoutMs: number, signal?: AbortSignal): Promise<GitRunResult> {\n\treturn new Promise((resolve) => {\n\t\tlet stdout = Buffer.alloc(0);\n\t\tlet stderr = \"\";\n\t\tlet timedOut = false;\n\t\tlet settled = false;\n\t\tlet child: ChildProcess;\n\t\tconst finish = (code: number | null, spawnError: boolean) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve({\n\t\t\t\tok: !spawnError && !timedOut && code === 0,\n\t\t\t\tcode,\n\t\t\t\tstdout,\n\t\t\t\tstderr,\n\t\t\t\ttimedOut,\n\t\t\t\tspawnError,\n\t\t\t});\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tchild?.kill(\"SIGKILL\");\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tchild?.kill(\"SIGKILL\");\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tchild = spawn(\"git\", [...GIT_CONFIG, ...args], {\n\t\t\t\tcwd,\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t// Observation must leave the on-disk index untouched.\n\t\t\t\tenv: { ...process.env, GIT_OPTIONAL_LOCKS: \"0\" },\n\t\t\t});\n\t\t} catch {\n\t\t\tresolve({ ok: false, code: null, stdout, stderr: \"\", timedOut: false, spawnError: true });\n\t\t\treturn;\n\t\t}\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstdout = Buffer.concat([stdout, chunk]);\n\t\t});\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString(\"utf-8\");\n\t\t});\n\t\tchild.on(\"error\", () => finish(null, true));\n\t\tchild.on(\"close\", (code) => finish(code, false));\n\t});\n}\n\nfunction emptyCoverage(): WorkspaceStateCoverage {\n\treturn {\n\t\ttracked: false,\n\t\tstaged: false,\n\t\tunstaged: false,\n\t\tuntracked: false,\n\t\tignored: false,\n\t\thashes: false,\n\t\tpatch: false,\n\t};\n}\n\nfunction fullCoverage(): WorkspaceStateCoverage {\n\treturn {\n\t\ttracked: true,\n\t\tstaged: true,\n\t\tunstaged: true,\n\t\tuntracked: true,\n\t\tignored: true,\n\t\thashes: true,\n\t\tpatch: false,\n\t};\n}\n\ninterface ParsedStatusPath {\n\tx: string;\n\ty: string;\n\tpath: string;\n\tpreviousPath?: string;\n}\n\nfunction parsePorcelainZ(stdout: Buffer): ParsedStatusPath[] {\n\tconst fields = stdout.toString(\"utf-8\").split(\"\\0\");\n\tconst parsed: ParsedStatusPath[] = [];\n\tlet i = 0;\n\twhile (i < fields.length) {\n\t\tconst record = fields[i++];\n\t\tif (!record || record.length < 4) continue;\n\t\tconst entry: ParsedStatusPath = { x: record[0], y: record[1], path: record.slice(3) };\n\t\tif (entry.x === \"R\" || entry.x === \"C\" || entry.y === \"R\" || entry.y === \"C\") {\n\t\t\tentry.previousPath = fields[i++];\n\t\t}\n\t\tparsed.push(entry);\n\t}\n\treturn parsed;\n}\n\nfunction classify(entry: ParsedStatusPath): {\n\tkind: WorkspaceStatePath[\"kind\"];\n\tstaged: boolean;\n\tunstaged: boolean;\n\tignored: boolean;\n} {\n\tconst { x, y } = entry;\n\tif (x === \"?\" && y === \"?\") {\n\t\treturn { kind: \"untracked\", staged: false, unstaged: false, ignored: false };\n\t}\n\tif (x === \"!\" && y === \"!\") {\n\t\treturn { kind: \"untracked\", staged: false, unstaged: false, ignored: true };\n\t}\n\tif (UNMERGED_XY.has(`${x}${y}`)) {\n\t\treturn {\n\t\t\tkind: x === \"D\" && y === \"D\" ? \"deleted\" : \"modified\",\n\t\t\tstaged: true,\n\t\t\tunstaged: true,\n\t\t\tignored: false,\n\t\t};\n\t}\n\tlet kind: WorkspaceStatePath[\"kind\"] = \"modified\";\n\tif (x === \"A\" || y === \"A\" || x === \"C\" || y === \"C\") kind = \"added\";\n\telse if (x === \"D\" || y === \"D\") kind = \"deleted\";\n\telse if (x === \"R\" || y === \"R\") kind = \"renamed\";\n\treturn {\n\t\tkind,\n\t\tstaged: x !== \" \" && x !== \"?\" && x !== \"!\",\n\t\tunstaged: y !== \" \" && y !== \"?\" && y !== \"!\",\n\t\tignored: false,\n\t};\n}\n\n/** Hash one workspace path: regular files by bytes, symlinks by link text. */\nfunction hashWorkspacePath(\n\ttoplevel: string,\n\tpath: string,\n\tmaxBytes: number,\n): { hash?: string; skipped: \"size\" | \"error\" | false } {\n\ttry {\n\t\tconst full = `${toplevel}/${path}`;\n\t\tconst stat = lstatSync(full);\n\t\tif (stat.isFile()) {\n\t\t\tif (stat.size > maxBytes) return { skipped: \"size\" };\n\t\t\treturn { hash: `sha256:${createHash(\"sha256\").update(readFileSync(full)).digest(\"hex\")}`, skipped: false };\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\treturn {\n\t\t\t\thash: `sha256:${createHash(\"sha256\").update(readlinkSync(full)).digest(\"hex\")}`,\n\t\t\t\tskipped: false,\n\t\t\t};\n\t\t}\n\t\t// Directories (submodule gitlinks) have no worktree content to hash;\n\t\t// that is expected, not a coverage gap.\n\t\tif (stat.isDirectory()) return { skipped: false };\n\t\treturn { skipped: \"error\" };\n\t} catch {\n\t\treturn { skipped: \"error\" };\n\t}\n}\n\nfunction prefixRelative(prefix: string, path: string): string {\n\tif (prefix === \"\" || prefix === \".\") return path;\n\treturn `${prefix}/${path}`;\n}\n\n/**\n * Observe a Git workspace without mutating it. Returns a version-1\n * `WorkspaceStateRecord`: `unsupported` for non-Git workspaces, `failed`\n * when git could not run or the observation was cancelled, and `incomplete`\n * when a limit truncated the capture. `observed` never implies more than\n * `coverage` states.\n */\nexport interface ToplevelComparison {\n\t/** Whether the physically resolved workspace root sits under the toplevel. */\n\tinside: boolean;\n\t/** Root path relative to the toplevel; strip this prefix from porcelain paths. */\n\tprefix: string;\n\t/** Physically resolved toplevel; base for absolute-path work like hashing. */\n\ttoplevelReal: string;\n}\n\n/**\n * Decide containment of the workspace root inside git's toplevel after both\n * sides are physically resolved. Windows spellings diverge from what Node's\n * realpath returns for the same directory (short 8.3 names, drive-letter\n * case), so the win32 branch resolves through the native resolver, folds\n * case for the comparison, and uses win32 path semantics. The platform and\n * resolver are injectable so tests can exercise those branches anywhere.\n */\nexport function compareToplevel(\n\tworkspaceRoot: string,\n\ttoplevel: string,\n\toptions?: {\n\t\tplatform?: NodeJS.Platform;\n\t\trealpath?: (path: string) => string;\n\t},\n): ToplevelComparison {\n\tconst platform = options?.platform ?? process.platform;\n\tconst isWin32 = platform === \"win32\";\n\tconst realpath =\n\t\toptions?.realpath ??\n\t\t(isWin32\n\t\t\t? (p: string) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn realpathSync.native(p);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn realpathSync(p);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t: realpathSync);\n\tconst rootReal = realpath(workspaceRoot);\n\tconst toplevelReal = realpath(toplevel);\n\tconst pathMod = isWin32 ? pathWin32 : { relative, sep, dirname };\n\tconst fold = (p: string) => (isWin32 ? p.toLowerCase() : p);\n\tconst prefix = pathMod.relative(toplevelReal, rootReal);\n\tconst inside = !(prefix === \"..\" || prefix.startsWith(`..${pathMod.sep}`) || isAbsolute(prefix));\n\tif (isWin32 && inside) {\n\t\t// Recompute the strip prefix under case folding so a divergent drive or\n\t\t// directory case still yields the clean \"\" or relative form.\n\t\tconst folded = pathMod.relative(fold(toplevelReal), fold(rootReal));\n\t\treturn { inside, prefix: folded, toplevelReal };\n\t}\n\treturn { inside, prefix, toplevelReal };\n}\n\nexport async function observeWorkspaceGit(\n\tworkspaceRoot: string,\n\toptions?: GitObserveOptions,\n): Promise<WorkspaceStateRecord> {\n\tconst maxPaths = options?.maxPaths ?? DEFAULT_MAX_PATHS;\n\tconst maxHashBytes = options?.maxHashBytes ?? DEFAULT_MAX_HASH_BYTES;\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst hashPaths = options?.hashPaths ?? true;\n\tconst signal = options?.signal;\n\tconst excludePaths = (options?.excludePaths ?? [])\n\t\t.map((p) => p.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\"))\n\t\t.filter((p) => p.length > 0);\n\tconst isExcluded = (repoPath: string): boolean =>\n\t\texcludePaths.some((ex) => repoPath === ex || repoPath.startsWith(`${ex}/`));\n\n\tconst warnings: string[] = [];\n\tconst failed = (message: string): WorkspaceStateRecord => ({\n\t\tversion: WORKSPACE_RECORD_VERSION,\n\t\tobservationId: randomUUID(),\n\t\tstatus: \"failed\",\n\t\tbackend: \"git\",\n\t\tworkspaceRoot,\n\t\tcapturedAt: new Date().toISOString(),\n\t\tcoverage: emptyCoverage(),\n\t\tpaths: [],\n\t\twarnings: [message],\n\t});\n\n\tif (signal?.aborted) {\n\t\treturn failed(\"workspace observation cancelled\");\n\t}\n\n\tconst head = await runGit(workspaceRoot, [\"rev-parse\", \"--show-toplevel\"], timeoutMs, signal);\n\tif (head.spawnError) return failed(\"git could not be executed\");\n\tif (head.timedOut) return failed(\"workspace observation timed out\");\n\tif (signal?.aborted) return failed(\"workspace observation cancelled\");\n\tif (!head.ok) {\n\t\tif (/not a git repository/i.test(head.stderr) || head.code === 128) {\n\t\t\treturn {\n\t\t\t\t...failed(\"not a git repository\"),\n\t\t\t\tstatus: \"unsupported\",\n\t\t\t};\n\t\t}\n\t\treturn failed(\"git rev-parse failed\");\n\t}\n\t// git resolves the toplevel physically (macOS /tmp -> /private/tmp,\n\t// Windows drive-letter case and short names), so containment is decided\n\t// through compareToplevel; otherwise a divergent spelling of the same\n\t// directory would fail the observation or corrupt every reported path.\n\tconst toplevel = head.stdout.toString(\"utf-8\").trim();\n\tconst comparison = compareToplevel(workspaceRoot, toplevel);\n\tif (!comparison.inside) {\n\t\treturn failed(\"workspace root is outside the repository toplevel\");\n\t}\n\tconst insidePrefix = comparison.prefix;\n\n\tconst headCommit = await runGit(workspaceRoot, [\"rev-parse\", \"--verify\", \"HEAD\"], timeoutMs, signal);\n\tconst mergeHead = await runGit(workspaceRoot, [\"rev-parse\", \"-q\", \"--verify\", \"MERGE_HEAD\"], timeoutMs, signal);\n\tif (signal?.aborted) return failed(\"workspace observation cancelled\");\n\tif (mergeHead.ok) warnings.push(\"merge in progress\");\n\tconst branch = await runGit(workspaceRoot, [\"branch\", \"--show-current\"], timeoutMs, signal);\n\n\tconst headCommitValue = headCommit.ok ? headCommit.stdout.toString(\"utf-8\").trim() : undefined;\n\tif (!headCommit.ok) warnings.push(\"unborn HEAD (no commits)\");\n\tconst branchValue = branch.ok ? branch.stdout.toString(\"utf-8\").trim() : \"\";\n\tif (branchValue === \"\") warnings.push(\"detached HEAD\");\n\n\t// Index baseline.\n\tconst index = await runGit(workspaceRoot, [\"ls-files\", \"--stage\", \"-z\"], timeoutMs, signal);\n\tif (!index.ok) return failed(\"git ls-files failed\");\n\tconst indexDigest = `sha256:${createHash(\"sha256\").update(index.stdout).digest(\"hex\")}`;\n\n\t// Staged / unstaged / untracked / ignored change sets.\n\tconst status = await runGit(\n\t\tworkspaceRoot,\n\t\t[\"status\", \"--porcelain\", \"-z\", \"--untracked-files=all\", \"--ignored=matching\"],\n\t\ttimeoutMs,\n\t\tsignal,\n\t);\n\tif (!status.ok) return failed(\"git status failed\");\n\n\tconst paths: WorkspaceStatePath[] = [];\n\tlet truncated = false;\n\tlet skippedHashes = 0;\n\tfor (const entry of parsePorcelainZ(status.stdout)) {\n\t\tif (isExcluded(entry.path) || (entry.previousPath !== undefined && isExcluded(entry.previousPath))) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (paths.length >= maxPaths) {\n\t\t\ttruncated = true;\n\t\t\twarnings.push(`path list truncated at ${maxPaths} paths`);\n\t\t\tbreak;\n\t\t}\n\t\tconst { kind, staged, unstaged, ignored } = classify(entry);\n\t\tconst workspacePath: WorkspaceStatePath = {\n\t\t\tpath: prefixRelative(insidePrefix, entry.path),\n\t\t\tkind,\n\t\t\tstaged,\n\t\t\tunstaged,\n\t\t\t...(ignored ? { ignored: true } : {}),\n\t\t\t...(entry.previousPath ? { previousPath: prefixRelative(insidePrefix, entry.previousPath) } : {}),\n\t\t};\n\t\tif (hashPaths && kind !== \"deleted\") {\n\t\t\tconst { hash, skipped } = hashWorkspacePath(comparison.toplevelReal, entry.path, maxHashBytes);\n\t\t\tif (hash) workspacePath.contentHash = hash;\n\t\t\telse if (skipped) skippedHashes++;\n\t\t}\n\t\tpaths.push(workspacePath);\n\t}\n\tif (skippedHashes > 0) warnings.push(`content hashing skipped for ${skippedHashes} path(s)`);\n\n\t// Digest over exactly the paths this record includes, so a later\n\t// comparison can cheaply detect that the covered state moved.\n\tconst digestInput = paths\n\t\t.map(\n\t\t\t(p) =>\n\t\t\t\t`${p.path}\\0${p.kind}\\0${p.staged ? \"S\" : \"\"}${p.unstaged ? \"U\" : \"\"}${p.ignored ? \"I\" : \"\"}\\0${p.contentHash ?? \"\"}\\n`,\n\t\t)\n\t\t.sort()\n\t\t.join(\"\");\n\n\treturn {\n\t\tversion: WORKSPACE_RECORD_VERSION,\n\t\tobservationId: randomUUID(),\n\t\tstatus: truncated || skippedHashes > 0 ? \"incomplete\" : \"observed\",\n\t\tbackend: \"git\",\n\t\tworkspaceRoot,\n\t\tcapturedAt: new Date().toISOString(),\n\t\t// `hashes` reflects the configured policy: a disabled hash pass is a\n\t\t// coverage statement, not an incomplete capture.\n\t\tcoverage: { ...fullCoverage(), hashes: hashPaths },\n\t\tbase: {\n\t\t\t...(headCommitValue ? { headCommit: headCommitValue } : {}),\n\t\t\t...(branchValue ? { branch: branchValue } : {}),\n\t\t\tindexDigest,\n\t\t\t...(truncated ? {} : { worktreeDigest: `sha256:${createHash(\"sha256\").update(digestInput).digest(\"hex\")}` }),\n\t\t},\n\t\tpaths,\n\t\twarnings,\n\t};\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { WorkspaceStateRecord } from "./state.ts";
2
+ export declare function formatWorkspaceProjection(record: WorkspaceStateRecord): string;
3
+ //# sourceMappingURL=projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../src/core/workspace/projection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAyB3E,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAiC9E","sourcesContent":["import type { WorkspaceStatePath, WorkspaceStateRecord } from \"./state.ts\";\n\n/**\n * Bounded, model-facing projection of a workspace observation (spec\n * 2026-09-01-harness-correctness-and-workspace-state.md § 3): status, base\n * identity, grouped paths, coverage/incomplete notices. Never hashes, never\n * artifact bytes, never absolute workspace paths.\n */\n\nconst MAX_PATHS_PER_GROUP = 20;\nconst HEAD_ABBREV = 7;\n\nfunction groupLabel(path: WorkspaceStatePath): string {\n\tif (path.ignored) return \"ignored\";\n\tconst flags = [path.staged ? \"staged\" : \"\", path.unstaged ? \"unstaged\" : \"\"].filter(Boolean).join(\" + \");\n\treturn flags ? `${path.kind} (${flags})` : path.kind;\n}\n\nfunction pathLabel(path: WorkspaceStatePath): string {\n\tif (path.kind === \"renamed\" && path.previousPath) {\n\t\treturn `${path.previousPath} -> ${path.path}`;\n\t}\n\treturn path.path;\n}\n\nexport function formatWorkspaceProjection(record: WorkspaceStateRecord): string {\n\tconst headerParts = [`Workspace: ${record.status}`];\n\tif (record.base?.branch) headerParts.push(`branch ${record.base.branch}`);\n\tif (record.base?.headCommit) headerParts.push(`HEAD ${record.base.headCommit.slice(0, HEAD_ABBREV)}`);\n\tconst lines: string[] = [headerParts.join(\" · \")];\n\n\tif (record.status === \"unsupported\") {\n\t\tlines.push(\"No workspace adapter applies to this workspace; treat the workspace as unknown.\");\n\t} else if (record.status === \"failed\") {\n\t\tlines.push(\"Workspace capture failed; treat this snapshot as absent, not as current.\");\n\t} else if (record.status === \"incomplete\") {\n\t\tlines.push(\"Configured limits prevented a complete capture; treat this snapshot as partial.\");\n\t}\n\n\tconst groups = new Map<string, string[]>();\n\tfor (const path of record.paths) {\n\t\tconst label = groupLabel(path);\n\t\tconst group = groups.get(label) ?? [];\n\t\tgroup.push(pathLabel(path));\n\t\tgroups.set(label, group);\n\t}\n\tconst labels = [...groups.keys()].sort();\n\tfor (const label of labels) {\n\t\tconst paths = groups.get(label)!;\n\t\tconst shown = paths.slice(0, MAX_PATHS_PER_GROUP);\n\t\tconst rest = paths.length - shown.length;\n\t\tlines.push(`${label}: ${shown.join(\", \")}${rest > 0 ? `, and ${rest} more` : \"\"}`);\n\t}\n\n\tif (record.warnings.length > 0) {\n\t\tlines.push(`Workspace notices: ${record.warnings.join(\"; \")}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Bounded, model-facing projection of a workspace observation (spec
3
+ * 2026-09-01-harness-correctness-and-workspace-state.md § 3): status, base
4
+ * identity, grouped paths, coverage/incomplete notices. Never hashes, never
5
+ * artifact bytes, never absolute workspace paths.
6
+ */
7
+ const MAX_PATHS_PER_GROUP = 20;
8
+ const HEAD_ABBREV = 7;
9
+ function groupLabel(path) {
10
+ if (path.ignored)
11
+ return "ignored";
12
+ const flags = [path.staged ? "staged" : "", path.unstaged ? "unstaged" : ""].filter(Boolean).join(" + ");
13
+ return flags ? `${path.kind} (${flags})` : path.kind;
14
+ }
15
+ function pathLabel(path) {
16
+ if (path.kind === "renamed" && path.previousPath) {
17
+ return `${path.previousPath} -> ${path.path}`;
18
+ }
19
+ return path.path;
20
+ }
21
+ export function formatWorkspaceProjection(record) {
22
+ const headerParts = [`Workspace: ${record.status}`];
23
+ if (record.base?.branch)
24
+ headerParts.push(`branch ${record.base.branch}`);
25
+ if (record.base?.headCommit)
26
+ headerParts.push(`HEAD ${record.base.headCommit.slice(0, HEAD_ABBREV)}`);
27
+ const lines = [headerParts.join(" · ")];
28
+ if (record.status === "unsupported") {
29
+ lines.push("No workspace adapter applies to this workspace; treat the workspace as unknown.");
30
+ }
31
+ else if (record.status === "failed") {
32
+ lines.push("Workspace capture failed; treat this snapshot as absent, not as current.");
33
+ }
34
+ else if (record.status === "incomplete") {
35
+ lines.push("Configured limits prevented a complete capture; treat this snapshot as partial.");
36
+ }
37
+ const groups = new Map();
38
+ for (const path of record.paths) {
39
+ const label = groupLabel(path);
40
+ const group = groups.get(label) ?? [];
41
+ group.push(pathLabel(path));
42
+ groups.set(label, group);
43
+ }
44
+ const labels = [...groups.keys()].sort();
45
+ for (const label of labels) {
46
+ const paths = groups.get(label);
47
+ const shown = paths.slice(0, MAX_PATHS_PER_GROUP);
48
+ const rest = paths.length - shown.length;
49
+ lines.push(`${label}: ${shown.join(", ")}${rest > 0 ? `, and ${rest} more` : ""}`);
50
+ }
51
+ if (record.warnings.length > 0) {
52
+ lines.push(`Workspace notices: ${record.warnings.join("; ")}`);
53
+ }
54
+ return lines.join("\n");
55
+ }
56
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../../../src/core/workspace/projection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WorkspaceStatePath, WorkspaceStateRecord } from \"./state.ts\";\n\n/**\n * Bounded, model-facing projection of a workspace observation (spec\n * 2026-09-01-harness-correctness-and-workspace-state.md § 3): status, base\n * identity, grouped paths, coverage/incomplete notices. Never hashes, never\n * artifact bytes, never absolute workspace paths.\n */\n\nconst MAX_PATHS_PER_GROUP = 20;\nconst HEAD_ABBREV = 7;\n\nfunction groupLabel(path: WorkspaceStatePath): string {\n\tif (path.ignored) return \"ignored\";\n\tconst flags = [path.staged ? \"staged\" : \"\", path.unstaged ? \"unstaged\" : \"\"].filter(Boolean).join(\" + \");\n\treturn flags ? `${path.kind} (${flags})` : path.kind;\n}\n\nfunction pathLabel(path: WorkspaceStatePath): string {\n\tif (path.kind === \"renamed\" && path.previousPath) {\n\t\treturn `${path.previousPath} -> ${path.path}`;\n\t}\n\treturn path.path;\n}\n\nexport function formatWorkspaceProjection(record: WorkspaceStateRecord): string {\n\tconst headerParts = [`Workspace: ${record.status}`];\n\tif (record.base?.branch) headerParts.push(`branch ${record.base.branch}`);\n\tif (record.base?.headCommit) headerParts.push(`HEAD ${record.base.headCommit.slice(0, HEAD_ABBREV)}`);\n\tconst lines: string[] = [headerParts.join(\" · \")];\n\n\tif (record.status === \"unsupported\") {\n\t\tlines.push(\"No workspace adapter applies to this workspace; treat the workspace as unknown.\");\n\t} else if (record.status === \"failed\") {\n\t\tlines.push(\"Workspace capture failed; treat this snapshot as absent, not as current.\");\n\t} else if (record.status === \"incomplete\") {\n\t\tlines.push(\"Configured limits prevented a complete capture; treat this snapshot as partial.\");\n\t}\n\n\tconst groups = new Map<string, string[]>();\n\tfor (const path of record.paths) {\n\t\tconst label = groupLabel(path);\n\t\tconst group = groups.get(label) ?? [];\n\t\tgroup.push(pathLabel(path));\n\t\tgroups.set(label, group);\n\t}\n\tconst labels = [...groups.keys()].sort();\n\tfor (const label of labels) {\n\t\tconst paths = groups.get(label)!;\n\t\tconst shown = paths.slice(0, MAX_PATHS_PER_GROUP);\n\t\tconst rest = paths.length - shown.length;\n\t\tlines.push(`${label}: ${shown.join(\", \")}${rest > 0 ? `, and ${rest} more` : \"\"}`);\n\t}\n\n\tif (record.warnings.length > 0) {\n\t\tlines.push(`Workspace notices: ${record.warnings.join(\"; \")}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}