@ebowwa/coder 0.7.63 → 0.7.65
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.
- package/dist/core/__tests__/permissions.test.d.ts +12 -0
- package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/core/__tests__/permissions.test.js +851 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/index.test.js +121 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
- package/dist/core/agent-loop/compaction.d.ts +39 -0
- package/dist/core/agent-loop/compaction.d.ts.map +1 -0
- package/dist/core/agent-loop/compaction.js +51 -0
- package/dist/core/agent-loop/formatters.d.ts +21 -0
- package/dist/core/agent-loop/formatters.d.ts.map +1 -0
- package/dist/core/agent-loop/formatters.js +42 -0
- package/dist/core/agent-loop/index.d.ts +25 -0
- package/dist/core/agent-loop/index.d.ts.map +1 -0
- package/dist/core/agent-loop/index.js +83 -0
- package/dist/core/agent-loop/loop-state.d.ts +74 -0
- package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
- package/dist/core/agent-loop/loop-state.js +147 -0
- package/dist/core/agent-loop/message-builder.d.ts +13 -0
- package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
- package/dist/core/agent-loop/message-builder.js +49 -0
- package/dist/core/agent-loop/tool-executor.d.ts +23 -0
- package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/tool-executor.js +152 -0
- package/dist/core/agent-loop/turn-executor.d.ts +57 -0
- package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/turn-executor.js +124 -0
- package/dist/core/agent-loop/types.d.ts +141 -0
- package/dist/core/agent-loop/types.d.ts.map +1 -0
- package/dist/core/agent-loop/types.js +4 -0
- package/dist/core/agent-loop.d.ts +17 -0
- package/dist/core/agent-loop.d.ts.map +1 -0
- package/dist/core/agent-loop.js +16 -0
- package/dist/core/api-client-impl.d.ts +62 -0
- package/dist/core/api-client-impl.d.ts.map +1 -0
- package/dist/core/api-client-impl.js +479 -0
- package/dist/core/api-client.d.ts +6 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/api-client.js +5 -0
- package/dist/core/checkpoints.d.ts +128 -0
- package/dist/core/checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints.js +438 -0
- package/dist/core/claude-md.d.ts +71 -0
- package/dist/core/claude-md.d.ts.map +1 -0
- package/dist/core/claude-md.js +198 -0
- package/dist/core/cognitive-security/hooks.d.ts +138 -0
- package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
- package/dist/core/cognitive-security/hooks.js +389 -0
- package/dist/core/cognitive-security/index.d.ts +751 -0
- package/dist/core/cognitive-security/index.d.ts.map +1 -0
- package/dist/core/cognitive-security/index.js +1123 -0
- package/dist/core/cognitive-security/middleware.d.ts +136 -0
- package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
- package/dist/core/cognitive-security/middleware.js +376 -0
- package/dist/core/config-loader.d.ts +127 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +219 -0
- package/dist/core/context-compaction.d.ts +87 -0
- package/dist/core/context-compaction.d.ts.map +1 -0
- package/dist/core/context-compaction.js +428 -0
- package/dist/core/git-status.d.ts +25 -0
- package/dist/core/git-status.d.ts.map +1 -0
- package/dist/core/git-status.js +204 -0
- package/dist/core/image.d.ts +69 -0
- package/dist/core/image.d.ts.map +1 -0
- package/dist/core/image.js +290 -0
- package/dist/core/image.test.d.ts +2 -0
- package/dist/core/image.test.d.ts.map +1 -0
- package/dist/core/image.test.js +149 -0
- package/dist/core/models.d.ts +123 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +325 -0
- package/dist/core/permissions.d.ts +81 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +327 -0
- package/dist/core/retry.d.ts +25 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +121 -0
- package/dist/core/session-store.d.ts +9 -0
- package/dist/core/session-store.d.ts.map +1 -0
- package/dist/core/session-store.js +10 -0
- package/dist/core/sessions/export.d.ts +47 -0
- package/dist/core/sessions/export.d.ts.map +1 -0
- package/dist/core/sessions/export.js +256 -0
- package/dist/core/sessions/index.d.ts +132 -0
- package/dist/core/sessions/index.d.ts.map +1 -0
- package/dist/core/sessions/index.js +442 -0
- package/dist/core/sessions/metadata.d.ts +77 -0
- package/dist/core/sessions/metadata.d.ts.map +1 -0
- package/dist/core/sessions/metadata.js +233 -0
- package/dist/core/sessions/persistence.d.ts +72 -0
- package/dist/core/sessions/persistence.d.ts.map +1 -0
- package/dist/core/sessions/persistence.js +201 -0
- package/dist/core/sessions/types.d.ts +110 -0
- package/dist/core/sessions/types.d.ts.map +1 -0
- package/dist/core/sessions/types.js +4 -0
- package/dist/core/stream-highlighter.d.ts +18 -0
- package/dist/core/stream-highlighter.d.ts.map +1 -0
- package/dist/core/stream-highlighter.js +916 -0
- package/dist/core/system-reminders.d.ts +89 -0
- package/dist/core/system-reminders.d.ts.map +1 -0
- package/dist/core/system-reminders.js +285 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
- package/dist/ecosystem/hooks/index.d.ts +59 -0
- package/dist/ecosystem/hooks/index.d.ts.map +1 -0
- package/dist/ecosystem/hooks/index.js +294 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
- package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
- package/dist/ecosystem/skills/index.d.ts +55 -0
- package/dist/ecosystem/skills/index.d.ts.map +1 -0
- package/dist/ecosystem/skills/index.js +258 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
- package/dist/ecosystem/tools/index.d.ts +24 -0
- package/dist/ecosystem/tools/index.d.ts.map +1 -0
- package/dist/ecosystem/tools/index.js +1709 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33688 -49712
- package/dist/interfaces/mcp/client.d.ts +40 -0
- package/dist/interfaces/mcp/client.d.ts.map +1 -0
- package/dist/interfaces/mcp/client.js +309 -0
- package/dist/interfaces/ui/index.d.ts +36 -0
- package/dist/interfaces/ui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/index.js +61 -0
- package/dist/interfaces/ui/spinner.d.ts +140 -0
- package/dist/interfaces/ui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/spinner.js +342 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/cli/index.js +32012 -50526
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/args.js +176 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/index.js +16 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/query.js +100 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/console.js +46 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/index.js +28 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/run.js +31 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/types.js +5 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -0
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/dist/teammates/index.d.ts +161 -0
- package/dist/teammates/index.d.ts.map +1 -0
- package/dist/teammates/index.js +827 -0
- package/dist/types/index.d.ts +482 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +52 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +6 -3
- package/packages/src/core/__tests__/permissions.test.ts +1091 -0
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +283 -0
- package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
- package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
- package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
- package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
- package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
- package/packages/src/core/agent-loop/compaction.ts +92 -0
- package/packages/src/core/agent-loop/formatters.ts +50 -0
- package/packages/src/core/agent-loop/index.ts +137 -0
- package/packages/src/core/agent-loop/loop-state.ts +187 -0
- package/packages/src/core/agent-loop/message-builder.ts +62 -0
- package/packages/src/core/agent-loop/tool-executor.ts +211 -0
- package/packages/src/core/agent-loop/turn-executor.ts +226 -0
- package/packages/src/core/agent-loop/types.ts +152 -0
- package/packages/src/core/agent-loop.ts +18 -0
- package/packages/src/core/api-client-impl.ts +729 -0
- package/packages/src/core/api-client.ts +6 -0
- package/packages/src/core/checkpoints.ts +606 -0
- package/packages/src/core/claude-md.ts +272 -0
- package/packages/src/core/cognitive-security/hooks.ts +591 -0
- package/packages/src/core/cognitive-security/index.ts +2041 -0
- package/packages/src/core/cognitive-security/middleware.ts +536 -0
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/config-loader.ts +324 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/git-status.ts +262 -0
- package/packages/src/core/image.test.ts +180 -0
- package/packages/src/core/image.ts +350 -0
- package/packages/src/core/lmdb.db +0 -0
- package/packages/src/core/lmdb.db-lock +0 -0
- package/packages/src/core/models.ts +507 -0
- package/packages/src/core/normalizers/todo +8 -0
- package/packages/src/core/permissions.ts +431 -0
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +180 -0
- package/packages/src/core/session-store.ts +36 -0
- package/packages/src/core/sessions/export.ts +329 -0
- package/packages/src/core/sessions/index.ts +587 -0
- package/packages/src/core/sessions/metadata.ts +309 -0
- package/packages/src/core/sessions/persistence.ts +244 -0
- package/packages/src/core/sessions/types.ts +169 -0
- package/packages/src/core/stream-highlighter.ts +1123 -0
- package/packages/src/core/system-reminders.ts +402 -0
- package/packages/src/core/todo +8 -0
- package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
- package/packages/src/ecosystem/hooks/index.ts +341 -0
- package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
- package/packages/src/ecosystem/skills/index.ts +295 -0
- package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
- package/packages/src/ecosystem/tools/index.ts +2051 -0
- package/packages/src/index.ts +141 -0
- package/packages/src/interfaces/mcp/client.ts +389 -0
- package/packages/src/interfaces/ui/index.ts +158 -0
- package/packages/src/interfaces/ui/lmdb.db +0 -0
- package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/spinner.ts +451 -0
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +415 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +393 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +84 -0
- package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +152 -0
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +299 -0
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +366 -0
- package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
- package/packages/src/lmdb.db +0 -0
- package/packages/src/lmdb.db-lock +0 -0
- package/packages/src/native/index.ts +2722 -0
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +1052 -0
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +723 -0
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint Manager - Save and restore conversation states
|
|
3
|
+
* Captures both chat context AND code/file changes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import type { Message } from "../types/index.js";
|
|
9
|
+
|
|
10
|
+
export interface FileSnapshot {
|
|
11
|
+
path: string;
|
|
12
|
+
content: string;
|
|
13
|
+
hash: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface GitState {
|
|
17
|
+
branch: string;
|
|
18
|
+
ahead: number;
|
|
19
|
+
behind: number;
|
|
20
|
+
staged: string[];
|
|
21
|
+
unstaged: string[];
|
|
22
|
+
untracked: string[];
|
|
23
|
+
stash?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Checkpoint {
|
|
27
|
+
id: string;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
label: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
messages: Message[];
|
|
33
|
+
files: FileSnapshot[];
|
|
34
|
+
gitState?: GitState;
|
|
35
|
+
metadata: {
|
|
36
|
+
model?: string;
|
|
37
|
+
workingDirectory?: string;
|
|
38
|
+
totalCost: number;
|
|
39
|
+
messageCount: number;
|
|
40
|
+
fileCount: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface CheckpointStore {
|
|
45
|
+
checkpoints: Map<string, Checkpoint>;
|
|
46
|
+
sessionFile: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const CHECKPOINTS_DIR = process.env.CLAUDE_CHECKPOINTS_DIR || `${process.env.HOME}/.claude/checkpoints`;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Generate a simple hash for file content
|
|
53
|
+
*/
|
|
54
|
+
function hashContent(content: string): string {
|
|
55
|
+
let hash = 0;
|
|
56
|
+
for (let i = 0; i < content.length; i++) {
|
|
57
|
+
const char = content.charCodeAt(i);
|
|
58
|
+
hash = ((hash << 5) - hash) + char;
|
|
59
|
+
hash = hash & hash;
|
|
60
|
+
}
|
|
61
|
+
return Math.abs(hash).toString(16);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get current git state
|
|
66
|
+
*/
|
|
67
|
+
function getGitState(workingDir: string): GitState | undefined {
|
|
68
|
+
try {
|
|
69
|
+
// Check if we're in a git repo
|
|
70
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
71
|
+
cwd: workingDir,
|
|
72
|
+
encoding: "utf-8",
|
|
73
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
77
|
+
cwd: workingDir,
|
|
78
|
+
encoding: "utf-8",
|
|
79
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
80
|
+
}).trim();
|
|
81
|
+
|
|
82
|
+
// Get ahead/behind counts
|
|
83
|
+
let ahead = 0;
|
|
84
|
+
let behind = 0;
|
|
85
|
+
try {
|
|
86
|
+
const counts = execSync("git rev-list --left-right --count @{upstream}...HEAD 2>/dev/null || echo '0 0'", {
|
|
87
|
+
cwd: workingDir,
|
|
88
|
+
encoding: "utf-8",
|
|
89
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
90
|
+
}).trim().split(/\s+/);
|
|
91
|
+
behind = parseInt(counts[0] || "0", 10);
|
|
92
|
+
ahead = parseInt(counts[1] || "0", 10);
|
|
93
|
+
} catch {
|
|
94
|
+
// No upstream
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const status = execSync("git status --porcelain", {
|
|
98
|
+
cwd: workingDir,
|
|
99
|
+
encoding: "utf-8",
|
|
100
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
101
|
+
}).trim();
|
|
102
|
+
|
|
103
|
+
const staged: string[] = [];
|
|
104
|
+
const unstaged: string[] = [];
|
|
105
|
+
const untracked: string[] = [];
|
|
106
|
+
|
|
107
|
+
for (const line of status.split("\n")) {
|
|
108
|
+
if (!line.trim()) continue;
|
|
109
|
+
const indexStatus = line[0];
|
|
110
|
+
const workTreeStatus = line[1];
|
|
111
|
+
const filePath = line.slice(3);
|
|
112
|
+
|
|
113
|
+
if (indexStatus === "?" && workTreeStatus === "?") {
|
|
114
|
+
untracked.push(filePath);
|
|
115
|
+
} else if (indexStatus !== " " && indexStatus !== "?") {
|
|
116
|
+
staged.push(filePath);
|
|
117
|
+
} else if (workTreeStatus !== " ") {
|
|
118
|
+
unstaged.push(filePath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { branch, ahead, behind, staged, unstaged, untracked };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
// Not in a git repo or git not available
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create a git stash with checkpoint info
|
|
131
|
+
*/
|
|
132
|
+
function createCheckpointStash(workingDir: string, checkpointId: string): string | undefined {
|
|
133
|
+
try {
|
|
134
|
+
const stashName = `claude-checkpoint-${checkpointId}`;
|
|
135
|
+
execSync(`git stash push -m "${stashName}" --include-untracked 2>/dev/null || true`, {
|
|
136
|
+
cwd: workingDir,
|
|
137
|
+
encoding: "utf-8",
|
|
138
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
139
|
+
});
|
|
140
|
+
return stashName;
|
|
141
|
+
} catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Capture file snapshots for modified files
|
|
148
|
+
*/
|
|
149
|
+
async function captureFileSnapshots(
|
|
150
|
+
workingDir: string,
|
|
151
|
+
filePaths: string[]
|
|
152
|
+
): Promise<FileSnapshot[]> {
|
|
153
|
+
const snapshots: FileSnapshot[] = [];
|
|
154
|
+
|
|
155
|
+
for (const filePath of filePaths) {
|
|
156
|
+
try {
|
|
157
|
+
const fullPath = `${workingDir}/${filePath}`;
|
|
158
|
+
const file = Bun.file(fullPath);
|
|
159
|
+
|
|
160
|
+
if (await file.exists()) {
|
|
161
|
+
const content = await file.text();
|
|
162
|
+
snapshots.push({
|
|
163
|
+
path: filePath,
|
|
164
|
+
content,
|
|
165
|
+
hash: hashContent(content),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
// Skip files that can't be read
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return snapshots;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Restore files from snapshots
|
|
178
|
+
*/
|
|
179
|
+
async function restoreFileSnapshots(
|
|
180
|
+
workingDir: string,
|
|
181
|
+
snapshots: FileSnapshot[]
|
|
182
|
+
): Promise<{ restored: number; failed: number }> {
|
|
183
|
+
let restored = 0;
|
|
184
|
+
let failed = 0;
|
|
185
|
+
|
|
186
|
+
for (const snapshot of snapshots) {
|
|
187
|
+
try {
|
|
188
|
+
const fullPath = `${workingDir}/${snapshot.path}`;
|
|
189
|
+
await Bun.write(fullPath, snapshot.content);
|
|
190
|
+
restored++;
|
|
191
|
+
} catch {
|
|
192
|
+
failed++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return { restored, failed };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Ensure checkpoints directory exists
|
|
201
|
+
*/
|
|
202
|
+
async function ensureCheckpointsDir(): Promise<void> {
|
|
203
|
+
const dir = Bun.file(CHECKPOINTS_DIR);
|
|
204
|
+
if (!(await dir.exists())) {
|
|
205
|
+
await Bun.write(CHECKPOINTS_DIR + "/.gitkeep", "");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get checkpoint file path for a session
|
|
211
|
+
*/
|
|
212
|
+
function getCheckpointFilePath(sessionId: string): string {
|
|
213
|
+
return `${CHECKPOINTS_DIR}/${sessionId}.json`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Load checkpoints for a session
|
|
218
|
+
*/
|
|
219
|
+
export async function loadCheckpoints(sessionId: string): Promise<Map<string, Checkpoint>> {
|
|
220
|
+
const checkpoints = new Map<string, Checkpoint>();
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const filePath = getCheckpointFilePath(sessionId);
|
|
224
|
+
const file = Bun.file(filePath);
|
|
225
|
+
|
|
226
|
+
if (await file.exists()) {
|
|
227
|
+
const content = await file.text();
|
|
228
|
+
const data = JSON.parse(content) as Checkpoint[];
|
|
229
|
+
|
|
230
|
+
for (const checkpoint of data) {
|
|
231
|
+
checkpoints.set(checkpoint.id, checkpoint);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
// Return empty map on error
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return checkpoints;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Save checkpoints for a session
|
|
243
|
+
*/
|
|
244
|
+
export async function saveCheckpoints(
|
|
245
|
+
sessionId: string,
|
|
246
|
+
checkpoints: Map<string, Checkpoint>
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
await ensureCheckpointsDir();
|
|
249
|
+
|
|
250
|
+
const filePath = getCheckpointFilePath(sessionId);
|
|
251
|
+
const data = Array.from(checkpoints.values());
|
|
252
|
+
|
|
253
|
+
await Bun.write(filePath, JSON.stringify(data, null, 2));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Create a new checkpoint with file snapshots
|
|
258
|
+
*/
|
|
259
|
+
export async function createCheckpoint(
|
|
260
|
+
sessionId: string,
|
|
261
|
+
messages: Message[],
|
|
262
|
+
options: {
|
|
263
|
+
label?: string;
|
|
264
|
+
description?: string;
|
|
265
|
+
model?: string;
|
|
266
|
+
workingDirectory?: string;
|
|
267
|
+
totalCost?: number;
|
|
268
|
+
trackFiles?: boolean;
|
|
269
|
+
} = {}
|
|
270
|
+
): Promise<Checkpoint> {
|
|
271
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
272
|
+
const workingDir = options.workingDirectory || process.cwd();
|
|
273
|
+
|
|
274
|
+
// Capture git state
|
|
275
|
+
const gitState = getGitState(workingDir);
|
|
276
|
+
|
|
277
|
+
// Capture file snapshots for all changed files
|
|
278
|
+
let fileSnapshots: FileSnapshot[] = [];
|
|
279
|
+
if (options.trackFiles !== false && gitState) {
|
|
280
|
+
const changedFiles = [
|
|
281
|
+
...gitState.staged,
|
|
282
|
+
...gitState.unstaged,
|
|
283
|
+
...gitState.untracked,
|
|
284
|
+
];
|
|
285
|
+
fileSnapshots = await captureFileSnapshots(workingDir, changedFiles);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const checkpoint: Checkpoint = {
|
|
289
|
+
id: randomUUID().slice(0, 8),
|
|
290
|
+
sessionId,
|
|
291
|
+
timestamp: Date.now(),
|
|
292
|
+
label: options.label || `Checkpoint ${checkpoints.size + 1}`,
|
|
293
|
+
description: options.description,
|
|
294
|
+
messages: JSON.parse(JSON.stringify(messages)), // Deep copy
|
|
295
|
+
files: fileSnapshots,
|
|
296
|
+
gitState,
|
|
297
|
+
metadata: {
|
|
298
|
+
model: options.model,
|
|
299
|
+
workingDirectory: workingDir,
|
|
300
|
+
totalCost: options.totalCost || 0,
|
|
301
|
+
messageCount: messages.length,
|
|
302
|
+
fileCount: fileSnapshots.length,
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
checkpoints.set(checkpoint.id, checkpoint);
|
|
307
|
+
await saveCheckpoints(sessionId, checkpoints);
|
|
308
|
+
|
|
309
|
+
return checkpoint;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Restore a checkpoint (returns the checkpoint, doesn't apply it)
|
|
314
|
+
*/
|
|
315
|
+
export async function restoreCheckpoint(
|
|
316
|
+
sessionId: string,
|
|
317
|
+
checkpointId: string
|
|
318
|
+
): Promise<Checkpoint | null> {
|
|
319
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
320
|
+
return checkpoints.get(checkpointId) || null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Apply a checkpoint - restore files and return messages
|
|
325
|
+
*/
|
|
326
|
+
export async function applyCheckpoint(
|
|
327
|
+
checkpoint: Checkpoint,
|
|
328
|
+
options: {
|
|
329
|
+
restoreFiles?: boolean;
|
|
330
|
+
restoreMessages?: boolean;
|
|
331
|
+
workingDirectory?: string;
|
|
332
|
+
} = {}
|
|
333
|
+
): Promise<{
|
|
334
|
+
messages: Message[];
|
|
335
|
+
filesRestored: number;
|
|
336
|
+
filesFailed: number;
|
|
337
|
+
}> {
|
|
338
|
+
const workingDir = options.workingDirectory || checkpoint.metadata.workingDirectory || process.cwd();
|
|
339
|
+
|
|
340
|
+
let filesRestored = 0;
|
|
341
|
+
let filesFailed = 0;
|
|
342
|
+
|
|
343
|
+
// Restore files if requested
|
|
344
|
+
if (options.restoreFiles !== false && checkpoint.files.length > 0) {
|
|
345
|
+
const result = await restoreFileSnapshots(workingDir, checkpoint.files);
|
|
346
|
+
filesRestored = result.restored;
|
|
347
|
+
filesFailed = result.failed;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
messages: options.restoreMessages !== false ? checkpoint.messages : [],
|
|
352
|
+
filesRestored,
|
|
353
|
+
filesFailed,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Delete a checkpoint
|
|
359
|
+
*/
|
|
360
|
+
export async function deleteCheckpoint(
|
|
361
|
+
sessionId: string,
|
|
362
|
+
checkpointId: string
|
|
363
|
+
): Promise<boolean> {
|
|
364
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
365
|
+
|
|
366
|
+
if (checkpoints.has(checkpointId)) {
|
|
367
|
+
checkpoints.delete(checkpointId);
|
|
368
|
+
await saveCheckpoints(sessionId, checkpoints);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* List all checkpoints for a session
|
|
377
|
+
*/
|
|
378
|
+
export async function listCheckpoints(sessionId: string): Promise<Checkpoint[]> {
|
|
379
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
380
|
+
return Array.from(checkpoints.values()).sort((a, b) => b.timestamp - a.timestamp);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Format checkpoint for display
|
|
385
|
+
*/
|
|
386
|
+
export function formatCheckpoint(checkpoint: Checkpoint, verbose = false): string {
|
|
387
|
+
const date = new Date(checkpoint.timestamp);
|
|
388
|
+
const timeStr = date.toLocaleTimeString();
|
|
389
|
+
const dateStr = date.toLocaleDateString();
|
|
390
|
+
|
|
391
|
+
let output = `\x1b[33m${checkpoint.id}\x1b[0m `;
|
|
392
|
+
output += `\x1b[1m${checkpoint.label}\x1b[0m `;
|
|
393
|
+
output += `\x1b[90m(${dateStr} ${timeStr})\x1b[0m`;
|
|
394
|
+
|
|
395
|
+
// Show file count if there are files
|
|
396
|
+
if (checkpoint.files.length > 0) {
|
|
397
|
+
output += ` \x1b[32m[${checkpoint.files.length} files]\x1b[0m`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Show git branch if available
|
|
401
|
+
if (checkpoint.gitState) {
|
|
402
|
+
output += ` \x1b[34m(${checkpoint.gitState.branch})\x1b[0m`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (verbose) {
|
|
406
|
+
output += `\n Messages: ${checkpoint.metadata.messageCount}`;
|
|
407
|
+
output += `\n Files: ${checkpoint.metadata.fileCount}`;
|
|
408
|
+
output += `\n Cost: $${checkpoint.metadata.totalCost.toFixed(4)}`;
|
|
409
|
+
if (checkpoint.gitState) {
|
|
410
|
+
const changes = checkpoint.gitState.staged.length +
|
|
411
|
+
checkpoint.gitState.unstaged.length +
|
|
412
|
+
checkpoint.gitState.untracked.length;
|
|
413
|
+
output += `\n Git changes: ${changes}`;
|
|
414
|
+
}
|
|
415
|
+
if (checkpoint.description) {
|
|
416
|
+
output += `\n ${checkpoint.description}`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return output;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Print checkpoints list
|
|
425
|
+
*/
|
|
426
|
+
export function printCheckpointsList(checkpoints: Checkpoint[]): void {
|
|
427
|
+
if (checkpoints.length === 0) {
|
|
428
|
+
console.log("\x1b[90mNo checkpoints saved.\x1b[0m");
|
|
429
|
+
console.log("\x1b[90mUse /checkpoint <label> to create one.\x1b[0m");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
console.log(`\n\x1b[1mCheckpoints (${checkpoints.length}):\x1b[0m`);
|
|
434
|
+
console.log("\x1b[90m─────────────────────────────────────────────────\x1b[0m");
|
|
435
|
+
|
|
436
|
+
for (const checkpoint of checkpoints) {
|
|
437
|
+
console.log(formatCheckpoint(checkpoint));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
console.log("\x1b[90m─────────────────────────────────────────────────\x1b[0m");
|
|
441
|
+
console.log("\x1b[90m/restore <id> - Restore checkpoint (files + chat)\x1b[0m");
|
|
442
|
+
console.log("\x1b[90m/restore-chat <id> - Restore chat only (no files)\x1b[0m");
|
|
443
|
+
console.log("\x1b[90m/checkpoint <label> - Create new checkpoint\x1b[0m");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Clear all checkpoints for a session
|
|
448
|
+
*/
|
|
449
|
+
export async function clearCheckpoints(sessionId: string): Promise<number> {
|
|
450
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
451
|
+
const count = checkpoints.size;
|
|
452
|
+
checkpoints.clear();
|
|
453
|
+
await saveCheckpoints(sessionId, checkpoints);
|
|
454
|
+
return count;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Get checkpoint summary for status display
|
|
459
|
+
*/
|
|
460
|
+
export function getCheckpointSummary(checkpoint: Checkpoint): string {
|
|
461
|
+
const parts: string[] = [];
|
|
462
|
+
|
|
463
|
+
if (checkpoint.files.length > 0) {
|
|
464
|
+
parts.push(`${checkpoint.files.length} files`);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
parts.push(`${checkpoint.metadata.messageCount} msgs`);
|
|
468
|
+
|
|
469
|
+
if (checkpoint.gitState) {
|
|
470
|
+
const changes = checkpoint.gitState.staged.length +
|
|
471
|
+
checkpoint.gitState.unstaged.length +
|
|
472
|
+
checkpoint.gitState.untracked.length;
|
|
473
|
+
if (changes > 0) {
|
|
474
|
+
parts.push(`${changes} changes`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return parts.join(" | ");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ============================================
|
|
482
|
+
// CHECKPOINT NAVIGATION (UNDO/REDO)
|
|
483
|
+
// ============================================
|
|
484
|
+
|
|
485
|
+
interface CheckpointNavigation {
|
|
486
|
+
sessionId: string;
|
|
487
|
+
checkpointIds: string[]; // Ordered list of checkpoint IDs
|
|
488
|
+
currentIndex: number; // Current position in the list (-1 = none)
|
|
489
|
+
undoneIds: string[]; // Checkpoints that were undone (for redo)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const NAVIGATION_FILE = (sessionId: string) => `${CHECKPOINTS_DIR}/${sessionId}-nav.json`;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Get checkpoint navigation state
|
|
496
|
+
*/
|
|
497
|
+
async function getNavigation(sessionId: string): Promise<CheckpointNavigation> {
|
|
498
|
+
try {
|
|
499
|
+
const file = Bun.file(NAVIGATION_FILE(sessionId));
|
|
500
|
+
if (await file.exists()) {
|
|
501
|
+
return JSON.parse(await file.text()) as CheckpointNavigation;
|
|
502
|
+
}
|
|
503
|
+
} catch {
|
|
504
|
+
// Return fresh navigation
|
|
505
|
+
}
|
|
506
|
+
return { sessionId, checkpointIds: [], currentIndex: -1, undoneIds: [] };
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Save checkpoint navigation state
|
|
511
|
+
*/
|
|
512
|
+
async function saveNavigation(nav: CheckpointNavigation): Promise<void> {
|
|
513
|
+
await ensureCheckpointsDir();
|
|
514
|
+
await Bun.write(NAVIGATION_FILE(nav.sessionId), JSON.stringify(nav, null, 2));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Register a new checkpoint (called after createCheckpoint)
|
|
519
|
+
*/
|
|
520
|
+
export async function registerCheckpoint(sessionId: string, checkpointId: string): Promise<void> {
|
|
521
|
+
const nav = await getNavigation(sessionId);
|
|
522
|
+
|
|
523
|
+
// If we're not at the end, truncate the list (new checkpoint = no redo)
|
|
524
|
+
if (nav.currentIndex < nav.checkpointIds.length - 1) {
|
|
525
|
+
nav.checkpointIds = nav.checkpointIds.slice(0, nav.currentIndex + 1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
nav.checkpointIds.push(checkpointId);
|
|
529
|
+
nav.currentIndex = nav.checkpointIds.length - 1;
|
|
530
|
+
nav.undoneIds = []; // Clear redo stack
|
|
531
|
+
|
|
532
|
+
await saveNavigation(nav);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Undo - go back to previous checkpoint
|
|
537
|
+
*/
|
|
538
|
+
export async function undoCheckpoint(
|
|
539
|
+
sessionId: string
|
|
540
|
+
): Promise<{ checkpoint: Checkpoint | null; canRedo: boolean }> {
|
|
541
|
+
const nav = await getNavigation(sessionId);
|
|
542
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
543
|
+
|
|
544
|
+
if (nav.currentIndex <= 0) {
|
|
545
|
+
return { checkpoint: null, canRedo: false };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Save current to redo stack
|
|
549
|
+
const currentId = nav.checkpointIds[nav.currentIndex];
|
|
550
|
+
if (currentId) {
|
|
551
|
+
nav.undoneIds.push(currentId);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Move back
|
|
555
|
+
nav.currentIndex--;
|
|
556
|
+
const prevId = nav.checkpointIds[nav.currentIndex];
|
|
557
|
+
|
|
558
|
+
await saveNavigation(nav);
|
|
559
|
+
|
|
560
|
+
const checkpoint = prevId ? checkpoints.get(prevId) || null : null;
|
|
561
|
+
return { checkpoint, canRedo: nav.undoneIds.length > 0 };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Redo - go forward to next checkpoint
|
|
566
|
+
*/
|
|
567
|
+
export async function redoCheckpoint(
|
|
568
|
+
sessionId: string
|
|
569
|
+
): Promise<{ checkpoint: Checkpoint | null; canRedo: boolean }> {
|
|
570
|
+
const nav = await getNavigation(sessionId);
|
|
571
|
+
const checkpoints = await loadCheckpoints(sessionId);
|
|
572
|
+
|
|
573
|
+
if (nav.undoneIds.length === 0) {
|
|
574
|
+
return { checkpoint: null, canRedo: false };
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Pop from redo stack
|
|
578
|
+
const nextId = nav.undoneIds.pop()!;
|
|
579
|
+
nav.currentIndex++;
|
|
580
|
+
|
|
581
|
+
await saveNavigation(nav);
|
|
582
|
+
|
|
583
|
+
const checkpoint = checkpoints.get(nextId) || null;
|
|
584
|
+
return { checkpoint, canRedo: nav.undoneIds.length > 0 };
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Get navigation status
|
|
589
|
+
*/
|
|
590
|
+
export async function getNavigationStatus(sessionId: string): Promise<{
|
|
591
|
+
total: number;
|
|
592
|
+
current: number;
|
|
593
|
+
canUndo: boolean;
|
|
594
|
+
canRedo: boolean;
|
|
595
|
+
currentId?: string;
|
|
596
|
+
}> {
|
|
597
|
+
const nav = await getNavigation(sessionId);
|
|
598
|
+
|
|
599
|
+
return {
|
|
600
|
+
total: nav.checkpointIds.length,
|
|
601
|
+
current: nav.currentIndex + 1, // 1-indexed for display
|
|
602
|
+
canUndo: nav.currentIndex > 0,
|
|
603
|
+
canRedo: nav.undoneIds.length > 0,
|
|
604
|
+
currentId: nav.checkpointIds[nav.currentIndex],
|
|
605
|
+
};
|
|
606
|
+
}
|