salmon-loop 0.2.3

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 (655) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/README.zh-CN.md +144 -0
  4. package/dist/cli/argv/headless-detection.js +60 -0
  5. package/dist/cli/argv/print-mode.js +60 -0
  6. package/dist/cli/authorization/allowlist.js +908 -0
  7. package/dist/cli/authorization/non-interactive.js +166 -0
  8. package/dist/cli/authorization/provider.js +416 -0
  9. package/dist/cli/chat-interface.js +83 -0
  10. package/dist/cli/chat.js +492 -0
  11. package/dist/cli/cli-runtime-context.js +12 -0
  12. package/dist/cli/commander-error-adapter.js +35 -0
  13. package/dist/cli/commander-error-meta.js +13 -0
  14. package/dist/cli/commands/allowlist.js +270 -0
  15. package/dist/cli/commands/chat.js +120 -0
  16. package/dist/cli/commands/config.js +250 -0
  17. package/dist/cli/commands/context.js +57 -0
  18. package/dist/cli/commands/dispatcher.js +53 -0
  19. package/dist/cli/commands/exit.js +9 -0
  20. package/dist/cli/commands/llm-output.js +135 -0
  21. package/dist/cli/commands/log-mode.js +143 -0
  22. package/dist/cli/commands/mode.js +136 -0
  23. package/dist/cli/commands/new.js +18 -0
  24. package/dist/cli/commands/parallel.js +256 -0
  25. package/dist/cli/commands/queue.js +130 -0
  26. package/dist/cli/commands/registry.js +85 -0
  27. package/dist/cli/commands/restore.js +26 -0
  28. package/dist/cli/commands/run/assistant-message.js +14 -0
  29. package/dist/cli/commands/run/config-resolution.js +37 -0
  30. package/dist/cli/commands/run/early-errors.js +108 -0
  31. package/dist/cli/commands/run/execute.js +73 -0
  32. package/dist/cli/commands/run/extensions-resolution.js +22 -0
  33. package/dist/cli/commands/run/handler.js +434 -0
  34. package/dist/cli/commands/run/headless-error-writer.js +182 -0
  35. package/dist/cli/commands/run/instruction-guard.js +24 -0
  36. package/dist/cli/commands/run/loop-params.js +46 -0
  37. package/dist/cli/commands/run/mode.js +8 -0
  38. package/dist/cli/commands/run/parse-options.js +67 -0
  39. package/dist/cli/commands/run/persist-session.js +35 -0
  40. package/dist/cli/commands/run/preflight.js +156 -0
  41. package/dist/cli/commands/run/reporter-factory.js +52 -0
  42. package/dist/cli/commands/run/runtime-llm.js +56 -0
  43. package/dist/cli/commands/run/runtime-options.js +30 -0
  44. package/dist/cli/commands/run/session.js +19 -0
  45. package/dist/cli/commands/run/structured-output.js +106 -0
  46. package/dist/cli/commands/run/types.js +2 -0
  47. package/dist/cli/commands/run/validate-options.js +28 -0
  48. package/dist/cli/commands/run/verbose.js +36 -0
  49. package/dist/cli/commands/run.js +2 -0
  50. package/dist/cli/commands/serve.js +323 -0
  51. package/dist/cli/commands/session.js +77 -0
  52. package/dist/cli/commands/snapshot-interactive.js +165 -0
  53. package/dist/cli/commands/snapshot.js +159 -0
  54. package/dist/cli/commands/status.js +17 -0
  55. package/dist/cli/commands/subagent.js +178 -0
  56. package/dist/cli/commands/subcommand-suggestions.js +63 -0
  57. package/dist/cli/commands/tool-names.js +155 -0
  58. package/dist/cli/commands/types.js +2 -0
  59. package/dist/cli/commands/utils.js +42 -0
  60. package/dist/cli/config.js +16 -0
  61. package/dist/cli/crash-reporter.js +5 -0
  62. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +164 -0
  63. package/dist/cli/headless/anthropic-stream-protocol.js +62 -0
  64. package/dist/cli/headless/json-protocol.js +124 -0
  65. package/dist/cli/headless/native-stream-normalized-encoder.js +206 -0
  66. package/dist/cli/headless/openai-responses-canonical-applier.js +94 -0
  67. package/dist/cli/headless/openai-responses-state.js +294 -0
  68. package/dist/cli/headless/openai-stream-encoder.js +152 -0
  69. package/dist/cli/headless/stdout-writer.js +9 -0
  70. package/dist/cli/headless/stream-json-protocol.js +136 -0
  71. package/dist/cli/index.js +8 -0
  72. package/dist/cli/locales/en.js +409 -0
  73. package/dist/cli/locales/index.js +7 -0
  74. package/dist/cli/program-bootstrap.js +14 -0
  75. package/dist/cli/program-commands.js +106 -0
  76. package/dist/cli/program-options.js +15 -0
  77. package/dist/cli/program-output-mode.js +11 -0
  78. package/dist/cli/program-parse.js +24 -0
  79. package/dist/cli/reporters/anthropic-stream.js +77 -0
  80. package/dist/cli/reporters/base.js +2 -0
  81. package/dist/cli/reporters/json.js +69 -0
  82. package/dist/cli/reporters/openai-stream.js +72 -0
  83. package/dist/cli/reporters/standard.js +226 -0
  84. package/dist/cli/reporters/stderr-log-reporter.js +71 -0
  85. package/dist/cli/reporters/stream-json.js +111 -0
  86. package/dist/cli/run-cli.js +25 -0
  87. package/dist/cli/slash/runtime.js +240 -0
  88. package/dist/cli/ui/App.js +273 -0
  89. package/dist/cli/ui/authorization/bus.js +35 -0
  90. package/dist/cli/ui/components/CommandInput.js +200 -0
  91. package/dist/cli/ui/components/CommandSuggestionList.js +20 -0
  92. package/dist/cli/ui/components/Markdown.js +423 -0
  93. package/dist/cli/ui/components/MessageList.js +34 -0
  94. package/dist/cli/ui/components/StatusBannerLine.js +7 -0
  95. package/dist/cli/ui/components/TodoDrawer.js +60 -0
  96. package/dist/cli/ui/components/WelcomeMessage.js +14 -0
  97. package/dist/cli/ui/components/animations/StretchingThinking.js +51 -0
  98. package/dist/cli/ui/components/animations/ThinkingWave.js +15 -0
  99. package/dist/cli/ui/components/animations/TypeIndicator.js +30 -0
  100. package/dist/cli/ui/components/layout/SplitPane.js +11 -0
  101. package/dist/cli/ui/components/messageList/MessageItem.js +27 -0
  102. package/dist/cli/ui/components/messageList/QueuePreviewList.js +11 -0
  103. package/dist/cli/ui/components/messageList/items/EmphasisMessageItem.js +20 -0
  104. package/dist/cli/ui/components/messageList/items/InterruptMessageItem.js +10 -0
  105. package/dist/cli/ui/components/messageList/items/LightweightMessageItem.js +12 -0
  106. package/dist/cli/ui/components/messageList/items/StandardMessageItem.js +23 -0
  107. package/dist/cli/ui/components/messageList/items/WelcomeMessageItem.js +7 -0
  108. package/dist/cli/ui/components/messageList/messageListLayout.js +27 -0
  109. package/dist/cli/ui/components/messageList/streaming.js +51 -0
  110. package/dist/cli/ui/components/messageList/types.js +2 -0
  111. package/dist/cli/ui/components/messageList/utils.js +7 -0
  112. package/dist/cli/ui/components/sidebar/FileContext.js +8 -0
  113. package/dist/cli/ui/components/sidebar/MissionControl.js +8 -0
  114. package/dist/cli/ui/config.js +59 -0
  115. package/dist/cli/ui/hooks/useCommandLifecycle.js +110 -0
  116. package/dist/cli/ui/hooks/useCommandSuggestions.js +87 -0
  117. package/dist/cli/ui/hooks/useInputHistory.js +57 -0
  118. package/dist/cli/ui/hooks/useLoopEvents.js +382 -0
  119. package/dist/cli/ui/hooks/useLoopState.js +73 -0
  120. package/dist/cli/ui/hooks/useOnionExit.js +31 -0
  121. package/dist/cli/ui/hooks/useTerminalDimensions.js +34 -0
  122. package/dist/cli/ui/index.js +136 -0
  123. package/dist/cli/ui/selection/bus.js +35 -0
  124. package/dist/cli/ui/status/formatStatusBanner.js +8 -0
  125. package/dist/cli/ui/store/context.js +17 -0
  126. package/dist/cli/ui/store/reducer.js +264 -0
  127. package/dist/cli/ui/store/types.js +81 -0
  128. package/dist/cli/ui/styles/theme.js +295 -0
  129. package/dist/cli/ui/types.js +2 -0
  130. package/dist/cli/ui/utils/sanitizer.js +122 -0
  131. package/dist/cli/ui/utils/transcript.js +28 -0
  132. package/dist/cli/utils/asyncQueue.js +125 -0
  133. package/dist/cli/utils/audit-scope.js +10 -0
  134. package/dist/cli/utils/detectors/index.js +38 -0
  135. package/dist/cli/utils/llm-output.js +34 -0
  136. package/dist/cli/utils/outcome-reporter.js +17 -0
  137. package/dist/cli/utils/safe-fs.js +184 -0
  138. package/dist/cli/utils/verify-resolver.js +34 -0
  139. package/dist/cli/utils/worktree-prepare-resolver.js +18 -0
  140. package/dist/core/adapters/fs/atomic-file-writer.js +129 -0
  141. package/dist/core/adapters/fs/file-adapter.js +95 -0
  142. package/dist/core/adapters/fs/filesystem.js +31 -0
  143. package/dist/core/adapters/fs/index.js +5 -0
  144. package/dist/core/adapters/fs/node-fs.js +7 -0
  145. package/dist/core/adapters/fs/readonly-filesystem.js +23 -0
  146. package/dist/core/adapters/git/git-adapter.js +704 -0
  147. package/dist/core/adapters/git/git-runner.js +119 -0
  148. package/dist/core/adapters/git/lock-manager.js +314 -0
  149. package/dist/core/adapters/git/types.js +2 -0
  150. package/dist/core/adapters/path/index.js +2 -0
  151. package/dist/core/adapters/path/path-adapter.js +23 -0
  152. package/dist/core/ast/guard.js +116 -0
  153. package/dist/core/ast/index.js +4 -0
  154. package/dist/core/ast/parser.js +284 -0
  155. package/dist/core/ast/validator.js +46 -0
  156. package/dist/core/backends/salmon-loop/task-executor.js +68 -0
  157. package/dist/core/checkpoint-domain/manifest-store.js +379 -0
  158. package/dist/core/checkpoint-domain/service.js +84 -0
  159. package/dist/core/checkpoint-domain/types.js +2 -0
  160. package/dist/core/config/defaults.js +50 -0
  161. package/dist/core/config/errors.js +11 -0
  162. package/dist/core/config/file-format.js +108 -0
  163. package/dist/core/config/index.js +7 -0
  164. package/dist/core/config/limits.js +77 -0
  165. package/dist/core/config/load.js +34 -0
  166. package/dist/core/config/normalize.js +35 -0
  167. package/dist/core/config/paths.js +20 -0
  168. package/dist/core/config/redact.js +16 -0
  169. package/dist/core/config/resolve-env.js +43 -0
  170. package/dist/core/config/resolve-llm.js +130 -0
  171. package/dist/core/config/resolve.js +68 -0
  172. package/dist/core/config/resolvers/ast-validation.js +8 -0
  173. package/dist/core/config/resolvers/context.js +21 -0
  174. package/dist/core/config/resolvers/observability.js +45 -0
  175. package/dist/core/config/resolvers/output.js +8 -0
  176. package/dist/core/config/resolvers/permission-mode.js +6 -0
  177. package/dist/core/config/resolvers/security.js +14 -0
  178. package/dist/core/config/resolvers/server.js +36 -0
  179. package/dist/core/config/resolvers/tool-authorization.js +39 -0
  180. package/dist/core/config/resolvers/ui.js +26 -0
  181. package/dist/core/config/types/config-file.js +2 -0
  182. package/dist/core/config/types/primitives.js +9 -0
  183. package/dist/core/config/types/resolved.js +2 -0
  184. package/dist/core/config/types.js +4 -0
  185. package/dist/core/config/validate.js +852 -0
  186. package/dist/core/context/assembly/default-prompt-assembler.js +7 -0
  187. package/dist/core/context/assembly/prompt-assembler.js +2 -0
  188. package/dist/core/context/ast/import-extractor.js +28 -0
  189. package/dist/core/context/ast/module-resolver.js +61 -0
  190. package/dist/core/context/ast/source-outline.js +25 -0
  191. package/dist/core/context/audit-constants.js +23 -0
  192. package/dist/core/context/audit.js +54 -0
  193. package/dist/core/context/budget/dynamic-adjuster.js +149 -0
  194. package/dist/core/context/budget/example-integration.js +49 -0
  195. package/dist/core/context/budget/integration.js +93 -0
  196. package/dist/core/context/builder.js +289 -0
  197. package/dist/core/context/cache/errors.js +16 -0
  198. package/dist/core/context/cache/incremental-updater.js +131 -0
  199. package/dist/core/context/cache/index.js +25 -0
  200. package/dist/core/context/cache/path-resolver.js +127 -0
  201. package/dist/core/context/cache/prompt-caching.js +207 -0
  202. package/dist/core/context/cache/store-factory.js +63 -0
  203. package/dist/core/context/cache/store.js +193 -0
  204. package/dist/core/context/cache/types.js +15 -0
  205. package/dist/core/context/compression/js-like-comments.js +139 -0
  206. package/dist/core/context/compression/smart-compress.js +61 -0
  207. package/dist/core/context/compression/whitespace.js +26 -0
  208. package/dist/core/context/dependencies.js +102 -0
  209. package/dist/core/context/effectiveness/index.js +25 -0
  210. package/dist/core/context/effectiveness/tracker.js +253 -0
  211. package/dist/core/context/effectiveness/types.js +15 -0
  212. package/dist/core/context/formatters/index.js +7 -0
  213. package/dist/core/context/formatters/json-converter.js +662 -0
  214. package/dist/core/context/formatters/types.js +6 -0
  215. package/dist/core/context/formatters/xml-context.js +296 -0
  216. package/dist/core/context/gatherers/architecture-gatherer.js +75 -0
  217. package/dist/core/context/gatherers/artifact-gatherer.js +53 -0
  218. package/dist/core/context/gatherers/ast-gatherer.js +370 -0
  219. package/dist/core/context/gatherers/ghost-dependency-gatherer.js +46 -0
  220. package/dist/core/context/gatherers/git-diff-gatherer.js +91 -0
  221. package/dist/core/context/gatherers/git-history-gatherer.js +57 -0
  222. package/dist/core/context/gatherers/knowledge-gatherer.js +101 -0
  223. package/dist/core/context/gatherers/metadata-gatherer.js +59 -0
  224. package/dist/core/context/gatherers/primary-text-gatherer.js +36 -0
  225. package/dist/core/context/gatherers/ripgrep-gatherer.js +104 -0
  226. package/dist/core/context/hash.js +52 -0
  227. package/dist/core/context/index.js +3 -0
  228. package/dist/core/context/keywords.js +179 -0
  229. package/dist/core/context/policies/budget-policy.js +36 -0
  230. package/dist/core/context/policies/pack-until-full.js +419 -0
  231. package/dist/core/context/scoring/relevance.js +191 -0
  232. package/dist/core/context/service-deps.js +32 -0
  233. package/dist/core/context/service-helpers.js +32 -0
  234. package/dist/core/context/service.js +265 -0
  235. package/dist/core/context/steps/context-budget.js +157 -0
  236. package/dist/core/context/steps/context-gather.js +71 -0
  237. package/dist/core/context/steps/context-primary.js +19 -0
  238. package/dist/core/context/steps/context-promotion.js +78 -0
  239. package/dist/core/context/steps/context-targets.js +85 -0
  240. package/dist/core/context/steps/types.js +2 -0
  241. package/dist/core/context/summarization/index.js +27 -0
  242. package/dist/core/context/summarization/prompts.js +80 -0
  243. package/dist/core/context/summarization/summarizer.js +377 -0
  244. package/dist/core/context/summarization/types.js +29 -0
  245. package/dist/core/context/targeting/churn-policy.js +27 -0
  246. package/dist/core/context/targeting/target-resolver.js +491 -0
  247. package/dist/core/context/token/adaptive-budget.js +364 -0
  248. package/dist/core/context/token/cache.js +163 -0
  249. package/dist/core/context/token/counter.js +190 -0
  250. package/dist/core/context/token/encoding-registry.js +173 -0
  251. package/dist/core/context/token/index.js +31 -0
  252. package/dist/core/context/token/token-budget.js +213 -0
  253. package/dist/core/context/token/types.js +10 -0
  254. package/dist/core/context/truncation/index.js +23 -0
  255. package/dist/core/context/truncation/semantic-truncator.js +103 -0
  256. package/dist/core/context/truncation/strategies/error-stack.js +94 -0
  257. package/dist/core/context/truncation/strategies/generic.js +48 -0
  258. package/dist/core/context/truncation/strategies/git-diff.js +99 -0
  259. package/dist/core/context/truncation/strategies/index.js +10 -0
  260. package/dist/core/context/truncation/strategies/json.js +142 -0
  261. package/dist/core/context/truncation/strategies/log.js +131 -0
  262. package/dist/core/context/truncation/strategies/test-result.js +140 -0
  263. package/dist/core/context/truncation/type-detector.js +133 -0
  264. package/dist/core/context/truncation/types.js +16 -0
  265. package/dist/core/context/types.js +2 -0
  266. package/dist/core/extensions/index.js +118 -0
  267. package/dist/core/extensions/load.js +36 -0
  268. package/dist/core/extensions/merge.js +29 -0
  269. package/dist/core/extensions/paths.js +40 -0
  270. package/dist/core/extensions/redact.js +37 -0
  271. package/dist/core/extensions/schemas.js +70 -0
  272. package/dist/core/extensions/types.js +2 -0
  273. package/dist/core/facades/cli-authorization-allowlist.js +3 -0
  274. package/dist/core/facades/cli-authorization-non-interactive.js +3 -0
  275. package/dist/core/facades/cli-authorization-provider.js +2 -0
  276. package/dist/core/facades/cli-chat.js +11 -0
  277. package/dist/core/facades/cli-command-allowlist.js +3 -0
  278. package/dist/core/facades/cli-command-chat.js +8 -0
  279. package/dist/core/facades/cli-command-checkpoint.js +3 -0
  280. package/dist/core/facades/cli-command-config.js +10 -0
  281. package/dist/core/facades/cli-command-dispatcher.js +2 -0
  282. package/dist/core/facades/cli-command-parallel.js +8 -0
  283. package/dist/core/facades/cli-command-session.js +2 -0
  284. package/dist/core/facades/cli-command-tool-names.js +6 -0
  285. package/dist/core/facades/cli-context.js +8 -0
  286. package/dist/core/facades/cli-headless.js +3 -0
  287. package/dist/core/facades/cli-observability.js +3 -0
  288. package/dist/core/facades/cli-program-bootstrap.js +2 -0
  289. package/dist/core/facades/cli-reporters.js +5 -0
  290. package/dist/core/facades/cli-run-execute.js +3 -0
  291. package/dist/core/facades/cli-run-handler.js +7 -0
  292. package/dist/core/facades/cli-run-headless-error-writer.js +2 -0
  293. package/dist/core/facades/cli-run-loop-params.js +2 -0
  294. package/dist/core/facades/cli-run-persist-session.js +2 -0
  295. package/dist/core/facades/cli-run-runtime-llm.js +5 -0
  296. package/dist/core/facades/cli-serve.js +21 -0
  297. package/dist/core/facades/cli-slash-runtime.js +9 -0
  298. package/dist/core/facades/cli-subagent.js +2 -0
  299. package/dist/core/facades/cli-ui.js +5 -0
  300. package/dist/core/facades/cli-utils-llm-output.js +3 -0
  301. package/dist/core/facades/cli-utils-path.js +2 -0
  302. package/dist/core/facades/cli-utils-worktree.js +2 -0
  303. package/dist/core/failure/diagnostics.js +221 -0
  304. package/dist/core/feedback/index.js +28 -0
  305. package/dist/core/feedback/parsers.js +59 -0
  306. package/dist/core/feedback/patterns.js +26 -0
  307. package/dist/core/feedback/types.js +2 -0
  308. package/dist/core/grizzco/domain/grizzco-types.js +41 -0
  309. package/dist/core/grizzco/dsl/DecisionEngine.js +149 -0
  310. package/dist/core/grizzco/dsl/MicroTaskRunner.js +39 -0
  311. package/dist/core/grizzco/dsl/llm-strategy.js +80 -0
  312. package/dist/core/grizzco/dsl/strategies.js +69 -0
  313. package/dist/core/grizzco/dsl/types.js +2 -0
  314. package/dist/core/grizzco/engine/observability/event-adapter.js +41 -0
  315. package/dist/core/grizzco/engine/observability/index.js +3 -0
  316. package/dist/core/grizzco/engine/observability/loop-telemetry.js +51 -0
  317. package/dist/core/grizzco/engine/outcome/index.js +2 -0
  318. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +167 -0
  319. package/dist/core/grizzco/engine/pipeline/pipeline.js +335 -0
  320. package/dist/core/grizzco/engine/pipeline/types.js +2 -0
  321. package/dist/core/grizzco/engine/transaction/attempt-failure.js +242 -0
  322. package/dist/core/grizzco/engine/transaction/authorization-summary.js +44 -0
  323. package/dist/core/grizzco/engine/transaction/index.js +3 -0
  324. package/dist/core/grizzco/engine/transaction/report-mapper.js +50 -0
  325. package/dist/core/grizzco/engine/transaction/retry-policy.js +19 -0
  326. package/dist/core/grizzco/engine/transaction/runner-builder.js +45 -0
  327. package/dist/core/grizzco/engine/transaction/session.js +58 -0
  328. package/dist/core/grizzco/engine/transaction/transaction-runner.js +193 -0
  329. package/dist/core/grizzco/engine/transaction/types.js +2 -0
  330. package/dist/core/grizzco/execution/Executor.js +58 -0
  331. package/dist/core/grizzco/execution/RejectionManager.js +71 -0
  332. package/dist/core/grizzco/execution/WorkerFactory.js +31 -0
  333. package/dist/core/grizzco/flows/SalmonLoopFlow.js +102 -0
  334. package/dist/core/grizzco/runtime/apply-back-runtime.js +136 -0
  335. package/dist/core/grizzco/runtime/apply-back-utils.js +13 -0
  336. package/dist/core/grizzco/runtime/host/host-runner.js +99 -0
  337. package/dist/core/grizzco/runtime/host/index.js +2 -0
  338. package/dist/core/grizzco/runtime/host/types.js +2 -0
  339. package/dist/core/grizzco/services/CachedService.js +42 -0
  340. package/dist/core/grizzco/services/implementations/default/GitConfigService.js +38 -0
  341. package/dist/core/grizzco/services/implementations/mock/MockLockService.js +11 -0
  342. package/dist/core/grizzco/services/implementations/mock/MockUserQuotaService.js +11 -0
  343. package/dist/core/grizzco/services/registry.js +30 -0
  344. package/dist/core/grizzco/services/types.js +2 -0
  345. package/dist/core/grizzco/steps/answer.js +75 -0
  346. package/dist/core/grizzco/steps/apply-back.js +46 -0
  347. package/dist/core/grizzco/steps/apply.js +136 -0
  348. package/dist/core/grizzco/steps/ast-validate.js +37 -0
  349. package/dist/core/grizzco/steps/audit.js +311 -0
  350. package/dist/core/grizzco/steps/context.js +74 -0
  351. package/dist/core/grizzco/steps/display-answer.js +6 -0
  352. package/dist/core/grizzco/steps/display-report.js +158 -0
  353. package/dist/core/grizzco/steps/display-research.js +6 -0
  354. package/dist/core/grizzco/steps/displayReview.js +6 -0
  355. package/dist/core/grizzco/steps/explore.js +245 -0
  356. package/dist/core/grizzco/steps/extractIssues.js +27 -0
  357. package/dist/core/grizzco/steps/generateFixPlan.js +13 -0
  358. package/dist/core/grizzco/steps/generateReview.js +71 -0
  359. package/dist/core/grizzco/steps/patch.js +220 -0
  360. package/dist/core/grizzco/steps/plan.js +191 -0
  361. package/dist/core/grizzco/steps/preflight.js +93 -0
  362. package/dist/core/grizzco/steps/prepare-deps.js +49 -0
  363. package/dist/core/grizzco/steps/read-only-shrink.js +4 -0
  364. package/dist/core/grizzco/steps/research.js +188 -0
  365. package/dist/core/grizzco/steps/rollback.js +138 -0
  366. package/dist/core/grizzco/steps/shrink.js +64 -0
  367. package/dist/core/grizzco/steps/validate.js +40 -0
  368. package/dist/core/grizzco/steps/verify.js +136 -0
  369. package/dist/core/grizzco/validation/AstValidationService.js +133 -0
  370. package/dist/core/grizzco/validation/ContextValidator.js +17 -0
  371. package/dist/core/grizzco/validation/ast-validation-policy.js +11 -0
  372. package/dist/core/grizzco/workers/direct-write-worker.js +44 -0
  373. package/dist/core/grizzco/workers/git-apply-worker.js +75 -0
  374. package/dist/core/grizzco/workers/i-merge-worker.js +2 -0
  375. package/dist/core/grizzco/workers/mm-three-way-worker.js +117 -0
  376. package/dist/core/grizzco/workers/no-op-worker.js +18 -0
  377. package/dist/core/grizzco/workers/overwrite-binary-worker.js +29 -0
  378. package/dist/core/grizzco/workers/strata-sync-worker.js +69 -0
  379. package/dist/core/grizzco/workers/three-way-merge-worker.js +84 -0
  380. package/dist/core/grizzco/workers/three-way-staged-worker.js +93 -0
  381. package/dist/core/grizzco/workers/union-merge-worker.js +71 -0
  382. package/dist/core/history/input-history.js +55 -0
  383. package/dist/core/intent/chat-intent.js +250 -0
  384. package/dist/core/interaction/events/bus.js +52 -0
  385. package/dist/core/interaction/model/events.js +2 -0
  386. package/dist/core/interaction/model/index.js +3 -0
  387. package/dist/core/interaction/model/task-state.js +9 -0
  388. package/dist/core/interaction/model/transition-policy.js +50 -0
  389. package/dist/core/interaction/model/types.js +2 -0
  390. package/dist/core/interaction/orchestration/facade.js +190 -0
  391. package/dist/core/interaction/orchestration/index.js +2 -0
  392. package/dist/core/interaction/orchestration/store.js +32 -0
  393. package/dist/core/interaction/sync/task-sync-engine.js +57 -0
  394. package/dist/core/interaction/turn-stop-reason.js +27 -0
  395. package/dist/core/language-support/index.js +3 -0
  396. package/dist/core/language-support/orchestrator.js +37 -0
  397. package/dist/core/language-support/strategies/extension-candidate-strategy.js +27 -0
  398. package/dist/core/language-support/strategies/index.js +3 -0
  399. package/dist/core/language-support/strategies/language-query-strategy.js +26 -0
  400. package/dist/core/llm/ai-sdk/chat-executor.js +88 -0
  401. package/dist/core/llm/ai-sdk/langfuse-headers.js +28 -0
  402. package/dist/core/llm/ai-sdk/message-mapper.js +240 -0
  403. package/dist/core/llm/ai-sdk/observation-context.js +16 -0
  404. package/dist/core/llm/ai-sdk/provider-factory.js +29 -0
  405. package/dist/core/llm/ai-sdk/request-params.js +18 -0
  406. package/dist/core/llm/ai-sdk/request-runtime.js +168 -0
  407. package/dist/core/llm/ai-sdk/result-mapper.js +31 -0
  408. package/dist/core/llm/ai-sdk/retry-classifier.js +82 -0
  409. package/dist/core/llm/ai-sdk/retry-executor.js +38 -0
  410. package/dist/core/llm/ai-sdk.js +92 -0
  411. package/dist/core/llm/audit.js +2 -0
  412. package/dist/core/llm/base-url.js +18 -0
  413. package/dist/core/llm/contracts/repair.js +68 -0
  414. package/dist/core/llm/errors.js +172 -0
  415. package/dist/core/llm/factory.js +21 -0
  416. package/dist/core/llm/http/index.js +2 -0
  417. package/dist/core/llm/index.js +6 -0
  418. package/dist/core/llm/message-composition.js +25 -0
  419. package/dist/core/llm/openai.js +69 -0
  420. package/dist/core/llm/output-policy.js +192 -0
  421. package/dist/core/llm/phase-router.js +55 -0
  422. package/dist/core/llm/redact.js +37 -0
  423. package/dist/core/llm/registry.js +81 -0
  424. package/dist/core/llm/retry-utils.js +114 -0
  425. package/dist/core/llm/stream-utils.js +87 -0
  426. package/dist/core/llm/utils.js +82 -0
  427. package/dist/core/observability/audit-file.js +199 -0
  428. package/dist/core/observability/audit-trail.js +125 -0
  429. package/dist/core/observability/authorization-decisions.js +54 -0
  430. package/dist/core/observability/debug-artifacts.js +61 -0
  431. package/dist/core/observability/error-envelope.js +63 -0
  432. package/dist/core/observability/error-mapping.js +271 -0
  433. package/dist/core/observability/ignored-error.js +6 -0
  434. package/dist/core/observability/logger.js +457 -0
  435. package/dist/core/observability/loop-event-reporter.js +46 -0
  436. package/dist/core/observability/monitor.js +240 -0
  437. package/dist/core/observability/run-outcome-reporter.js +15 -0
  438. package/dist/core/observability/token-usage.js +36 -0
  439. package/dist/core/observability/ui-log-sanitize.js +35 -0
  440. package/dist/core/patch/aggregator.js +93 -0
  441. package/dist/core/patch/diff.js +298 -0
  442. package/dist/core/permission-gate/default-gate.js +115 -0
  443. package/dist/core/permission-gate/gate.js +2 -0
  444. package/dist/core/permission-gate/types.js +2 -0
  445. package/dist/core/plan/index.js +2 -0
  446. package/dist/core/plan/manager.js +123 -0
  447. package/dist/core/plan/markdown-editor.js +238 -0
  448. package/dist/core/plan/storage.js +75 -0
  449. package/dist/core/plan/types.js +2 -0
  450. package/dist/core/plugin/interface.js +2 -0
  451. package/dist/core/plugin/loader.js +130 -0
  452. package/dist/core/plugin/registry.js +90 -0
  453. package/dist/core/plugin/validator.js +98 -0
  454. package/dist/core/prompts/registry.js +189 -0
  455. package/dist/core/prompts/runtime.js +69 -0
  456. package/dist/core/prompts/schema.js +2 -0
  457. package/dist/core/prompts/templates/phases/explore_user.hbs +26 -0
  458. package/dist/core/prompts/templates/phases/patch_user.hbs +57 -0
  459. package/dist/core/prompts/templates/phases/plan_user.hbs +33 -0
  460. package/dist/core/prompts/templates/system/_context_json_legend.hbs +21 -0
  461. package/dist/core/prompts/templates/system/_tool_defs.hbs +60 -0
  462. package/dist/core/prompts/templates/system/explore_system.hbs +26 -0
  463. package/dist/core/prompts/templates/system/main_system.hbs +18 -0
  464. package/dist/core/prompts/templates/system/patch_system.hbs +10 -0
  465. package/dist/core/prompts/templates/system/plan_system.hbs +1 -0
  466. package/dist/core/prompts/templates/system/reflection.hbs +39 -0
  467. package/dist/core/protocols/a2a/agent-card.js +30 -0
  468. package/dist/core/protocols/a2a/mapper.js +14 -0
  469. package/dist/core/protocols/a2a/sdk/auth-middleware.js +31 -0
  470. package/dist/core/protocols/a2a/sdk/executor.js +301 -0
  471. package/dist/core/protocols/a2a/sdk/server.js +24 -0
  472. package/dist/core/protocols/a2a/task-projection.js +45 -0
  473. package/dist/core/protocols/acp/acp-command-runner.js +204 -0
  474. package/dist/core/protocols/acp/acp-filesystem.js +43 -0
  475. package/dist/core/protocols/acp/checkpoint-meta.js +2 -0
  476. package/dist/core/protocols/acp/formal-agent.js +1201 -0
  477. package/dist/core/protocols/acp/handlers.js +51 -0
  478. package/dist/core/protocols/acp/permission-provider.js +122 -0
  479. package/dist/core/protocols/acp/stdio-server.js +116 -0
  480. package/dist/core/reflection/engine.js +55 -0
  481. package/dist/core/reflection/types.js +2 -0
  482. package/dist/core/runtime/agent-server-runtime.js +88 -0
  483. package/dist/core/runtime/bun-runtime.js +26 -0
  484. package/dist/core/runtime/command-runner-context.js +16 -0
  485. package/dist/core/runtime/exit-codes.js +11 -0
  486. package/dist/core/runtime/fastify-fetch-bridge.js +51 -0
  487. package/dist/core/runtime/fastify-server-bundle.js +26 -0
  488. package/dist/core/runtime/initialize.js +132 -0
  489. package/dist/core/runtime/loop-finalize.js +71 -0
  490. package/dist/core/runtime/loop-run-lifecycle.js +73 -0
  491. package/dist/core/runtime/loop-run-reporter.js +19 -0
  492. package/dist/core/runtime/loop-runtime-config.js +26 -0
  493. package/dist/core/runtime/loop-session-runner.js +30 -0
  494. package/dist/core/runtime/loop.js +84 -0
  495. package/dist/core/runtime/paths.js +84 -0
  496. package/dist/core/runtime/process-runner.js +16 -0
  497. package/dist/core/runtime/process-types.js +2 -0
  498. package/dist/core/runtime/semaphore.js +41 -0
  499. package/dist/core/runtime/sidecar-fastify-plugin.js +35 -0
  500. package/dist/core/runtime/sidecar-paths.js +47 -0
  501. package/dist/core/runtime/sidecar-route-catalog.js +103 -0
  502. package/dist/core/runtime/spawn-command.js +392 -0
  503. package/dist/core/runtime/spawn-interactive.js +71 -0
  504. package/dist/core/security/redaction.js +160 -0
  505. package/dist/core/session/compression.js +323 -0
  506. package/dist/core/session/flow.js +85 -0
  507. package/dist/core/session/manager.js +313 -0
  508. package/dist/core/session/pruning-strategy.js +153 -0
  509. package/dist/core/session/session-context-builder.js +122 -0
  510. package/dist/core/session/summary-sync.js +82 -0
  511. package/dist/core/session/token-tracker.js +82 -0
  512. package/dist/core/session/types.js +2 -0
  513. package/dist/core/skills/bridge.js +33 -0
  514. package/dist/core/skills/index.js +8 -0
  515. package/dist/core/skills/loader.js +80 -0
  516. package/dist/core/skills/parser.js +66 -0
  517. package/dist/core/skills/runtime/MicroTaskRunner.js +102 -0
  518. package/dist/core/skills/runtime/SkillRunner.js +108 -0
  519. package/dist/core/skills/strategy.js +29 -0
  520. package/dist/core/skills/types.js +2 -0
  521. package/dist/core/slash/index.js +6 -0
  522. package/dist/core/slash/parser.js +33 -0
  523. package/dist/core/slash/registry.js +78 -0
  524. package/dist/core/slash/router.js +76 -0
  525. package/dist/core/slash/steps/slash-decide.js +19 -0
  526. package/dist/core/slash/steps/slash-execute.js +73 -0
  527. package/dist/core/slash/steps/types.js +2 -0
  528. package/dist/core/slash/strategy.js +33 -0
  529. package/dist/core/slash/types.js +2 -0
  530. package/dist/core/strata/checkpoint/manager.js +492 -0
  531. package/dist/core/strata/checkpoint/snapshot-audit.js +88 -0
  532. package/dist/core/strata/checkpoint/snapshot-create.js +79 -0
  533. package/dist/core/strata/checkpoint/snapshot-write-tree.js +72 -0
  534. package/dist/core/strata/engine/shadow-merge-engine.js +394 -0
  535. package/dist/core/strata/index.js +15 -0
  536. package/dist/core/strata/interaction/content-guardian.js +59 -0
  537. package/dist/core/strata/interaction/file-system-provider.js +89 -0
  538. package/dist/core/strata/layers/file-state-resolver.js +157 -0
  539. package/dist/core/strata/layers/immutable-git-layer.js +42 -0
  540. package/dist/core/strata/layers/shadow-driver/copy-backend.js +114 -0
  541. package/dist/core/strata/layers/shadow-driver/env.js +29 -0
  542. package/dist/core/strata/layers/shadow-driver/error-classifier.js +41 -0
  543. package/dist/core/strata/layers/shadow-driver/index.js +17 -0
  544. package/dist/core/strata/layers/shadow-driver/readonly-lock.js +221 -0
  545. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +234 -0
  546. package/dist/core/strata/layers/shadow-driver/strategy.js +86 -0
  547. package/dist/core/strata/layers/sidecar-layer.js +96 -0
  548. package/dist/core/strata/layers/worktree.js +240 -0
  549. package/dist/core/strata/runtime/environment.js +377 -0
  550. package/dist/core/strata/runtime/synchronizer.js +819 -0
  551. package/dist/core/strata/types.js +46 -0
  552. package/dist/core/streaming/canonical/canonical-responses-event-emitter.js +326 -0
  553. package/dist/core/streaming/canonical/function-call-item-id.js +13 -0
  554. package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +54 -0
  555. package/dist/core/streaming/canonical/responses-event-emitter.js +127 -0
  556. package/dist/core/streaming/canonical/responses-events.js +2 -0
  557. package/dist/core/streaming/normalized-events.js +9 -0
  558. package/dist/core/streaming/normalized-from-text.js +47 -0
  559. package/dist/core/streaming/stream-assembler.js +347 -0
  560. package/dist/core/structured-output/index.js +3 -0
  561. package/dist/core/structured-output/json-extract.js +70 -0
  562. package/dist/core/structured-output/json-schema-validator.js +90 -0
  563. package/dist/core/structured-output/types.js +2 -0
  564. package/dist/core/sub-agent/artifacts/store.js +141 -0
  565. package/dist/core/sub-agent/artifacts/types.js +2 -0
  566. package/dist/core/sub-agent/controller.js +69 -0
  567. package/dist/core/sub-agent/core/loop.js +79 -0
  568. package/dist/core/sub-agent/core/manager.js +246 -0
  569. package/dist/core/sub-agent/registry-defaults.js +52 -0
  570. package/dist/core/sub-agent/registry.js +35 -0
  571. package/dist/core/sub-agent/tools/task-spawn.js +29 -0
  572. package/dist/core/sub-agent/types.js +23 -0
  573. package/dist/core/target-runtime/command-resolver.js +42 -0
  574. package/dist/core/target-runtime/index.js +3 -0
  575. package/dist/core/target-runtime/profile.js +73 -0
  576. package/dist/core/testgen/detector.js +17 -0
  577. package/dist/core/testgen/index.js +38 -0
  578. package/dist/core/testgen/templates.js +46 -0
  579. package/dist/core/tools/audit.js +140 -0
  580. package/dist/core/tools/authorization/types.js +2 -0
  581. package/dist/core/tools/budget.js +118 -0
  582. package/dist/core/tools/builtin/artifact.js +29 -0
  583. package/dist/core/tools/builtin/ast-grep.js +107 -0
  584. package/dist/core/tools/builtin/ast.js +62 -0
  585. package/dist/core/tools/builtin/code-search/backends/powershell.js +84 -0
  586. package/dist/core/tools/builtin/code-search/backends/rg.js +85 -0
  587. package/dist/core/tools/builtin/code-search/executor.js +87 -0
  588. package/dist/core/tools/builtin/code-search/parse/plain-grep.js +59 -0
  589. package/dist/core/tools/builtin/code-search/parse/rg-json.js +31 -0
  590. package/dist/core/tools/builtin/code-search/spec.js +82 -0
  591. package/dist/core/tools/builtin/fs.js +243 -0
  592. package/dist/core/tools/builtin/git.js +118 -0
  593. package/dist/core/tools/builtin/index.js +80 -0
  594. package/dist/core/tools/builtin/interaction.js +120 -0
  595. package/dist/core/tools/builtin/knowledge.js +98 -0
  596. package/dist/core/tools/builtin/plan.js +148 -0
  597. package/dist/core/tools/builtin/proposal.js +207 -0
  598. package/dist/core/tools/builtin/shell.js +71 -0
  599. package/dist/core/tools/builtin/verify.js +41 -0
  600. package/dist/core/tools/capability/executor.js +84 -0
  601. package/dist/core/tools/capability/runner.js +50 -0
  602. package/dist/core/tools/capability/types.js +2 -0
  603. package/dist/core/tools/dispatcher.js +80 -0
  604. package/dist/core/tools/headless-payload.js +37 -0
  605. package/dist/core/tools/loader.js +100 -0
  606. package/dist/core/tools/mapper.js +142 -0
  607. package/dist/core/tools/mcp/client.js +308 -0
  608. package/dist/core/tools/mcp/loader.js +110 -0
  609. package/dist/core/tools/mcp/schema.js +54 -0
  610. package/dist/core/tools/mcp/streamable-http.js +101 -0
  611. package/dist/core/tools/mcp/types.js +26 -0
  612. package/dist/core/tools/parallel/isolation.js +25 -0
  613. package/dist/core/tools/parallel/lock-manager.js +124 -0
  614. package/dist/core/tools/parallel/persistence.js +126 -0
  615. package/dist/core/tools/parallel/plan-builder.js +66 -0
  616. package/dist/core/tools/parallel/plan.js +2 -0
  617. package/dist/core/tools/parallel/refs.js +7 -0
  618. package/dist/core/tools/parallel/resolve-args.js +50 -0
  619. package/dist/core/tools/parallel/resource-helpers.js +35 -0
  620. package/dist/core/tools/parallel/resources.js +2 -0
  621. package/dist/core/tools/parallel/scheduler.js +372 -0
  622. package/dist/core/tools/parser.js +89 -0
  623. package/dist/core/tools/permissions/permission-rules.js +503 -0
  624. package/dist/core/tools/plugins/loader.js +102 -0
  625. package/dist/core/tools/policy.js +87 -0
  626. package/dist/core/tools/registry.js +29 -0
  627. package/dist/core/tools/router.js +514 -0
  628. package/dist/core/tools/sanitize.js +78 -0
  629. package/dist/core/tools/schema-utils.js +71 -0
  630. package/dist/core/tools/session.js +1105 -0
  631. package/dist/core/tools/streaming/ToolCallAccumulator.js +64 -0
  632. package/dist/core/tools/types.js +2 -0
  633. package/dist/core/types/authorization.js +2 -0
  634. package/dist/core/types/context.js +2 -0
  635. package/dist/core/types/errors.js +29 -0
  636. package/dist/core/types/execution.js +65 -0
  637. package/dist/core/types/index.js +9 -0
  638. package/dist/core/types/llm.js +9 -0
  639. package/dist/core/types/loop.js +2 -0
  640. package/dist/core/types/planning.js +2 -0
  641. package/dist/core/types/runtime.js +2 -0
  642. package/dist/core/types/usage.js +2 -0
  643. package/dist/core/ui/kaomoji.js +5 -0
  644. package/dist/core/utils/path.js +116 -0
  645. package/dist/core/utils/platform-shell.js +10 -0
  646. package/dist/core/utils/sanitizer.js +107 -0
  647. package/dist/core/verification/runner.js +265 -0
  648. package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +272 -0
  649. package/dist/integrations/langfuse/outcome-proxy.js +68 -0
  650. package/dist/interfaces/cli/task-runner.js +11 -0
  651. package/dist/languages/typescript/index.js +178 -0
  652. package/dist/locales/en.js +679 -0
  653. package/dist/locales/index.js +11 -0
  654. package/dist/utils/eol.js +35 -0
  655. package/package.json +153 -0
