gitspace 0.2.0-rc.20 → 0.2.0-rc.21
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/package.json +11 -6
- package/.claude/settings.local.json +0 -10
- package/.gitspace/bundle.json +0 -50
- package/.gitspace/events.json +0 -11
- package/.gitspace/processes.json +0 -23
- package/.gitspace/scripts/select/01-status.sh +0 -39
- package/.gitspace/scripts/setup/01-install-deps.sh +0 -12
- package/.gitspace/scripts/setup/02-typecheck.sh +0 -16
- package/AGENTS.md +0 -469
- package/CLAUDE.md +0 -1
- package/bun.lock +0 -794
- package/docs/CONNECTION.md +0 -623
- package/docs/GATEWAY-WORKER.md +0 -319
- package/docs/GETTING-STARTED.md +0 -448
- package/docs/GITSPACE-PLATFORM.md +0 -1819
- package/docs/INFRASTRUCTURE.md +0 -1347
- package/docs/PROTOCOL.md +0 -619
- package/docs/QUICKSTART.md +0 -183
- package/docs/RELAY.md +0 -327
- package/docs/REMOTE-DESIGN.md +0 -554
- package/docs/ROADMAP.md +0 -564
- package/docs/SITE_DOCS_FIGMA_MAKE.md +0 -1176
- package/docs/STACK-DESIGN.md +0 -588
- package/docs/UNIFIED_ARCHITECTURE.md +0 -138
- package/experiments/pty-benchmark.ts +0 -148
- package/experiments/pty-latency.ts +0 -100
- package/experiments/router/client.ts +0 -199
- package/experiments/router/protocol.ts +0 -74
- package/experiments/router/router.ts +0 -217
- package/experiments/router/session.ts +0 -180
- package/experiments/router/test.ts +0 -133
- package/experiments/socket-bandwidth.ts +0 -77
- package/homebrew/gitspace.rb +0 -45
- package/landing-page/ATTRIBUTIONS.md +0 -3
- package/landing-page/README.md +0 -11
- package/landing-page/bun.lock +0 -801
- package/landing-page/guidelines/Guidelines.md +0 -61
- package/landing-page/index.html +0 -37
- package/landing-page/package.json +0 -90
- package/landing-page/postcss.config.mjs +0 -15
- package/landing-page/public/_redirects +0 -1
- package/landing-page/public/favicon.png +0 -0
- package/landing-page/src/app/App.tsx +0 -53
- package/landing-page/src/app/components/figma/ImageWithFallback.tsx +0 -27
- package/landing-page/src/app/components/ui/accordion.tsx +0 -66
- package/landing-page/src/app/components/ui/alert-dialog.tsx +0 -157
- package/landing-page/src/app/components/ui/alert.tsx +0 -66
- package/landing-page/src/app/components/ui/aspect-ratio.tsx +0 -11
- package/landing-page/src/app/components/ui/avatar.tsx +0 -53
- package/landing-page/src/app/components/ui/badge.tsx +0 -46
- package/landing-page/src/app/components/ui/breadcrumb.tsx +0 -109
- package/landing-page/src/app/components/ui/button.tsx +0 -57
- package/landing-page/src/app/components/ui/calendar.tsx +0 -75
- package/landing-page/src/app/components/ui/card.tsx +0 -92
- package/landing-page/src/app/components/ui/carousel.tsx +0 -241
- package/landing-page/src/app/components/ui/chart.tsx +0 -353
- package/landing-page/src/app/components/ui/checkbox.tsx +0 -32
- package/landing-page/src/app/components/ui/collapsible.tsx +0 -33
- package/landing-page/src/app/components/ui/command.tsx +0 -177
- package/landing-page/src/app/components/ui/context-menu.tsx +0 -252
- package/landing-page/src/app/components/ui/dialog.tsx +0 -135
- package/landing-page/src/app/components/ui/drawer.tsx +0 -132
- package/landing-page/src/app/components/ui/dropdown-menu.tsx +0 -257
- package/landing-page/src/app/components/ui/form.tsx +0 -168
- package/landing-page/src/app/components/ui/hover-card.tsx +0 -44
- package/landing-page/src/app/components/ui/input-otp.tsx +0 -77
- package/landing-page/src/app/components/ui/input.tsx +0 -21
- package/landing-page/src/app/components/ui/label.tsx +0 -24
- package/landing-page/src/app/components/ui/menubar.tsx +0 -276
- package/landing-page/src/app/components/ui/navigation-menu.tsx +0 -168
- package/landing-page/src/app/components/ui/pagination.tsx +0 -127
- package/landing-page/src/app/components/ui/popover.tsx +0 -48
- package/landing-page/src/app/components/ui/progress.tsx +0 -31
- package/landing-page/src/app/components/ui/radio-group.tsx +0 -45
- package/landing-page/src/app/components/ui/resizable.tsx +0 -56
- package/landing-page/src/app/components/ui/scroll-area.tsx +0 -58
- package/landing-page/src/app/components/ui/select.tsx +0 -189
- package/landing-page/src/app/components/ui/separator.tsx +0 -28
- package/landing-page/src/app/components/ui/sheet.tsx +0 -139
- package/landing-page/src/app/components/ui/sidebar.tsx +0 -726
- package/landing-page/src/app/components/ui/skeleton.tsx +0 -13
- package/landing-page/src/app/components/ui/slider.tsx +0 -63
- package/landing-page/src/app/components/ui/sonner.tsx +0 -25
- package/landing-page/src/app/components/ui/switch.tsx +0 -31
- package/landing-page/src/app/components/ui/table.tsx +0 -116
- package/landing-page/src/app/components/ui/tabs.tsx +0 -66
- package/landing-page/src/app/components/ui/textarea.tsx +0 -18
- package/landing-page/src/app/components/ui/toggle-group.tsx +0 -73
- package/landing-page/src/app/components/ui/toggle.tsx +0 -47
- package/landing-page/src/app/components/ui/tooltip.tsx +0 -61
- package/landing-page/src/app/components/ui/use-mobile.ts +0 -21
- package/landing-page/src/app/components/ui/utils.ts +0 -6
- package/landing-page/src/components/docs/DocsContent.tsx +0 -801
- package/landing-page/src/components/docs/DocsSidebar.tsx +0 -90
- package/landing-page/src/components/landing/CTA.tsx +0 -59
- package/landing-page/src/components/landing/Comparison.tsx +0 -84
- package/landing-page/src/components/landing/FaultyTerminal.tsx +0 -424
- package/landing-page/src/components/landing/Features.tsx +0 -201
- package/landing-page/src/components/landing/Hero.tsx +0 -142
- package/landing-page/src/components/landing/Pricing.tsx +0 -140
- package/landing-page/src/components/landing/Roadmap.tsx +0 -86
- package/landing-page/src/components/landing/Security.tsx +0 -81
- package/landing-page/src/components/landing/TerminalWindow.tsx +0 -27
- package/landing-page/src/components/landing/UseCases.tsx +0 -55
- package/landing-page/src/components/landing/Workflow.tsx +0 -101
- package/landing-page/src/components/layout/DashboardNavbar.tsx +0 -37
- package/landing-page/src/components/layout/Footer.tsx +0 -55
- package/landing-page/src/components/layout/LandingNavbar.tsx +0 -82
- package/landing-page/src/components/ui/badge.tsx +0 -39
- package/landing-page/src/components/ui/breadcrumb.tsx +0 -115
- package/landing-page/src/components/ui/button.tsx +0 -57
- package/landing-page/src/components/ui/card.tsx +0 -79
- package/landing-page/src/components/ui/mock-terminal.tsx +0 -68
- package/landing-page/src/components/ui/separator.tsx +0 -28
- package/landing-page/src/lib/utils.ts +0 -6
- package/landing-page/src/main.tsx +0 -10
- package/landing-page/src/pages/Dashboard.tsx +0 -133
- package/landing-page/src/pages/DocsPage.tsx +0 -79
- package/landing-page/src/pages/LandingPage.tsx +0 -31
- package/landing-page/src/pages/TerminalView.tsx +0 -106
- package/landing-page/src/styles/fonts.css +0 -0
- package/landing-page/src/styles/index.css +0 -3
- package/landing-page/src/styles/tailwind.css +0 -4
- package/landing-page/src/styles/theme.css +0 -181
- package/landing-page/vite.config.ts +0 -19
- package/scripts/GHOSTTY_TAB_BUG.md +0 -106
- package/scripts/build.ts +0 -298
- package/scripts/migrate-secrets.ts +0 -77
- package/scripts/release.ts +0 -140
- package/scripts/sample-events.ts +0 -263
- package/scripts/test-tabs-minimal.ts +0 -68
- package/scripts/test-tabs-workaround.ts +0 -95
- package/scripts/test-tabs.ts +0 -171
- package/src/__tests__/test-utils.ts +0 -298
- package/src/app/input/__tests__/sessionCommands.test.ts +0 -40
- package/src/app/input/sessionCommands.ts +0 -94
- package/src/app/session/__tests__/useAttachController.test.ts +0 -229
- package/src/app/session/createSessionBackend.bun.ts +0 -76
- package/src/app/session/createSessionBackend.web.ts +0 -104
- package/src/app/session/types.ts +0 -16
- package/src/app/session/useAttachController.ts +0 -220
- package/src/app/session/useProcessActions.ts +0 -201
- package/src/app/session/useSessionClient.ts +0 -35
- package/src/app/session/useWorkspaceDeleteFlow.ts +0 -170
- package/src/app.tui.tsx +0 -2929
- package/src/app.web.tsx +0 -1454
- package/src/commands/__tests__/connect-key.test.ts +0 -10
- package/src/commands/__tests__/events.test.ts +0 -201
- package/src/commands/__tests__/notifications.test.ts +0 -349
- package/src/commands/__tests__/process.test.ts +0 -251
- package/src/commands/__tests__/serve-messages.test.ts +0 -190
- package/src/commands/__tests__/serve-process-hosting.test.ts +0 -63
- package/src/commands/access.ts +0 -298
- package/src/commands/add.ts +0 -455
- package/src/commands/auth.ts +0 -369
- package/src/commands/bundle.ts +0 -232
- package/src/commands/config.ts +0 -242
- package/src/commands/connect-key.ts +0 -1
- package/src/commands/connect.ts +0 -576
- package/src/commands/directory.ts +0 -16
- package/src/commands/events.ts +0 -157
- package/src/commands/host.ts +0 -566
- package/src/commands/identity.ts +0 -184
- package/src/commands/linear.ts +0 -717
- package/src/commands/list.ts +0 -181
- package/src/commands/migrate.ts +0 -52
- package/src/commands/notifications.ts +0 -351
- package/src/commands/process.ts +0 -104
- package/src/commands/relay.ts +0 -315
- package/src/commands/remove.ts +0 -279
- package/src/commands/review.ts +0 -787
- package/src/commands/serve.ts +0 -1946
- package/src/commands/share.ts +0 -451
- package/src/commands/status.ts +0 -125
- package/src/commands/switch.ts +0 -361
- package/src/commands/tmux.ts +0 -317
- package/src/components/DPad.web.tsx +0 -343
- package/src/components/DiffViewer.web.tsx +0 -1192
- package/src/components/Events.tsx +0 -137
- package/src/components/Events.tui.tsx +0 -129
- package/src/components/Events.web.tsx +0 -386
- package/src/components/FloatingControls.web.tsx +0 -112
- package/src/components/FloatingJogWheel.web.tsx +0 -240
- package/src/components/Flow.tsx +0 -458
- package/src/components/Flow.tui.tsx +0 -343
- package/src/components/Flow.web.tsx +0 -442
- package/src/components/Inbox.tsx +0 -448
- package/src/components/Inbox.tui.tsx +0 -262
- package/src/components/Inbox.web.tsx +0 -329
- package/src/components/MachineList.tsx +0 -187
- package/src/components/MachineList.tui.tsx +0 -161
- package/src/components/MachineList.web.tsx +0 -210
- package/src/components/NumPad.web.tsx +0 -270
- package/src/components/ProjectList.tsx +0 -175
- package/src/components/ProjectList.tui.tsx +0 -109
- package/src/components/ProjectList.web.tsx +0 -143
- package/src/components/ProjectOnboardingStep.ts +0 -23
- package/src/components/ProjectOnboardingStep.tui.tsx +0 -88
- package/src/components/ProjectOnboardingStep.web.tsx +0 -59
- package/src/components/RemoteMachineScreen.tui.tsx +0 -690
- package/src/components/ScriptTerminal.tui.tsx +0 -160
- package/src/components/ScriptTerminal.web.tsx +0 -89
- package/src/components/SessionTerminal.tui.tsx +0 -406
- package/src/components/SessionTerminal.web.tsx +0 -467
- package/src/components/SpacesBrowser.tsx +0 -540
- package/src/components/SpacesBrowser.tui.tsx +0 -258
- package/src/components/SpacesBrowser.web.tsx +0 -332
- package/src/components/TerminalControls.web.tsx +0 -464
- package/src/components/ThreadPanel.web.tsx +0 -798
- package/src/components/__tests__/SpacesBrowser.test.ts +0 -541
- package/src/components/__tests__/SpacesBrowser.tui.test.tsx +0 -249
- package/src/components/__tests__/script-terminal-buffer.tui.test.ts +0 -72
- package/src/components/index.ts +0 -105
- package/src/components/review-decision-colors.ts +0 -11
- package/src/components/script-terminal-buffer.tui.ts +0 -37
- package/src/components/session-terminal-page-navigation.ts +0 -48
- package/src/components/terminal-bracketed-paste.tui.test.ts +0 -43
- package/src/components/terminal-bracketed-paste.tui.ts +0 -46
- package/src/core/__tests__/access.test.ts +0 -240
- package/src/core/__tests__/bundle-refresh.test.ts +0 -567
- package/src/core/__tests__/bundle.test.ts +0 -209
- package/src/core/__tests__/github-review.test.ts +0 -781
- package/src/core/__tests__/project-lifecycle.test.ts +0 -137
- package/src/core/__tests__/workspace-lifecycle.test.ts +0 -159
- package/src/core/__tests__/workspace.test.ts +0 -149
- package/src/core/access.ts +0 -277
- package/src/core/bundle-refresh.ts +0 -1064
- package/src/core/bundle.ts +0 -326
- package/src/core/config.ts +0 -405
- package/src/core/git.ts +0 -768
- package/src/core/github-review.ts +0 -761
- package/src/core/github.ts +0 -151
- package/src/core/identity.ts +0 -631
- package/src/core/linear.ts +0 -403
- package/src/core/preferences-service.ts +0 -17
- package/src/core/project-catalog.ts +0 -52
- package/src/core/project-lifecycle.ts +0 -163
- package/src/core/review-executor.ts +0 -316
- package/src/core/review.ts +0 -407
- package/src/core/secret-runtime.ts +0 -167
- package/src/core/shell.ts +0 -117
- package/src/core/trusted-relays.ts +0 -315
- package/src/core/workspace-lifecycle.ts +0 -216
- package/src/core/workspace.ts +0 -363
- package/src/hooks/__tests__/useLocalSession.tui.test.ts +0 -557
- package/src/hooks/index.ts +0 -8
- package/src/hooks/index.tui.ts +0 -32
- package/src/hooks/useDaemonStatus.tui.ts +0 -174
- package/src/hooks/useLocalSession.tui.ts +0 -395
- package/src/hooks/useRelayConnection.web.ts +0 -54
- package/src/hooks/useRemoteMachines.tui.ts +0 -166
- package/src/hooks/useRemoteTerminal.tui.ts +0 -22
- package/src/hooks/useReview.web.ts +0 -248
- package/src/hooks/useTerminal.web.ts +0 -36
- package/src/hooks/useUserActivity.ts +0 -61
- package/src/hooks/useVisualViewport.web.ts +0 -104
- package/src/index.ts +0 -1376
- package/src/lib/events/__tests__/collector-filter.test.ts +0 -105
- package/src/lib/events/__tests__/store-query.test.ts +0 -103
- package/src/lib/events/collector.ts +0 -494
- package/src/lib/events/filters.ts +0 -26
- package/src/lib/events/index.ts +0 -11
- package/src/lib/events/indexer.ts +0 -14
- package/src/lib/events/paths.ts +0 -69
- package/src/lib/events/reader.ts +0 -212
- package/src/lib/events/store.ts +0 -141
- package/src/lib/invite.web.ts +0 -58
- package/src/lib/preferences-service.web.ts +0 -41
- package/src/lib/processes/__tests__/config.test.ts +0 -83
- package/src/lib/processes/__tests__/names.test.ts +0 -125
- package/src/lib/processes/__tests__/schema.test.ts +0 -208
- package/src/lib/processes/__tests__/watchdog.test.ts +0 -210
- package/src/lib/processes/autostart.ts +0 -16
- package/src/lib/processes/config.ts +0 -187
- package/src/lib/processes/control.ts +0 -53
- package/src/lib/processes/editor.ts +0 -32
- package/src/lib/processes/events-config.ts +0 -37
- package/src/lib/processes/index.ts +0 -14
- package/src/lib/processes/instances.ts +0 -20
- package/src/lib/processes/manager.ts +0 -131
- package/src/lib/processes/names.ts +0 -71
- package/src/lib/processes/registry.ts +0 -26
- package/src/lib/processes/runner.ts +0 -211
- package/src/lib/processes/scheduler.ts +0 -17
- package/src/lib/processes/schema.ts +0 -74
- package/src/lib/processes/session-list.ts +0 -15
- package/src/lib/processes/state.ts +0 -82
- package/src/lib/processes/watchdog.test.ts +0 -79
- package/src/lib/processes/watchdog.ts +0 -106
- package/src/lib/remote-session/__tests__/protocol.test.ts +0 -291
- package/src/lib/remote-session/index.ts +0 -7
- package/src/lib/remote-session/protocol.ts +0 -443
- package/src/lib/remote-session/session-handler.ts +0 -1298
- package/src/lib/remote-session/workspace-scanner.ts +0 -161
- package/src/lib/sonner.web.ts +0 -1
- package/src/lib/storage/identity-store.web.ts +0 -94
- package/src/lib/tmux-lite/README.md +0 -81
- package/src/lib/tmux-lite/cli.ts +0 -855
- package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +0 -349
- package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +0 -291
- package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +0 -142
- package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +0 -339
- package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +0 -477
- package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +0 -499
- package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +0 -371
- package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +0 -573
- package/src/lib/tmux-lite/crypto/access-control.test.ts +0 -512
- package/src/lib/tmux-lite/crypto/access-control.ts +0 -320
- package/src/lib/tmux-lite/crypto/frames.test.ts +0 -262
- package/src/lib/tmux-lite/crypto/frames.ts +0 -141
- package/src/lib/tmux-lite/crypto/handshake.ts +0 -894
- package/src/lib/tmux-lite/crypto/identity.test.ts +0 -220
- package/src/lib/tmux-lite/crypto/identity.ts +0 -286
- package/src/lib/tmux-lite/crypto/index.ts +0 -51
- package/src/lib/tmux-lite/crypto/invites.test.ts +0 -381
- package/src/lib/tmux-lite/crypto/invites.ts +0 -215
- package/src/lib/tmux-lite/crypto/keyexchange.ts +0 -435
- package/src/lib/tmux-lite/crypto/keys.test.ts +0 -58
- package/src/lib/tmux-lite/crypto/keys.ts +0 -47
- package/src/lib/tmux-lite/crypto/secretbox.test.ts +0 -169
- package/src/lib/tmux-lite/crypto/secretbox.ts +0 -124
- package/src/lib/tmux-lite/handshake-handler.ts +0 -451
- package/src/lib/tmux-lite/process-run.integration.test.ts +0 -266
- package/src/lib/tmux-lite/protocol.test.ts +0 -307
- package/src/lib/tmux-lite/protocol.ts +0 -291
- package/src/lib/tmux-lite/relay-client.ts +0 -506
- package/src/lib/tmux-lite/server-lifecycle.test.ts +0 -212
- package/src/lib/tmux-lite/server.ts +0 -1412
- package/src/lib/tmux-lite/shell-integration.sh +0 -37
- package/src/lib/tmux-lite/terminal-queries.test.ts +0 -54
- package/src/lib/tmux-lite/terminal-queries.ts +0 -49
- package/src/notifications/__tests__/useNotifications.test.ts +0 -739
- package/src/notifications/index.ts +0 -32
- package/src/notifications/policy.test.ts +0 -424
- package/src/notifications/policy.ts +0 -139
- package/src/notifications/types.ts +0 -82
- package/src/notifications/useNotifications.ts +0 -350
- package/src/pages/ReviewPage.web.tsx +0 -511
- package/src/preferences/index.ts +0 -1
- package/src/preferences/types.ts +0 -9
- package/src/relay/__tests__/e2e-flow.test.ts +0 -1284
- package/src/relay/__tests__/helpers/auth.ts +0 -354
- package/src/relay/__tests__/helpers/ports.ts +0 -51
- package/src/relay/__tests__/protocol-validation.test.ts +0 -265
- package/src/relay/authorization.ts +0 -303
- package/src/relay/embedded-assets.generated.d.ts +0 -15
- package/src/relay/identity.ts +0 -352
- package/src/relay/index.ts +0 -57
- package/src/relay/pipes.test.ts +0 -427
- package/src/relay/pipes.ts +0 -195
- package/src/relay/protocol.ts +0 -804
- package/src/relay/registries.test.ts +0 -437
- package/src/relay/registries.ts +0 -593
- package/src/relay/server.test.ts +0 -1323
- package/src/relay/server.ts +0 -1128
- package/src/relay/signing.ts +0 -238
- package/src/relay/types.ts +0 -69
- package/src/relay-client/__tests__/machine-directory-client.test.ts +0 -152
- package/src/relay-client/__tests__/useMachineDirectory.test.ts +0 -172
- package/src/relay-client/adapters/browser.ts +0 -27
- package/src/relay-client/adapters/node.ts +0 -29
- package/src/relay-client/index.ts +0 -33
- package/src/relay-client/machine-directory-client.ts +0 -244
- package/src/relay-client/useMachineDirectory.ts +0 -175
- package/src/serve/client-session-manager.ts +0 -635
- package/src/serve/daemon.ts +0 -497
- package/src/serve/pty-session.ts +0 -236
- package/src/serve/types.ts +0 -174
- package/src/session/__tests__/backend-manager.test.ts +0 -101
- package/src/session/__tests__/local-session-backend.test.ts +0 -1129
- package/src/session/__tests__/reducer.test.ts +0 -80
- package/src/session/__tests__/remote-session-backend.test.ts +0 -995
- package/src/session/__tests__/session-name.test.ts +0 -35
- package/src/session/__tests__/useBundleRefreshAttachFlow.test.ts +0 -431
- package/src/session/__tests__/useRemoteSessionClient.test.ts +0 -424
- package/src/session/__tests__/workspace-shell-hooks.integration.test.ts +0 -268
- package/src/session/__tests__/workspace-shell-hooks.test.ts +0 -24
- package/src/session/adapters/browser-remote.ts +0 -101
- package/src/session/adapters/node-remote.ts +0 -135
- package/src/session/backend-key.ts +0 -5
- package/src/session/backend-manager.ts +0 -80
- package/src/session/backend.ts +0 -93
- package/src/session/backends/local-session-backend.ts +0 -1119
- package/src/session/backends/remote-session-backend.ts +0 -1378
- package/src/session/crypto/__tests__/web-terminal.test.ts +0 -1158
- package/src/session/crypto/frames.web.ts +0 -205
- package/src/session/crypto/handshake.web.ts +0 -396
- package/src/session/crypto/identity.web.ts +0 -133
- package/src/session/crypto/keyexchange.web.ts +0 -246
- package/src/session/crypto/relay-signing.web.ts +0 -53
- package/src/session/events.ts +0 -38
- package/src/session/index.ts +0 -116
- package/src/session/reducer.ts +0 -274
- package/src/session/selectors.ts +0 -28
- package/src/session/session-name.ts +0 -50
- package/src/session/types.ts +0 -101
- package/src/session/useBundleRefreshAttachFlow.ts +0 -608
- package/src/session/useRemoteSessionClient.ts +0 -424
- package/src/session/useSessionEngine.ts +0 -432
- package/src/session/workspace-shell-hooks.ts +0 -35
- package/src/tui/__tests__/input-text.test.ts +0 -24
- package/src/tui/__tests__/local-terminal-sync.test.ts +0 -82
- package/src/tui/__tests__/session-terminal-page-navigation.test.ts +0 -94
- package/src/tui/app.tsx +0 -2
- package/src/tui/index.ts +0 -18
- package/src/tui/input-text.ts +0 -38
- package/src/tui/local-terminal-sync.ts +0 -41
- package/src/types/bundle-refresh.ts +0 -42
- package/src/types/bundle.ts +0 -130
- package/src/types/config.ts +0 -287
- package/src/types/errors.ts +0 -292
- package/src/types/events.ts +0 -91
- package/src/types/identity.ts +0 -284
- package/src/types/processes.ts +0 -45
- package/src/types/review.ts +0 -349
- package/src/types/script-phase.ts +0 -3
- package/src/types/workspace-fuzzy.ts +0 -49
- package/src/types/workspace.ts +0 -151
- package/src/utils/__tests__/onboarding.test.ts +0 -358
- package/src/utils/__tests__/run-scripts.test.ts +0 -535
- package/src/utils/__tests__/run-workspace-scripts.test.ts +0 -406
- package/src/utils/__tests__/workspace-setup.integration.test.ts +0 -633
- package/src/utils/__tests__/workspace-state.test.ts +0 -78
- package/src/utils/bun-socket-writer.ts +0 -80
- package/src/utils/clipboard.ts +0 -53
- package/src/utils/deps.test.ts +0 -31
- package/src/utils/deps.ts +0 -145
- package/src/utils/device.web.ts +0 -163
- package/src/utils/fuzzy-match.ts +0 -125
- package/src/utils/hostnames.ts +0 -43
- package/src/utils/hunk-header.ts +0 -17
- package/src/utils/id.ts +0 -9
- package/src/utils/logger.ts +0 -127
- package/src/utils/markdown.ts +0 -254
- package/src/utils/normalize-env-key.ts +0 -13
- package/src/utils/onboarding.ts +0 -279
- package/src/utils/prompts.ts +0 -176
- package/src/utils/run-commands.ts +0 -112
- package/src/utils/run-scripts.ts +0 -337
- package/src/utils/run-workspace-scripts.ts +0 -355
- package/src/utils/sanitize.test.ts +0 -149
- package/src/utils/sanitize.ts +0 -162
- package/src/utils/secrets.ts +0 -836
- package/src/utils/shell-escape.ts +0 -40
- package/src/utils/utf8.ts +0 -79
- package/src/utils/workspace-id.ts +0 -55
- package/src/utils/workspace-state.ts +0 -427
- package/src/version.generated.d.ts +0 -2
- package/todo-security.md +0 -92
- package/tsconfig.json +0 -29
- package/web/README.md +0 -73
- package/web/bun.lock +0 -675
- package/web/eslint.config.js +0 -23
- package/web/index.css +0 -249
- package/web/index.html +0 -16
- package/web/main.tsx +0 -10
- package/web/package.json +0 -39
- package/web/public/vite.svg +0 -1
- package/web/tsconfig.app.json +0 -35
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -26
- package/web/vite.config.ts +0 -39
- package/worker/bun.lock +0 -237
- package/worker/package.json +0 -22
- package/worker/schema.sql +0 -96
- package/worker/src/handlers/auth.ts +0 -451
- package/worker/src/handlers/subdomains.ts +0 -376
- package/worker/src/handlers/user.ts +0 -98
- package/worker/src/index.ts +0 -70
- package/worker/src/middleware/auth.ts +0 -152
- package/worker/src/services/cloudflare.ts +0 -609
- package/worker/src/types.ts +0 -96
- package/worker/tsconfig.json +0 -15
- package/worker/wrangler.toml +0 -26
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local review operation executor.
|
|
3
|
-
*
|
|
4
|
-
* Shared by LocalSessionBackend (direct filesystem access) and
|
|
5
|
-
* RemoteSessionHandler (over encrypted WebSocket). Implements all
|
|
6
|
-
* ReviewOperation variants using the core review and git modules.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
getThreads,
|
|
11
|
-
createThread,
|
|
12
|
-
addReply,
|
|
13
|
-
updateThread,
|
|
14
|
-
updateComment,
|
|
15
|
-
deleteComment,
|
|
16
|
-
detectPRNumber,
|
|
17
|
-
} from './review.js';
|
|
18
|
-
import {
|
|
19
|
-
getWorkspaceChangedFiles,
|
|
20
|
-
getWorkspaceDiff,
|
|
21
|
-
getWorkspaceFileContextRange,
|
|
22
|
-
getWorkspaceFileDiff,
|
|
23
|
-
getWorkspaceFileVersions,
|
|
24
|
-
} from './git.js';
|
|
25
|
-
import { importGitHubReview, pushGitHubReview } from './github-review.js';
|
|
26
|
-
import { readProjectConfig } from './config.js';
|
|
27
|
-
import { scanWorkspaces } from '../lib/remote-session/workspace-scanner.js';
|
|
28
|
-
import type { ReviewOperation, ReviewResult } from '../types/review.js';
|
|
29
|
-
import { matchesWorkspaceId } from '../utils/workspace-id.js';
|
|
30
|
-
|
|
31
|
-
type ScanWorkspacesFn = typeof scanWorkspaces;
|
|
32
|
-
|
|
33
|
-
async function resolveWorkspaceByName(
|
|
34
|
-
projectName: string,
|
|
35
|
-
workspaceName: string,
|
|
36
|
-
scan: ScanWorkspacesFn
|
|
37
|
-
): Promise<{ id: string; path: string; baseBranch: string }> {
|
|
38
|
-
const workspaces = await scan();
|
|
39
|
-
const workspace = workspaces.find(
|
|
40
|
-
(w) => w.projectName === projectName && matchesWorkspaceId(w, workspaceName)
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
if (!workspace) {
|
|
44
|
-
throw new Error(`Workspace not found: ${projectName}:${workspaceName}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let baseBranch = 'main';
|
|
48
|
-
try {
|
|
49
|
-
const projectConfig = readProjectConfig(projectName);
|
|
50
|
-
baseBranch = projectConfig.baseBranch ?? 'main';
|
|
51
|
-
} catch {
|
|
52
|
-
// Fall back to 'main'
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return { id: workspace.id, path: workspace.path, baseBranch };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Execute a ReviewOperation with direct filesystem access.
|
|
60
|
-
*
|
|
61
|
-
* @param operation - The operation to execute
|
|
62
|
-
* @param scan - scanWorkspaces implementation (injectable for testing)
|
|
63
|
-
*/
|
|
64
|
-
export async function executeLocalReviewOperation(
|
|
65
|
-
operation: ReviewOperation,
|
|
66
|
-
scan: ScanWorkspacesFn = scanWorkspaces
|
|
67
|
-
): Promise<ReviewResult> {
|
|
68
|
-
switch (operation.op) {
|
|
69
|
-
case 'get_threads': {
|
|
70
|
-
const workspace = await resolveWorkspaceByName(
|
|
71
|
-
operation.projectName,
|
|
72
|
-
operation.workspaceName,
|
|
73
|
-
scan
|
|
74
|
-
);
|
|
75
|
-
const threads = getThreads(workspace.path, workspace.id, workspace.baseBranch);
|
|
76
|
-
return { op: 'threads', threads };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
case 'create_thread': {
|
|
80
|
-
const workspace = await resolveWorkspaceByName(
|
|
81
|
-
operation.projectName,
|
|
82
|
-
operation.workspaceName,
|
|
83
|
-
scan
|
|
84
|
-
);
|
|
85
|
-
const thread = await createThread(
|
|
86
|
-
workspace.path,
|
|
87
|
-
workspace.id,
|
|
88
|
-
workspace.baseBranch,
|
|
89
|
-
operation.target,
|
|
90
|
-
operation.body,
|
|
91
|
-
operation.decision
|
|
92
|
-
);
|
|
93
|
-
return { op: 'thread_created', thread };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
case 'add_reply': {
|
|
97
|
-
const workspace = await resolveWorkspaceByName(
|
|
98
|
-
operation.projectName,
|
|
99
|
-
operation.workspaceName,
|
|
100
|
-
scan
|
|
101
|
-
);
|
|
102
|
-
const thread = addReply(
|
|
103
|
-
workspace.path,
|
|
104
|
-
workspace.id,
|
|
105
|
-
workspace.baseBranch,
|
|
106
|
-
operation.threadId,
|
|
107
|
-
operation.body
|
|
108
|
-
);
|
|
109
|
-
return { op: 'comment_added', thread };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
case 'update_thread': {
|
|
113
|
-
const workspace = await resolveWorkspaceByName(
|
|
114
|
-
operation.projectName,
|
|
115
|
-
operation.workspaceName,
|
|
116
|
-
scan
|
|
117
|
-
);
|
|
118
|
-
const thread = updateThread(
|
|
119
|
-
workspace.path,
|
|
120
|
-
workspace.id,
|
|
121
|
-
workspace.baseBranch,
|
|
122
|
-
operation.threadId,
|
|
123
|
-
{ resolved: operation.resolved, decision: operation.decision }
|
|
124
|
-
);
|
|
125
|
-
return { op: 'thread_updated', thread };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
case 'update_comment': {
|
|
129
|
-
const workspace = await resolveWorkspaceByName(
|
|
130
|
-
operation.projectName,
|
|
131
|
-
operation.workspaceName,
|
|
132
|
-
scan
|
|
133
|
-
);
|
|
134
|
-
const thread = updateComment(
|
|
135
|
-
workspace.path,
|
|
136
|
-
workspace.id,
|
|
137
|
-
workspace.baseBranch,
|
|
138
|
-
operation.threadId,
|
|
139
|
-
operation.commentId,
|
|
140
|
-
operation.body
|
|
141
|
-
);
|
|
142
|
-
return { op: 'comment_updated', thread };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
case 'delete_comment': {
|
|
146
|
-
const workspace = await resolveWorkspaceByName(
|
|
147
|
-
operation.projectName,
|
|
148
|
-
operation.workspaceName,
|
|
149
|
-
scan
|
|
150
|
-
);
|
|
151
|
-
const thread = deleteComment(
|
|
152
|
-
workspace.path,
|
|
153
|
-
workspace.id,
|
|
154
|
-
workspace.baseBranch,
|
|
155
|
-
operation.threadId,
|
|
156
|
-
operation.commentId
|
|
157
|
-
);
|
|
158
|
-
return {
|
|
159
|
-
op: 'comment_deleted',
|
|
160
|
-
thread,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
case 'get_diff': {
|
|
165
|
-
const workspace = await resolveWorkspaceByName(
|
|
166
|
-
operation.projectName,
|
|
167
|
-
operation.workspaceName,
|
|
168
|
-
scan
|
|
169
|
-
);
|
|
170
|
-
const diffResult = await getWorkspaceDiff(workspace.path, workspace.baseBranch);
|
|
171
|
-
return {
|
|
172
|
-
op: 'diff',
|
|
173
|
-
diff: diffResult.diff,
|
|
174
|
-
baseBranch: diffResult.baseBranch,
|
|
175
|
-
headBranch: diffResult.headBranch,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
case 'get_changed_files': {
|
|
180
|
-
const workspace = await resolveWorkspaceByName(
|
|
181
|
-
operation.projectName,
|
|
182
|
-
operation.workspaceName,
|
|
183
|
-
scan
|
|
184
|
-
);
|
|
185
|
-
const changed = await getWorkspaceChangedFiles(workspace.path, workspace.baseBranch);
|
|
186
|
-
return {
|
|
187
|
-
op: 'changed_files',
|
|
188
|
-
files: changed.files,
|
|
189
|
-
baseBranch: changed.baseBranch,
|
|
190
|
-
headBranch: changed.headBranch,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
case 'get_file_diff': {
|
|
195
|
-
const workspace = await resolveWorkspaceByName(
|
|
196
|
-
operation.projectName,
|
|
197
|
-
operation.workspaceName,
|
|
198
|
-
scan
|
|
199
|
-
);
|
|
200
|
-
const result = await getWorkspaceFileDiff(
|
|
201
|
-
workspace.path,
|
|
202
|
-
workspace.baseBranch,
|
|
203
|
-
operation.filePath,
|
|
204
|
-
operation.prevFilePath
|
|
205
|
-
);
|
|
206
|
-
return {
|
|
207
|
-
op: 'file_diff',
|
|
208
|
-
filePath: operation.filePath,
|
|
209
|
-
prevFilePath: operation.prevFilePath,
|
|
210
|
-
diff: result.diff,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
case 'get_file_versions': {
|
|
215
|
-
const workspace = await resolveWorkspaceByName(
|
|
216
|
-
operation.projectName,
|
|
217
|
-
operation.workspaceName,
|
|
218
|
-
scan
|
|
219
|
-
);
|
|
220
|
-
const { oldContents, newContents } = await getWorkspaceFileVersions(
|
|
221
|
-
workspace.path,
|
|
222
|
-
workspace.baseBranch,
|
|
223
|
-
operation.filePath,
|
|
224
|
-
operation.prevFilePath
|
|
225
|
-
);
|
|
226
|
-
return {
|
|
227
|
-
op: 'file_versions',
|
|
228
|
-
filePath: operation.filePath,
|
|
229
|
-
prevFilePath: operation.prevFilePath,
|
|
230
|
-
oldContents,
|
|
231
|
-
newContents,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
case 'get_file_context_range': {
|
|
236
|
-
const workspace = await resolveWorkspaceByName(
|
|
237
|
-
operation.projectName,
|
|
238
|
-
operation.workspaceName,
|
|
239
|
-
scan
|
|
240
|
-
);
|
|
241
|
-
const result = await getWorkspaceFileContextRange(
|
|
242
|
-
workspace.path,
|
|
243
|
-
workspace.baseBranch,
|
|
244
|
-
operation.filePath,
|
|
245
|
-
operation.prevFilePath,
|
|
246
|
-
{
|
|
247
|
-
oldStart: operation.oldStart,
|
|
248
|
-
oldEnd: operation.oldEnd,
|
|
249
|
-
newStart: operation.newStart,
|
|
250
|
-
newEnd: operation.newEnd,
|
|
251
|
-
}
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
op: 'file_context_range',
|
|
256
|
-
filePath: operation.filePath,
|
|
257
|
-
prevFilePath: operation.prevFilePath,
|
|
258
|
-
oldStart: result.oldStart,
|
|
259
|
-
oldLines: result.oldLines,
|
|
260
|
-
oldTotal: result.oldTotal,
|
|
261
|
-
newStart: result.newStart,
|
|
262
|
-
newLines: result.newLines,
|
|
263
|
-
newTotal: result.newTotal,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
case 'import_github': {
|
|
268
|
-
const workspace = await resolveWorkspaceByName(
|
|
269
|
-
operation.projectName,
|
|
270
|
-
operation.workspaceName,
|
|
271
|
-
scan
|
|
272
|
-
);
|
|
273
|
-
const prNumber =
|
|
274
|
-
operation.prNumber ?? (await detectPRNumber(workspace.path)) ?? null;
|
|
275
|
-
if (!prNumber) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
'Could not determine PR number. Pass prNumber explicitly or ensure the branch has an open PR.'
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
const { imported, threads } = await importGitHubReview(
|
|
281
|
-
workspace.path,
|
|
282
|
-
workspace.id,
|
|
283
|
-
workspace.baseBranch,
|
|
284
|
-
prNumber
|
|
285
|
-
);
|
|
286
|
-
return { op: 'github_imported', imported, threads };
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
case 'push_github': {
|
|
290
|
-
const workspace = await resolveWorkspaceByName(
|
|
291
|
-
operation.projectName,
|
|
292
|
-
operation.workspaceName,
|
|
293
|
-
scan
|
|
294
|
-
);
|
|
295
|
-
const prNumber =
|
|
296
|
-
operation.prNumber ?? (await detectPRNumber(workspace.path)) ?? null;
|
|
297
|
-
if (!prNumber) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
'Could not determine PR number. Pass prNumber explicitly or ensure the branch has an open PR.'
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
const { url } = await pushGitHubReview(
|
|
303
|
-
workspace.path,
|
|
304
|
-
workspace.id,
|
|
305
|
-
workspace.baseBranch,
|
|
306
|
-
prNumber
|
|
307
|
-
);
|
|
308
|
-
return { op: 'github_pushed', prNumber, url };
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
default: {
|
|
312
|
-
const unknown = operation as { op: string };
|
|
313
|
-
throw new Error(`Unknown review operation: ${unknown.op}`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
package/src/core/review.ts
DELETED
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core review operations
|
|
3
|
-
*
|
|
4
|
-
* Manages ReviewThread storage in <workspace>/.gitspace/review/<workspaceName>/notes.json
|
|
5
|
-
* Threads are namespaced by workspace name so that workspaces branched from each
|
|
6
|
-
* other don't share the same thread store.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'fs';
|
|
10
|
-
import { join, dirname } from 'path';
|
|
11
|
-
import { exec } from 'child_process';
|
|
12
|
-
import { promisify } from 'util';
|
|
13
|
-
import type {
|
|
14
|
-
ReviewSession,
|
|
15
|
-
ReviewThread,
|
|
16
|
-
ReviewComment,
|
|
17
|
-
ThreadTarget,
|
|
18
|
-
HunkDecision,
|
|
19
|
-
} from '../types/review.js';
|
|
20
|
-
import { generateId } from '../utils/id.js';
|
|
21
|
-
import { SpacesError } from '../types/errors.js';
|
|
22
|
-
import { logger } from '../utils/logger.js';
|
|
23
|
-
|
|
24
|
-
const execAsync = promisify(exec);
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Path Helpers
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns the directory where review notes are stored for this workspace.
|
|
32
|
-
* Path: <workspacePath>/.gitspace/review/<workspaceName>/
|
|
33
|
-
*/
|
|
34
|
-
function getReviewDir(workspacePath: string, workspaceName: string): string {
|
|
35
|
-
return join(workspacePath, '.gitspace', 'review', workspaceName);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Returns the path to the notes.json file for this workspace.
|
|
40
|
-
*/
|
|
41
|
-
function getNotesPath(workspacePath: string, workspaceName: string): string {
|
|
42
|
-
return join(getReviewDir(workspacePath, workspaceName), 'notes.json');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// ============================================================================
|
|
46
|
-
// Session Read / Write
|
|
47
|
-
// ============================================================================
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Read the review session from disk. Returns a fresh session if none exists.
|
|
51
|
-
*/
|
|
52
|
-
export function readReviewSession(
|
|
53
|
-
workspacePath: string,
|
|
54
|
-
workspaceName: string,
|
|
55
|
-
baseBranch: string
|
|
56
|
-
): ReviewSession {
|
|
57
|
-
const notesPath = getNotesPath(workspacePath, workspaceName);
|
|
58
|
-
|
|
59
|
-
if (!existsSync(notesPath)) {
|
|
60
|
-
return createEmptySession(workspaceName, baseBranch);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const raw = readFileSync(notesPath, 'utf-8');
|
|
65
|
-
const parsed = JSON.parse(raw) as ReviewSession;
|
|
66
|
-
return parsed;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
const code =
|
|
69
|
-
typeof error === 'object' && error !== null && 'code' in error
|
|
70
|
-
? String((error as { code?: unknown }).code)
|
|
71
|
-
: undefined;
|
|
72
|
-
|
|
73
|
-
if (code === 'ENOENT') {
|
|
74
|
-
return createEmptySession(workspaceName, baseBranch);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
78
|
-
|
|
79
|
-
if (error instanceof SyntaxError) {
|
|
80
|
-
logger.error(`Failed to parse review notes at ${notesPath}: ${message}`);
|
|
81
|
-
throw new SpacesError(`Corrupted review notes at ${notesPath}: ${message}`, 'USER_ERROR', 1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
logger.error(`Failed to read review notes at ${notesPath}: ${message}`);
|
|
85
|
-
if (error instanceof Error) {
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
throw new SpacesError(`Failed to read review notes at ${notesPath}: ${message}`, 'SYSTEM_ERROR', 2);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Write the review session to disk, creating directories as needed.
|
|
95
|
-
*/
|
|
96
|
-
export function writeReviewSession(
|
|
97
|
-
workspacePath: string,
|
|
98
|
-
workspaceName: string,
|
|
99
|
-
session: ReviewSession
|
|
100
|
-
): void {
|
|
101
|
-
const notesPath = getNotesPath(workspacePath, workspaceName);
|
|
102
|
-
const dir = dirname(notesPath);
|
|
103
|
-
|
|
104
|
-
if (!existsSync(dir)) {
|
|
105
|
-
mkdirSync(dir, { recursive: true });
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const updated: ReviewSession = {
|
|
109
|
-
...session,
|
|
110
|
-
updatedAt: new Date().toISOString(),
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
writeFileSync(notesPath, JSON.stringify(updated, null, 2), 'utf-8');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function createEmptySession(workspaceName: string, baseBranch: string): ReviewSession {
|
|
117
|
-
const now = new Date().toISOString();
|
|
118
|
-
return {
|
|
119
|
-
version: '1.0',
|
|
120
|
-
workspaceName,
|
|
121
|
-
baseBranch,
|
|
122
|
-
prNumber: null,
|
|
123
|
-
threads: [],
|
|
124
|
-
createdAt: now,
|
|
125
|
-
updatedAt: now,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ============================================================================
|
|
130
|
-
// Thread CRUD
|
|
131
|
-
// ============================================================================
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Get all threads for a workspace.
|
|
135
|
-
*/
|
|
136
|
-
export function getThreads(
|
|
137
|
-
workspacePath: string,
|
|
138
|
-
workspaceName: string,
|
|
139
|
-
baseBranch: string
|
|
140
|
-
): ReviewThread[] {
|
|
141
|
-
return readReviewSession(workspacePath, workspaceName, baseBranch).threads;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Create a new thread with an initial comment.
|
|
146
|
-
*/
|
|
147
|
-
export async function createThread(
|
|
148
|
-
workspacePath: string,
|
|
149
|
-
workspaceName: string,
|
|
150
|
-
baseBranch: string,
|
|
151
|
-
target: ThreadTarget,
|
|
152
|
-
body: string,
|
|
153
|
-
decision?: HunkDecision,
|
|
154
|
-
author = 'local'
|
|
155
|
-
): Promise<ReviewThread> {
|
|
156
|
-
// Ensure .gitignore decision is handled before reading/writing session state so
|
|
157
|
-
// concurrent CRUD writes cannot be overwritten after an async gap.
|
|
158
|
-
await ensureGitignore(workspacePath, workspaceName);
|
|
159
|
-
|
|
160
|
-
const session = readReviewSession(workspacePath, workspaceName, baseBranch);
|
|
161
|
-
const now = new Date().toISOString();
|
|
162
|
-
|
|
163
|
-
const threadId = generateId();
|
|
164
|
-
const commentId = generateId();
|
|
165
|
-
|
|
166
|
-
const comment: ReviewComment = {
|
|
167
|
-
id: commentId,
|
|
168
|
-
threadId,
|
|
169
|
-
body,
|
|
170
|
-
author,
|
|
171
|
-
createdAt: now,
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const thread: ReviewThread = {
|
|
175
|
-
id: threadId,
|
|
176
|
-
target,
|
|
177
|
-
decision: target.kind === 'hunk' ? (decision ?? 'pending') : undefined,
|
|
178
|
-
resolved: false,
|
|
179
|
-
comments: [comment],
|
|
180
|
-
createdAt: now,
|
|
181
|
-
updatedAt: now,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
session.threads.push(thread);
|
|
185
|
-
|
|
186
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
187
|
-
return thread;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Add a reply to an existing thread.
|
|
192
|
-
*/
|
|
193
|
-
export function addReply(
|
|
194
|
-
workspacePath: string,
|
|
195
|
-
workspaceName: string,
|
|
196
|
-
baseBranch: string,
|
|
197
|
-
threadId: string,
|
|
198
|
-
body: string,
|
|
199
|
-
author = 'local'
|
|
200
|
-
): ReviewThread {
|
|
201
|
-
const session = readReviewSession(workspacePath, workspaceName, baseBranch);
|
|
202
|
-
const thread = session.threads.find(t => t.id === threadId);
|
|
203
|
-
|
|
204
|
-
if (!thread) {
|
|
205
|
-
throw new SpacesError(`Thread not found: ${threadId}`, 'USER_ERROR', 1);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const now = new Date().toISOString();
|
|
209
|
-
const comment: ReviewComment = {
|
|
210
|
-
id: generateId(),
|
|
211
|
-
threadId,
|
|
212
|
-
body,
|
|
213
|
-
author,
|
|
214
|
-
createdAt: now,
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
thread.comments.push(comment);
|
|
218
|
-
thread.updatedAt = now;
|
|
219
|
-
|
|
220
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
221
|
-
return thread;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Update a thread's resolved status or hunk decision.
|
|
226
|
-
*/
|
|
227
|
-
export function updateThread(
|
|
228
|
-
workspacePath: string,
|
|
229
|
-
workspaceName: string,
|
|
230
|
-
baseBranch: string,
|
|
231
|
-
threadId: string,
|
|
232
|
-
updates: { resolved?: boolean; decision?: HunkDecision }
|
|
233
|
-
): ReviewThread {
|
|
234
|
-
const session = readReviewSession(workspacePath, workspaceName, baseBranch);
|
|
235
|
-
const thread = session.threads.find(t => t.id === threadId);
|
|
236
|
-
|
|
237
|
-
if (!thread) {
|
|
238
|
-
throw new SpacesError(`Thread not found: ${threadId}`, 'USER_ERROR', 1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (updates.resolved !== undefined) {
|
|
242
|
-
thread.resolved = updates.resolved;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (updates.decision !== undefined && thread.target.kind === 'hunk') {
|
|
246
|
-
thread.decision = updates.decision;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
thread.updatedAt = new Date().toISOString();
|
|
250
|
-
|
|
251
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
252
|
-
return thread;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Update the body of a specific comment in a thread.
|
|
257
|
-
*/
|
|
258
|
-
export function updateComment(
|
|
259
|
-
workspacePath: string,
|
|
260
|
-
workspaceName: string,
|
|
261
|
-
baseBranch: string,
|
|
262
|
-
threadId: string,
|
|
263
|
-
commentId: string,
|
|
264
|
-
body: string
|
|
265
|
-
): ReviewThread {
|
|
266
|
-
const session = readReviewSession(workspacePath, workspaceName, baseBranch);
|
|
267
|
-
const thread = session.threads.find(t => t.id === threadId);
|
|
268
|
-
|
|
269
|
-
if (!thread) {
|
|
270
|
-
throw new SpacesError(`Thread not found: ${threadId}`, 'USER_ERROR', 1);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const comment = thread.comments.find(c => c.id === commentId);
|
|
274
|
-
if (!comment) {
|
|
275
|
-
throw new SpacesError(`Comment not found: ${commentId}`, 'USER_ERROR', 1);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
comment.body = body;
|
|
279
|
-
thread.updatedAt = new Date().toISOString();
|
|
280
|
-
|
|
281
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
282
|
-
return thread;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Delete a specific comment from a thread.
|
|
287
|
-
* If it's the only comment in the thread, the thread itself is deleted.
|
|
288
|
-
* Returns the updated thread snapshot. If the last comment is deleted,
|
|
289
|
-
* the returned thread keeps its original target metadata and has comments: [].
|
|
290
|
-
*/
|
|
291
|
-
export function deleteComment(
|
|
292
|
-
workspacePath: string,
|
|
293
|
-
workspaceName: string,
|
|
294
|
-
baseBranch: string,
|
|
295
|
-
threadId: string,
|
|
296
|
-
commentId: string
|
|
297
|
-
): ReviewThread {
|
|
298
|
-
const session = readReviewSession(workspacePath, workspaceName, baseBranch);
|
|
299
|
-
const threadIndex = session.threads.findIndex(t => t.id === threadId);
|
|
300
|
-
|
|
301
|
-
if (threadIndex === -1) {
|
|
302
|
-
throw new SpacesError(`Thread not found: ${threadId}`, 'USER_ERROR', 1);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const thread = session.threads[threadIndex];
|
|
306
|
-
const commentIndex = thread.comments.findIndex(c => c.id === commentId);
|
|
307
|
-
|
|
308
|
-
if (commentIndex === -1) {
|
|
309
|
-
throw new SpacesError(`Comment not found: ${commentId}`, 'USER_ERROR', 1);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
thread.comments.splice(commentIndex, 1);
|
|
313
|
-
thread.updatedAt = new Date().toISOString();
|
|
314
|
-
|
|
315
|
-
// If no comments remain, delete the whole thread
|
|
316
|
-
if (thread.comments.length === 0) {
|
|
317
|
-
session.threads.splice(threadIndex, 1);
|
|
318
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
319
|
-
return thread;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
writeReviewSession(workspacePath, workspaceName, session);
|
|
323
|
-
return thread;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// ============================================================================
|
|
327
|
-
// .gitignore Management
|
|
328
|
-
// ============================================================================
|
|
329
|
-
|
|
330
|
-
const GITIGNORE_ENTRY = '.gitspace/review/';
|
|
331
|
-
const GITIGNORE_MARKER = '# gssh review — workspace review notes';
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Ensure that .gitspace/review/ is handled appropriately in .gitignore.
|
|
335
|
-
* On first note creation, prompts the user whether to gitignore or commit.
|
|
336
|
-
* Remembers the choice in a local marker file so it's not asked again.
|
|
337
|
-
*/
|
|
338
|
-
async function ensureGitignore(workspacePath: string, workspaceName: string): Promise<void> {
|
|
339
|
-
const reviewDir = getReviewDir(workspacePath, workspaceName);
|
|
340
|
-
const markerPath = join(reviewDir, '.gitignore-decided');
|
|
341
|
-
|
|
342
|
-
// Already decided — skip
|
|
343
|
-
if (existsSync(markerPath)) return;
|
|
344
|
-
|
|
345
|
-
const gitignorePath = join(workspacePath, '.gitignore');
|
|
346
|
-
const alreadyIgnored =
|
|
347
|
-
existsSync(gitignorePath) &&
|
|
348
|
-
readFileSync(gitignorePath, 'utf-8').includes(GITIGNORE_ENTRY);
|
|
349
|
-
|
|
350
|
-
if (alreadyIgnored) {
|
|
351
|
-
// Already ignored — write marker and return
|
|
352
|
-
mkdirSync(dirname(markerPath), { recursive: true });
|
|
353
|
-
writeFileSync(markerPath, 'gitignored\n', 'utf-8');
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
let keepPrivate = true;
|
|
358
|
-
|
|
359
|
-
// Only prompt when running interactively (stdin is a TTY).
|
|
360
|
-
// When called from the serve daemon there is no TTY, so we silently
|
|
361
|
-
// default to keeping notes private.
|
|
362
|
-
const isTTY = Boolean(process.stdin.isTTY);
|
|
363
|
-
if (isTTY) {
|
|
364
|
-
try {
|
|
365
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
366
|
-
keepPrivate = await confirm({
|
|
367
|
-
message:
|
|
368
|
-
'Review notes found. Keep them private (add to .gitignore) or share with the team (commit alongside branch)?',
|
|
369
|
-
default: true,
|
|
370
|
-
});
|
|
371
|
-
} catch {
|
|
372
|
-
// Prompt cancelled or non-interactive — default to private
|
|
373
|
-
keepPrivate = true;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (keepPrivate) {
|
|
378
|
-
appendFileSync(
|
|
379
|
-
gitignorePath,
|
|
380
|
-
`\n${GITIGNORE_MARKER}\n${GITIGNORE_ENTRY}\n`,
|
|
381
|
-
'utf-8'
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
mkdirSync(dirname(markerPath), { recursive: true });
|
|
386
|
-
writeFileSync(markerPath, keepPrivate ? 'gitignored\n' : 'committed\n', 'utf-8');
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// ============================================================================
|
|
390
|
-
// Workspace Detection
|
|
391
|
-
// ============================================================================
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Detect the PR number associated with the current workspace branch
|
|
395
|
-
* by using `gh pr view`.
|
|
396
|
-
*/
|
|
397
|
-
export async function detectPRNumber(workspacePath: string): Promise<number | null> {
|
|
398
|
-
try {
|
|
399
|
-
const { stdout } = await execAsync('gh pr view --json number --jq .number', {
|
|
400
|
-
cwd: workspacePath,
|
|
401
|
-
});
|
|
402
|
-
const num = parseInt(stdout.trim(), 10);
|
|
403
|
-
return isNaN(num) ? null : num;
|
|
404
|
-
} catch {
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
}
|