@@ -0,0 +1,492 @@
1
+ import { randomBytes } from 'crypto';
2
+ import { tmpdir } from 'os';
3
+ import { join } from 'path';
4
+ import { mkdir, rm } from '../../adapters/fs/node-fs.js';
5
+ import { GitAdapter } from '../../adapters/git/git-adapter.js';
6
+ import { recordAuditEvent } from '../../observability/audit-trail.js';
7
+ import { getLogger } from '../../observability/logger.js';
8
+ import { normalizePath } from '../../utils/path.js';
9
+ import { extractSafeSnapshotErrorSummary, hashRepoPathForAudit } from './snapshot-audit.js';
10
+ import { createSnapshotCommitFromStagedTree } from './snapshot-create.js';
11
+ import { probeWriteTreeFailure, tryWriteTreeWithRetry } from './snapshot-write-tree.js';
12
+ export class CheckpointManager {
13
+ writeTreeRetryDelaysMs = [30, 80];
14
+ /**
15
+ * Creates a safe snapshot of the current repository state (T0).
16
+ * S8P Checkpoint Protocol v1.0
17
+ *
18
+ * Definition:
19
+ * - T0 (Snapshot): The stable baseline state when the AI task begins.
20
+ *
21
+ * Features:
22
+ * - Zero pollution: Does not modify the user's index or working tree.
23
+ * - Precision: Separately captures staged and working tree states.
24
+ * - Safety: Excludes ignored files (like node_modules) by default.
25
+ */
26
+ async createSafeSnapshot(repoPath, includePaths = [], message) {
27
+ let step;
28
+ // 1. Capture Staged State (read directly from user's real index)
29
+ // git write-tree generates a tree object from the current index
30
+ const git = new GitAdapter(repoPath);
31
+ try {
32
+ step = 'write-tree';
33
+ const { tree: stagedTree } = await tryWriteTreeWithRetry(git, this.writeTreeRetryDelaysMs);
34
+ const commitHash = await createSnapshotCommitFromStagedTree({
35
+ git,
36
+ stagedTree,
37
+ includePaths,
38
+ message,
39
+ onStep: (currentStep) => {
40
+ step = currentStep;
41
+ },
42
+ });
43
+ // 9. Mount Reference (Persistence)
44
+ // Use update-ref to prevent GC and allow easy lookup
45
+ step = 'update-ref';
46
+ await git.query([
47
+ 'update-ref',
48
+ '-m',
49
+ 's8p-checkpoint',
50
+ `refs/s8p/snapshots/${commitHash}`,
51
+ commitHash,
52
+ ]);
53
+ return { commitHash, stagedTree };
54
+ }
55
+ catch (error) {
56
+ if (step === 'write-tree' ||
57
+ step === 'read-tree' ||
58
+ step === 'add-u' ||
59
+ step === 'write-tree-final' ||
60
+ step === 'commit-tree') {
61
+ let writeTreeProbe = {};
62
+ if (step === 'write-tree') {
63
+ try {
64
+ writeTreeProbe = await probeWriteTreeFailure(git);
65
+ }
66
+ catch {
67
+ writeTreeProbe = {};
68
+ }
69
+ }
70
+ recordAuditEvent('snapshot.create.step.failed', {
71
+ step,
72
+ repoPathHash: hashRepoPathForAudit(repoPath),
73
+ includePathsCount: includePaths.length,
74
+ ...extractSafeSnapshotErrorSummary(error),
75
+ ...writeTreeProbe,
76
+ }, { source: 'runtime', severity: 'high', scope: 'session', phase: 'PREFLIGHT' });
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+ /**
82
+ * Restores a snapshot to a shadow worktree.
83
+ * CRITICAL: Never run this on the main repository!
84
+ *
85
+ * DESIGN INTENT (see docs/design/checkpoint.md):
86
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
87
+ * This implements the "Source is Truth" principle by creating HIGH-FIDELITY replica:
88
+ * - Staged changes (git add)
89
+ * - Unstaged changes (modified but not staged)
90
+ * - Untracked files (captured in snapshot)
91
+ *
92
+ * Why the "Dirty State" is INTENTIONAL (not a bug):
93
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
94
+ * Common Misunderstanding:
95
+ * ❌ "Having Index ≠ Worktree means the system is inconsistent"
96
+ * ✅ Correct: This is the FOUNDATION of 3-way merge semantics
97
+ *
98
+ * Design Philosophy:
99
+ * This method intentionally preserves the "dirty state" to maintain staged/unstaged distinction:
100
+ * - Working tree = snapshot's working tree (contains all changes including unstaged)
101
+ * - Index = snapshot's staged tree (only staged changes)
102
+ * - git status = unstaged changes (Working - Staged)
103
+ *
104
+ * Why This Matters for Apply-Back (docs/design/applyback.md):
105
+ * When AI patches are applied, 3-way merge needs:
106
+ * Base: Original Staged State (from snapshot metadata)
107
+ * Theirs: User's concurrent changes (if any)
108
+ * Ours: AI-generated patches
109
+ *
110
+ * If we flattened (Index = Worktree), we LOSE staged/unstaged distinction,
111
+ * breaking transactional semantics.
112
+ *
113
+ * This design is CORRECT and necessary for:
114
+ * 1. Preserving user's original staged/unstaged state in worktree isolation
115
+ * 2. Enabling 3-way merge to correctly incorporate user changes
116
+ * 3. Supporting workflow where dirty data must be maintained across operations
117
+ *
118
+ * Post-Condition (Best-Effort):
119
+ * - Git index is refreshed to reflect working tree state
120
+ * - Subsequent fs.readFile operations should observe the latest working tree content
121
+ * - Note: This is a best-effort guarantee, not a strict fsync operation
122
+ */
123
+ async restoreToShadow(repoPath, shadowPath, snapshotHash) {
124
+ const git = new GitAdapter(repoPath);
125
+ const shadowGit = new GitAdapter(shadowPath);
126
+ // 1. Get Metadata
127
+ const msg = await git.query(['log', '-1', '--format=%B', snapshotHash]);
128
+ let meta;
129
+ try {
130
+ meta = JSON.parse(msg);
131
+ }
132
+ catch {
133
+ throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
134
+ }
135
+ if (!meta.staged) {
136
+ throw new Error(`Snapshot ${snapshotHash} missing staged tree info`);
137
+ }
138
+ getLogger().debug(`[restoreToShadow] Restoring snapshot ${snapshotHash} to shadow worktree`);
139
+ getLogger().debug(`[restoreToShadow] Snapshot metadata - staged tree: ${meta.staged}`);
140
+ // 2. Restore Working Tree
141
+ // Force checkout the snapshot in the shadow worktree
142
+ // This sets HEAD, Index, and Working Tree to the snapshot state (Dirty)
143
+ await shadowGit.exec(['checkout', '-f', snapshotHash]);
144
+ getLogger().debug(`[restoreToShadow] Step 1: Checked out snapshot to shadow worktree`);
145
+ // 3. Reset HEAD to Original Parent
146
+ // The snapshot commit is created with HEAD as parent.
147
+ // We want the shadow worktree's HEAD to match the original repo's HEAD (Clean),
148
+ // so that git diff shows the correct unstaged changes.
149
+ const parent = await git.query(['rev-parse', `${snapshotHash}^`]);
150
+ const parentTrimmed = parent.trim();
151
+ await shadowGit.exec(['reset', '--soft', parentTrimmed]);
152
+ getLogger().debug(`[restoreToShadow] Step 2: Reset HEAD to parent ${parentTrimmed}`);
153
+ // 4. Restore Staged State
154
+ // Read the staged tree into the shadow worktree's index
155
+ // This ensures the Index matches the original Staged state.
156
+ await shadowGit.query(['read-tree', meta.staged]);
157
+ getLogger().debug(`[restoreToShadow] Step 3: Restored staged tree to index`);
158
+ // At this point:
159
+ // - Shadow Disk = Snapshot Working Tree (contains dirty data)
160
+ // - Shadow Index = Snapshot Staged State (original staged tree)
161
+ // - git status = Unstaged changes (Working - Staged)
162
+ //
163
+ // This design correctly preserves the original staged/unstaged state.
164
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
165
+ // CRITICAL: Filesystem Cache Synchronization
166
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
167
+ // Problem (especially on Windows):
168
+ // After git operations, filesystem caches may contain stale data.
169
+ // Subsequent fs.readFile() might read OLD content from before checkout.
170
+ //
171
+ // Solution:
172
+ // 'git update-index --refresh' forces Git to stat() all tracked files,
173
+ // updating its internal cache to match current filesystem state.
174
+ //
175
+ // Why NOT throw on error (defensive design):
176
+ // - This is PERFORMANCE OPTIMIZATION, not correctness requirement
177
+ // - System has FALLBACK: readSnapshotFile() reads from Git Object Database
178
+ // - See docs/design/checkpoint.md L38-42: "reads directly from blob"
179
+ // - Throwing would break execution for non-critical optimization failure
180
+ //
181
+ // Performance optimization: Use 'git update-index --refresh' instead of
182
+ // 'git status' to avoid scanning untracked files in large repositories.
183
+ // Then use 'status -uno' for logging without full untracked scan.
184
+ try {
185
+ // Refresh index entries for tracked files only (fast, no untracked scan)
186
+ await shadowGit.query(['update-index', '-q', '--refresh']);
187
+ getLogger().debug(`[restoreToShadow] Git index refreshed successfully`);
188
+ // Get status for logging (skip untracked files for performance)
189
+ const status = await shadowGit.query(['status', '--short', '-uno']);
190
+ const headRef = await shadowGit.query(['rev-parse', 'HEAD']);
191
+ getLogger().debug(`[restoreToShadow] Post-restore git status in ${shadowPath}:\n${status || '(clean)'}`);
192
+ getLogger().debug(`[restoreToShadow] Current HEAD: ${headRef.trim()}`);
193
+ if (status.trim()) {
194
+ getLogger().debug(`[restoreToShadow] Shadow worktree contains unstaged changes as expected. ` +
195
+ `This preserves the original dirty state for 3-way merge.`);
196
+ }
197
+ }
198
+ catch (e) {
199
+ // NOT a critical failure - system has fallback via Git Object Database
200
+ getLogger().error(`[restoreToShadow] Failed to refresh index or verify status: ${e instanceof Error ? e.message : String(e)}`);
201
+ // Intentionally NOT throwing - execution continues with ODB fallback
202
+ // See readSnapshotFile() for the direct Git object database read path
203
+ }
204
+ }
205
+ /**
206
+ * Reads a file from a snapshot using Git object database.
207
+ *
208
+ * ARCHITECTURE OPTIMIZATION: This is the PREFERRED method for reading file content.
209
+ *
210
+ * Advantages:
211
+ * 1. Avoids filesystem cache issues entirely (no Windows cache delays)
212
+ * 2. Works with untracked files (captured in snapshot)
213
+ * 3. Works with ignored files (if explicitly included via includePaths)
214
+ * 4. Provides consistent cross-platform behavior
215
+ * 5. Better performance (direct object read vs filesystem I/O)
216
+ *
217
+ * @param repoPath - Repository path (main or shadow worktree)
218
+ * @param snapshotHash - Snapshot commit hash
219
+ * @param filePath - Relative file path (will be normalized)
220
+ * @returns File content as string, or null if file doesn't exist in snapshot
221
+ */
222
+ async readSnapshotFile(repoPath, snapshotHash, filePath) {
223
+ try {
224
+ const git = new GitAdapter(repoPath);
225
+ const normalized = normalizePath(filePath);
226
+ const content = await git.query(['show', `${snapshotHash}:${normalized}`]);
227
+ return content;
228
+ }
229
+ catch (error) {
230
+ const msg = error instanceof Error ? error.message : String(error);
231
+ // Only return null if the file truly doesn't exist in the snapshot
232
+ // Git messages:
233
+ // - "fatal: Path '...' does not exist in '...'"
234
+ // - "fatal: path '...' exists on disk, but not in '...'"
235
+ if (msg.includes('does not exist') ||
236
+ msg.includes('not in') ||
237
+ msg.includes('invalid object name')) {
238
+ getLogger().debug(`[CheckpointManager] File ${filePath} not found in snapshot ${snapshotHash}`);
239
+ return null;
240
+ }
241
+ // Rethrow unexpected errors (e.g. git process crash, corruption) to avoid masking real issues
242
+ throw error;
243
+ }
244
+ }
245
+ /**
246
+ * Exports a snapshot to a specific directory.
247
+ * Uses git checkout-index to avoid creating a .git directory in the target.
248
+ */
249
+ async exportSnapshot(repoPath, snapshotHash, targetDir) {
250
+ // Ensure target directory exists
251
+ await mkdir(targetDir, { recursive: true });
252
+ // Use a temporary index to avoid messing with the main repo index
253
+ const random = randomBytes(4).toString('hex');
254
+ const tempIndexFile = join(tmpdir(), `s8p-export-idx-${Date.now()}-${random}`);
255
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndexFile };
256
+ try {
257
+ // 1. Read snapshot tree into temporary index
258
+ const git = new GitAdapter(repoPath);
259
+ await git.exec(['read-tree', snapshotHash], { env });
260
+ // 2. Checkout index to target directory
261
+ // Note: checkout-index requires trailing slash in prefix to denote directory
262
+ const normalizedTarget = normalizePath(targetDir);
263
+ const prefix = normalizedTarget.endsWith('/') ? normalizedTarget : `${normalizedTarget}/`;
264
+ await git.exec(['checkout-index', '-a', '--prefix', prefix], { env });
265
+ }
266
+ finally {
267
+ // Cleanup temporary index
268
+ try {
269
+ await rm(tempIndexFile, { force: true });
270
+ }
271
+ catch {
272
+ // Ignore cleanup errors
273
+ }
274
+ }
275
+ }
276
+ /**
277
+ * Restores a snapshot to the main repository.
278
+ * WARNING: This is a destructive operation.
279
+ */
280
+ async restoreToMain(repoPath, snapshotHash, force = false) {
281
+ const git = new GitAdapter(repoPath);
282
+ // 1. Safety Check
283
+ if (!force) {
284
+ const status = await git.getStatus();
285
+ if (status.trim()) {
286
+ throw new Error('Workspace is dirty. Use --force to overwrite.');
287
+ }
288
+ }
289
+ // 2. Get Metadata
290
+ const msg = await git.query(['log', '-1', '--format=%B', snapshotHash]);
291
+ let meta;
292
+ try {
293
+ meta = JSON.parse(msg);
294
+ }
295
+ catch {
296
+ throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
297
+ }
298
+ // 3. Get Parent Commit (Original HEAD)
299
+ // The snapshot commit is created with HEAD as parent
300
+ const parent = await git.query(['rev-parse', `${snapshotHash}^`]);
301
+ // 4. Restore HEAD (Soft Reset)
302
+ // Move HEAD back to where it was when snapshot was taken
303
+ await git.exec(['reset', '--soft', parent.trim()]);
304
+ // 5. Cleanup AI mess before restoring snapshot
305
+ await git.exec(['clean', '-fd', '-e', '.salmonloop']);
306
+ // 6. Restore Working Tree
307
+ // Checkout files from snapshot commit into working directory
308
+ await git.exec(['checkout', snapshotHash, '--', '.']);
309
+ // 7. Restore Index
310
+ // Reset index to match the staged tree from metadata
311
+ await git.query(['read-tree', meta.staged]);
312
+ }
313
+ /**
314
+ * Lists all available snapshots.
315
+ */
316
+ async listSnapshots(repoPath, limit) {
317
+ try {
318
+ const args = [
319
+ 'for-each-ref',
320
+ '--sort=-committerdate',
321
+ '--format=%(refname) %(objectname:short) %(committerdate:iso) %(subject)',
322
+ ];
323
+ if (limit && limit > 0) {
324
+ args.push(`--count=${limit}`);
325
+ }
326
+ args.push('refs/s8p/snapshots/');
327
+ const git = new GitAdapter(repoPath);
328
+ const output = await git.query(args);
329
+ return output
330
+ .split('\n')
331
+ .filter((line) => line.trim())
332
+ .map((line) => {
333
+ // Format: refs/s8p/snapshots/<hash> <short-hash> <date> <subject>
334
+ const parts = line.split(' ');
335
+ const ref = parts[0];
336
+ const hash = parts[1];
337
+ const timestamp = parts.slice(2, 5).join(' ');
338
+ const message = parts.slice(5).join(' ');
339
+ return { hash, timestamp, message, ref };
340
+ });
341
+ }
342
+ catch {
343
+ return [];
344
+ }
345
+ }
346
+ /**
347
+ * Deletes a specific snapshot by its hash.
348
+ * If the hash is short, it tries to find the full ref.
349
+ */
350
+ async deleteSnapshot(repoPath, snapshotHash) {
351
+ const git = new GitAdapter(repoPath);
352
+ // Try to delete directly first (assuming full hash matches ref name)
353
+ try {
354
+ await git.exec(['update-ref', '-d', `refs/s8p/snapshots/${snapshotHash}`]);
355
+ return;
356
+ }
357
+ catch {
358
+ // If direct deletion fails (maybe hash mismatch or short hash), try to find the ref
359
+ }
360
+ // Fallback: find the ref pointing to this hash
361
+ // Note: This is expensive if there are many snapshots, but safe.
362
+ // However, clearSnapshots should use the ref directly for performance.
363
+ const snapshots = await this.listSnapshots(repoPath);
364
+ const target = snapshots.find((s) => s.hash.startsWith(snapshotHash));
365
+ if (target) {
366
+ await git.exec(['update-ref', '-d', target.ref]);
367
+ }
368
+ else {
369
+ // If we can't find it, maybe it's already gone.
370
+ // We could throw, but idempotency is nice.
371
+ getLogger().debug(`Could not find snapshot ref for hash ${snapshotHash} to delete.`);
372
+ }
373
+ }
374
+ /**
375
+ * Clears all snapshots.
376
+ */
377
+ async clearSnapshots(repoPath) {
378
+ const git = new GitAdapter(repoPath);
379
+ const snapshots = await this.listSnapshots(repoPath);
380
+ for (const snapshot of snapshots) {
381
+ // Use the full ref name directly for reliable deletion
382
+ await git.exec(['update-ref', '-d', snapshot.ref]);
383
+ }
384
+ }
385
+ /**
386
+ * Gets details of a snapshot (staged vs unstaged files).
387
+ */
388
+ async getSnapshotDetails(repoPath, snapshotHash) {
389
+ const git = new GitAdapter(repoPath);
390
+ const msg = await git.query(['log', '-1', '--format=%B', snapshotHash]);
391
+ let meta;
392
+ try {
393
+ meta = JSON.parse(msg);
394
+ }
395
+ catch {
396
+ throw new Error(`Invalid snapshot metadata for ${snapshotHash}`);
397
+ }
398
+ // Staged files: Diff between Parent (HEAD at time of snapshot) and Staged Tree
399
+ // Note: We use snapshot^ (Parent) as the base
400
+ const stagedOutput = await git.query(['diff', '--name-only', `${snapshotHash}^`, meta.staged]);
401
+ // Unstaged files: Diff between Staged Tree and Snapshot (Working Tree)
402
+ const unstagedOutput = await git.query(['diff', '--name-only', meta.staged, snapshotHash]);
403
+ return {
404
+ stagedFiles: stagedOutput.split('\n').filter((f) => f.trim()),
405
+ unstagedFiles: unstagedOutput.split('\n').filter((f) => f.trim()),
406
+ };
407
+ }
408
+ /**
409
+ * Gets the content of a file from a snapshot.
410
+ */
411
+ async getSnapshotFileContent(repoPath, snapshotHash, filePath) {
412
+ const git = new GitAdapter(repoPath);
413
+ return await git.query(['show', `${snapshotHash}:${filePath}`]);
414
+ }
415
+ /**
416
+ * Gets the list of all files contained in a snapshot.
417
+ */
418
+ async getSnapshotFiles(repoPath, snapshotHash) {
419
+ const git = new GitAdapter(repoPath);
420
+ const output = await git.query(['ls-tree', '-r', '--name-only', snapshotHash]);
421
+ return output.split('\n').filter((f) => f.trim());
422
+ }
423
+ /**
424
+ * Gets the diff between a snapshot and the current workspace, or between two snapshots.
425
+ */
426
+ async getSnapshotDiff(repoPath, hash, otherHash, codeMode = false) {
427
+ const git = new GitAdapter(repoPath);
428
+ const args = ['diff'];
429
+ // If not in code mode, use --stat for summary.
430
+ if (!codeMode) {
431
+ args.push('--stat');
432
+ }
433
+ if (otherHash) {
434
+ args.push(hash, otherHash);
435
+ }
436
+ else {
437
+ args.push(hash); // Compares working tree (implicitly) with hash.
438
+ // Note: "git diff hash" compares working tree with hash.
439
+ // "git diff hash HEAD" compares hash with HEAD.
440
+ // If we want "Current Workspace vs Snapshot", usually we want to see what changed SINCE snapshot.
441
+ // git diff <snapshot>.. (working tree)
442
+ }
443
+ return await git.query(args);
444
+ }
445
+ /**
446
+ * Creates a lightweight dirty backup (T1) using git stash create.
447
+ * This captures the exact state of Index and Worktree at the moment of call (Pre-Apply).
448
+ *
449
+ * Definition:
450
+ * - T1 (Dirty Backup): The user's workspace state just before AI changes are applied.
451
+ * This captures any user edits made between T0 and T1.
452
+ *
453
+ * Returns a stash commit hash or null if workspace is clean.
454
+ */
455
+ async createDirtyBackup(repoPath) {
456
+ const git = new GitAdapter(repoPath);
457
+ const status = await git.query(['status', '--porcelain']);
458
+ if (!status.trim())
459
+ return null;
460
+ // Use the robust snapshot mechanism instead of 'stash create' to ensure
461
+ // that complex states like "AD" (Added in index, Deleted in worktree)
462
+ // are correctly captured.
463
+ const snapshot = await this.createSafeSnapshot(repoPath, [], 'T1 Dirty Backup');
464
+ return snapshot.commitHash;
465
+ }
466
+ /**
467
+ * Restores a dirty backup (T1).
468
+ */
469
+ async restoreDirtyBackup(repoPath, backupHash) {
470
+ const git = new GitAdapter(repoPath);
471
+ getLogger().debug(`[CheckpointManager] Restoring dirty backup T1: ${backupHash}`);
472
+ // 1. Get Metadata (stored by createSafeSnapshot)
473
+ const msg = await git.query(['log', '-1', '--format=%B', backupHash]);
474
+ let meta;
475
+ try {
476
+ meta = JSON.parse(msg);
477
+ }
478
+ catch {
479
+ throw new Error(`Invalid backup metadata for ${backupHash}`);
480
+ }
481
+ // 2. Restore Worktree and Index to the T1 state
482
+ // We use read-tree --reset -u to ensure the worktree matches the backup commit exactly,
483
+ // which correctly restores deleted files.
484
+ await git.exec(['read-tree', '--reset', '-u', backupHash]);
485
+ // 3. Restore the original Staged state
486
+ // This resets the index to match the user's original staged changes.
487
+ await git.exec(['read-tree', meta.staged]);
488
+ // 4. Update stat cache so 'git status' is immediately accurate.
489
+ await git.exec(['update-index', '--refresh'], { allowError: true });
490
+ }
491
+ }
492
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1,88 @@
1
+ import { createHash } from 'crypto';
2
+ function extractSafeErrorCode(error) {
3
+ if (!error || typeof error !== 'object')
4
+ return undefined;
5
+ if (typeof error.code === 'string') {
6
+ return error.code;
7
+ }
8
+ if (typeof error.name === 'string') {
9
+ return error.name;
10
+ }
11
+ return undefined;
12
+ }
13
+ function hashSafe(value) {
14
+ return createHash('sha256').update(value).digest('hex').slice(0, 16);
15
+ }
16
+ export function hashRepoPathForAudit(repoPath) {
17
+ return hashSafe(repoPath);
18
+ }
19
+ export function classifyGitFailureHint(error) {
20
+ const asRecord = error && typeof error === 'object' ? error : null;
21
+ if (!asRecord)
22
+ return undefined;
23
+ const stderr = typeof asRecord.stderr === 'string' ? asRecord.stderr.toLowerCase() : '';
24
+ const message = typeof asRecord.message === 'string' ? asRecord.message.toLowerCase() : '';
25
+ const command = typeof asRecord.command === 'string' ? asRecord.command.toLowerCase() : '';
26
+ const body = `${message}\n${stderr}`;
27
+ if (body.includes('index.lock'))
28
+ return 'GIT_INDEX_LOCKED';
29
+ if (body.includes('you need to resolve your current index first') ||
30
+ body.includes('unmerged files')) {
31
+ return 'GIT_INDEX_UNMERGED';
32
+ }
33
+ if (body.includes('error building trees'))
34
+ return 'GIT_TREE_BUILD_FAILED';
35
+ if (body.includes('invalid object') || body.includes('invalid sha1 pointer')) {
36
+ return 'GIT_OBJECT_CORRUPTED';
37
+ }
38
+ if (body.includes('not a git repository'))
39
+ return 'GIT_NOT_REPOSITORY';
40
+ if (body.includes('must be run in a work tree'))
41
+ return 'GIT_NOT_WORKTREE';
42
+ if (body.includes('detected dubious ownership'))
43
+ return 'GIT_DUBIOUS_OWNERSHIP';
44
+ if (body.includes('permission denied'))
45
+ return 'GIT_PERMISSION_DENIED';
46
+ if (body.includes('unable to read index file') ||
47
+ body.includes('index file smaller than expected') ||
48
+ body.includes('bad index file') ||
49
+ body.includes('index file corrupt')) {
50
+ return 'GIT_INDEX_CORRUPTED';
51
+ }
52
+ if (body.includes('unable to write new index file') || body.includes('could not write index')) {
53
+ return 'GIT_INDEX_WRITE_FAILED';
54
+ }
55
+ if (body.includes('no space left on device'))
56
+ return 'GIT_NO_SPACE';
57
+ if (command.includes('write-tree') && body.includes('fatal:'))
58
+ return 'GIT_WRITE_TREE_FATAL';
59
+ return 'GIT_FAILURE_UNKNOWN';
60
+ }
61
+ export function extractSafeSnapshotErrorSummary(error) {
62
+ if (!error || typeof error !== 'object') {
63
+ return {
64
+ errorName: typeof error,
65
+ };
66
+ }
67
+ const asRecord = error;
68
+ const safe = {
69
+ errorCode: extractSafeErrorCode(error),
70
+ errorName: typeof asRecord.name === 'string' ? asRecord.name : undefined,
71
+ errorHintCode: classifyGitFailureHint(error),
72
+ writeTreeAttempts: typeof asRecord.writeTreeAttempts === 'number' ? asRecord.writeTreeAttempts : undefined,
73
+ };
74
+ if (typeof asRecord.message === 'string' && asRecord.message.length > 0) {
75
+ safe.errorFingerprint = hashSafe(asRecord.message);
76
+ }
77
+ if (typeof asRecord.stderr === 'string' && asRecord.stderr.length > 0) {
78
+ const firstLine = asRecord.stderr.split('\n')[0] ?? '';
79
+ if (firstLine.length > 0) {
80
+ safe.stderrFingerprint = hashSafe(firstLine);
81
+ }
82
+ }
83
+ if (typeof asRecord.command === 'string' && asRecord.command.length > 0) {
84
+ safe.commandFingerprint = hashSafe(asRecord.command);
85
+ }
86
+ return safe;
87
+ }
88
+ //# sourceMappingURL=snapshot-audit.js.map
@@ -0,0 +1,79 @@
1
+ import { randomBytes } from 'crypto';
2
+ import { tmpdir } from 'os';
3
+ import { join } from 'path';
4
+ import { rm } from '../../adapters/fs/node-fs.js';
5
+ import { normalizePath } from '../../utils/path.js';
6
+ async function getSafeUntrackedFiles(git) {
7
+ // --exclude-standard: Respect .gitignore
8
+ // -o: List other (untracked) files
9
+ const output = await git.query(['ls-files', '-o', '--exclude-standard']);
10
+ const files = output.split('\n').filter((f) => f.trim());
11
+ // Hardcoded blacklist for double safety
12
+ return files.filter((f) => {
13
+ const normalized = normalizePath(f);
14
+ return (!normalized.includes('node_modules/') &&
15
+ !normalized.includes('.env') &&
16
+ !normalized.includes('.git/') &&
17
+ !normalized.includes('dist/') &&
18
+ !normalized.includes('build/'));
19
+ });
20
+ }
21
+ export async function createSnapshotCommitFromStagedTree(input) {
22
+ const { git, stagedTree, includePaths, message, onStep } = input;
23
+ const random = randomBytes(4).toString('hex');
24
+ const tempIndexFile = join(tmpdir(), `s8p-idx-${Date.now()}-${random}`);
25
+ const env = { GIT_INDEX_FILE: tempIndexFile };
26
+ try {
27
+ // We use the staged tree as the base because it contains all tracked files
28
+ // (including newly added ones), while HEAD may not.
29
+ onStep?.('read-tree');
30
+ await git.exec(['read-tree', stagedTree], { env });
31
+ // Update tracked files (Modified/Deleted) from working tree.
32
+ onStep?.('add-u');
33
+ await git.exec(['add', '-u', '.'], { env });
34
+ const untracked = await getSafeUntrackedFiles(git);
35
+ if (untracked.length > 0) {
36
+ await git.exec(['add', '--', ...untracked], { env });
37
+ }
38
+ // Explicit include list supports ignored paths when needed.
39
+ if (includePaths.length > 0) {
40
+ for (const file of includePaths) {
41
+ try {
42
+ const isIgnored = await git
43
+ .exec(['check-ignore', file])
44
+ .then((out) => !!out.trim())
45
+ .catch(() => false);
46
+ if (isIgnored) {
47
+ await git.exec(['add', '-f', '--', file], { env });
48
+ }
49
+ else {
50
+ await git.exec(['add', '--', file], { env });
51
+ }
52
+ }
53
+ catch {
54
+ // Ignore per-file add failures to keep snapshot best-effort.
55
+ }
56
+ }
57
+ }
58
+ onStep?.('write-tree-final');
59
+ const workingTree = (await git.exec(['write-tree'], { env })).trim();
60
+ const metadata = JSON.stringify({
61
+ v: '1.0',
62
+ staged: stagedTree,
63
+ forced: includePaths,
64
+ desc: message,
65
+ ts: Date.now(),
66
+ });
67
+ onStep?.('commit-tree');
68
+ return (await git.exec(['commit-tree', workingTree, '-p', 'HEAD', '-m', metadata], { env })).trim();
69
+ }
70
+ finally {
71
+ try {
72
+ await rm(tempIndexFile, { force: true });
73
+ }
74
+ catch {
75
+ // Ignore cleanup errors.
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=snapshot-create.js.map