aicodeman 1.41.0 → 1.42.0
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/README.md +101 -21
- package/dist/attachment-registry.d.ts +2 -0
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +14 -3
- package/dist/attachment-registry.js.map +1 -1
- package/dist/cli-agent.d.ts +2 -0
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +13 -9
- package/dist/cli-agent.js.map +1 -1
- package/dist/cli.d.ts +25 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +97 -11
- package/dist/cli.js.map +1 -1
- package/dist/config/buffer-limits.d.ts +9 -0
- package/dist/config/buffer-limits.d.ts.map +1 -1
- package/dist/config/buffer-limits.js +9 -0
- package/dist/config/buffer-limits.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +10 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +26 -0
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +89 -6
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +61 -0
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/folder-listing.d.ts +94 -0
- package/dist/folder-listing.d.ts.map +1 -0
- package/dist/folder-listing.js +241 -0
- package/dist/folder-listing.js.map +1 -0
- package/dist/git-worktrees.d.ts +292 -0
- package/dist/git-worktrees.d.ts.map +1 -0
- package/dist/git-worktrees.js +791 -0
- package/dist/git-worktrees.js.map +1 -0
- package/dist/hooks-config.d.ts +36 -16
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +45 -20
- package/dist/hooks-config.js.map +1 -1
- package/dist/html-view-capabilities.d.ts +54 -0
- package/dist/html-view-capabilities.d.ts.map +1 -0
- package/dist/html-view-capabilities.js +103 -0
- package/dist/html-view-capabilities.js.map +1 -0
- package/dist/mcp-server.d.ts +114 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +918 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-sync.d.ts +5 -0
- package/dist/mcp-sync.d.ts.map +1 -1
- package/dist/mcp-sync.js +51 -5
- package/dist/mcp-sync.js.map +1 -1
- package/dist/mux-interface.d.ts +36 -1
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/remote-files.d.ts +37 -0
- package/dist/remote-files.d.ts.map +1 -1
- package/dist/remote-files.js +103 -0
- package/dist/remote-files.js.map +1 -1
- package/dist/remote-pane-history.d.ts +103 -0
- package/dist/remote-pane-history.d.ts.map +1 -0
- package/dist/remote-pane-history.js +258 -0
- package/dist/remote-pane-history.js.map +1 -0
- package/dist/remote-ssh-limiter.d.ts +16 -3
- package/dist/remote-ssh-limiter.d.ts.map +1 -1
- package/dist/remote-ssh-limiter.js +37 -6
- package/dist/remote-ssh-limiter.js.map +1 -1
- package/dist/services/unified-session-service.d.ts +8 -0
- package/dist/services/unified-session-service.d.ts.map +1 -1
- package/dist/services/unified-session-service.js +21 -0
- package/dist/services/unified-session-service.js.map +1 -1
- package/dist/session-activity.d.ts +51 -0
- package/dist/session-activity.d.ts.map +1 -1
- package/dist/session-activity.js +62 -0
- package/dist/session-activity.js.map +1 -1
- package/dist/session-submit-verifier.js +1 -1
- package/dist/session.d.ts +90 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +247 -9
- package/dist/session.js.map +1 -1
- package/dist/tab-layout-legacy-order.d.ts.map +1 -1
- package/dist/tab-layout-legacy-order.js +72 -13
- package/dist/tab-layout-legacy-order.js.map +1 -1
- package/dist/tab-layout-service.d.ts +33 -2
- package/dist/tab-layout-service.d.ts.map +1 -1
- package/dist/tab-layout-service.js +102 -7
- package/dist/tab-layout-service.js.map +1 -1
- package/dist/tab-layout.d.ts +9 -0
- package/dist/tab-layout.d.ts.map +1 -1
- package/dist/tab-layout.js +15 -2
- package/dist/tab-layout.js.map +1 -1
- package/dist/tmux-manager.d.ts +130 -7
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +263 -36
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/session.d.ts +53 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/usage-telemetry.d.ts +44 -7
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +72 -7
- package/dist/usage-telemetry.js.map +1 -1
- package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
- package/dist/utils/deepseek-cli-resolver.js +1 -1
- package/dist/utils/deepseek-cli-resolver.js.map +1 -1
- package/dist/web/approval-inbox.d.ts +24 -2
- package/dist/web/approval-inbox.d.ts.map +1 -1
- package/dist/web/approval-inbox.js +8 -2
- package/dist/web/approval-inbox.js.map +1 -1
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +33 -0
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/ports/session-port.d.ts +8 -1
- package/dist/web/ports/session-port.d.ts.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.f04bb2a5.js +43 -0
- package/dist/web/public/app.f04bb2a5.js.br +0 -0
- package/dist/web/public/app.f04bb2a5.js.gz +0 -0
- package/dist/web/public/approvals-ui.js +4 -0
- package/dist/web/public/approvals-ui.js.br +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.bc9c1f63.js → constants.12b0c247.js} +395 -7
- package/dist/web/public/constants.12b0c247.js.br +0 -0
- package/dist/web/public/constants.12b0c247.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/gesture/gesture-codeman.js +112 -6
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.06659676.js +2 -0
- package/dist/web/public/i18n.06659676.js.br +0 -0
- package/dist/web/public/i18n.06659676.js.gz +0 -0
- package/dist/web/public/image-input.fcdcb46c.js +462 -0
- package/dist/web/public/image-input.fcdcb46c.js.br +0 -0
- package/dist/web/public/image-input.fcdcb46c.js.gz +0 -0
- package/dist/web/public/index.html +169 -24
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/{keyboard-accessory.c1e7c7b7.js → keyboard-accessory.b42f539b.js} +124 -44
- package/dist/web/public/keyboard-accessory.b42f539b.js.br +0 -0
- package/dist/web/public/keyboard-accessory.b42f539b.js.gz +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js +11 -2
- package/dist/web/public/mobile-overview.js.br +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.2dff1580.css +1 -0
- package/dist/web/public/mobile.2dff1580.css.br +0 -0
- package/dist/web/public/mobile.2dff1580.css.gz +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.71846fcb.js → panels-ui.73ec0137.js} +87 -87
- package/dist/web/public/panels-ui.73ec0137.js.br +0 -0
- package/dist/web/public/panels-ui.73ec0137.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/{respawn-ui.ff0dae4c.js → respawn-ui.d7adcf6a.js} +1 -1
- package/dist/web/public/respawn-ui.d7adcf6a.js.br +0 -0
- package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.d52ff6aa.js +100 -0
- package/dist/web/public/session-ui.d52ff6aa.js.br +0 -0
- package/dist/web/public/session-ui.d52ff6aa.js.gz +0 -0
- package/dist/web/public/settings-ui.3a22bd2a.js +97 -0
- package/dist/web/public/settings-ui.3a22bd2a.js.br +0 -0
- package/dist/web/public/settings-ui.3a22bd2a.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js +11 -0
- package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/{spreadsheet-preview.af82fa2f.js → spreadsheet-preview.75c751de.js} +1 -1
- package/dist/web/public/spreadsheet-preview.75c751de.js.br +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js +110 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.ba6a9b84.css +1 -0
- package/dist/web/public/styles.ba6a9b84.css.br +0 -0
- package/dist/web/public/styles.ba6a9b84.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +6 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js +1 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.br +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.a5867793.js → tab-rail-resize.7157e0bd.js} +1 -1
- package/dist/web/public/tab-rail-resize.7157e0bd.js.br +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +45 -24
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.0a7ccc29.js +2 -0
- package/dist/web/public/terminal-ui.0a7ccc29.js.br +0 -0
- package/dist/web/public/terminal-ui.0a7ccc29.js.gz +0 -0
- package/dist/web/public/tile-grid.js +439 -18
- package/dist/web/public/tile-grid.js.br +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js +12 -0
- package/dist/web/public/webview-tabs.js.br +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/public/whats-new.json +13 -0
- package/dist/web/route-helpers.d.ts +8 -0
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +8 -0
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/admin-routes.d.ts.map +1 -1
- package/dist/web/routes/admin-routes.js +4 -1
- package/dist/web/routes/admin-routes.js.map +1 -1
- package/dist/web/routes/approval-routes.d.ts.map +1 -1
- package/dist/web/routes/approval-routes.js +12 -10
- package/dist/web/routes/approval-routes.js.map +1 -1
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +10 -3
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts +46 -1
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +214 -27
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
- package/dist/web/routes/hook-event-routes.js +8 -1
- package/dist/web/routes/hook-event-routes.js.map +1 -1
- package/dist/web/routes/html-view-routes.d.ts +60 -0
- package/dist/web/routes/html-view-routes.d.ts.map +1 -0
- package/dist/web/routes/html-view-routes.js +288 -0
- package/dist/web/routes/html-view-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +2 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/ralph-routes.d.ts.map +1 -1
- package/dist/web/routes/ralph-routes.js +19 -14
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/reboot-restore-routes.js +2 -2
- package/dist/web/routes/reboot-restore-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts +8 -5
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +462 -109
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +24 -6
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts +9 -0
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +91 -12
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/worktree-routes.d.ts +52 -0
- package/dist/web/routes/worktree-routes.d.ts.map +1 -0
- package/dist/web/routes/worktree-routes.js +220 -0
- package/dist/web/routes/worktree-routes.js.map +1 -0
- package/dist/web/schemas.d.ts +45 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +105 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/screen-blocked.d.ts +80 -0
- package/dist/web/screen-blocked.d.ts.map +1 -0
- package/dist/web/screen-blocked.js +106 -0
- package/dist/web/screen-blocked.js.map +1 -0
- package/dist/web/server.d.ts +6 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +73 -14
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +1 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +20 -0
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/session-wait-registry.d.ts +27 -9
- package/dist/web/session-wait-registry.d.ts.map +1 -1
- package/dist/web/session-wait-registry.js +36 -13
- package/dist/web/session-wait-registry.js.map +1 -1
- package/dist/web/sse-events.d.ts +12 -4
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +12 -4
- package/dist/web/sse-events.js.map +1 -1
- package/package.json +3 -2
- package/skills/codeman/SKILL.md +47 -27
- package/skills/codeman/preamble.sh +14 -11
- package/skills/codeman/reference/endpoints.md +1 -1
- package/skills/codeman/reference/recipes.md +12 -9
- package/skills/codeman/reference/verbs.md +3 -1
- package/dist/web/public/app.6f8552e1.js +0 -43
- package/dist/web/public/app.6f8552e1.js.br +0 -0
- package/dist/web/public/app.6f8552e1.js.gz +0 -0
- package/dist/web/public/constants.bc9c1f63.js.br +0 -0
- package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
- package/dist/web/public/i18n.a9840ea2.js +0 -2
- package/dist/web/public/i18n.a9840ea2.js.br +0 -0
- package/dist/web/public/i18n.a9840ea2.js.gz +0 -0
- package/dist/web/public/image-input.e00471c6.js +0 -283
- package/dist/web/public/image-input.e00471c6.js.br +0 -0
- package/dist/web/public/image-input.e00471c6.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.br +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
- package/dist/web/public/mobile.111b187d.css +0 -1
- package/dist/web/public/mobile.111b187d.css.br +0 -0
- package/dist/web/public/mobile.111b187d.css.gz +0 -0
- package/dist/web/public/panels-ui.71846fcb.js.br +0 -0
- package/dist/web/public/panels-ui.71846fcb.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.br +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/session-ui.f731602c.js +0 -100
- package/dist/web/public/session-ui.f731602c.js.br +0 -0
- package/dist/web/public/session-ui.f731602c.js.gz +0 -0
- package/dist/web/public/settings-ui.4e33390f.js +0 -97
- package/dist/web/public/settings-ui.4e33390f.js.br +0 -0
- package/dist/web/public/settings-ui.4e33390f.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
- package/dist/web/public/styles.81a72906.css +0 -1
- package/dist/web/public/styles.81a72906.css.br +0 -0
- package/dist/web/public/styles.81a72906.css.gz +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js +0 -1
- package/dist/web/public/tab-layout-browser.065fcba4.js.br +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
- package/dist/web/public/tab-rail-resize.a5867793.js.gz +0 -0
- package/dist/web/public/terminal-ui.b7267005.js +0 -2
- package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
- package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Folder listings for the file-preview folder grid.
|
|
3
|
+
*
|
|
4
|
+
* A folder the user opens (a clicked folder path, or "Folder" in the file
|
|
5
|
+
* preview) is resolved, checked against the same attachment guard a single file
|
|
6
|
+
* goes through, and GRANTED an id. The grid then asks for files by
|
|
7
|
+
* `folderId + file name` only, so browser requests never carry arbitrary paths —
|
|
8
|
+
* the rule the attachment routes follow for single files.
|
|
9
|
+
*
|
|
10
|
+
* Grants live in their own small registry, not in the attachment registry: that one
|
|
11
|
+
* is capped at 200 records per session, and a folder of 300 renders would evict the
|
|
12
|
+
* session's real attachments.
|
|
13
|
+
*
|
|
14
|
+
* Listings contain non-hidden subfolders and files whose type the preview supports
|
|
15
|
+
* (`isSupportedAttachmentExtension`), newest first, capped at
|
|
16
|
+
* {@link MAX_FOLDER_ENTRIES}. Local folders are read with `fs`; a remote (SSH) case's
|
|
17
|
+
* folder is listed on its host in one ssh round trip (`remoteListFolder`).
|
|
18
|
+
*/
|
|
19
|
+
import { randomUUID } from 'node:crypto';
|
|
20
|
+
import fs from 'node:fs/promises';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import { extname, isAbsolute, join, posix } from 'node:path';
|
|
23
|
+
import { getAttachmentType, isSupportedAttachmentExtension, supportedAttachmentExtensions, } from './attachment-registry.js';
|
|
24
|
+
import { isBlockedAttachmentPath, isUnderTree, loadAttachmentGuardConfig } from './config/attachment-guard.js';
|
|
25
|
+
import { isMultiUserMode, userSpacePath } from './config/multiuser.js';
|
|
26
|
+
import { remoteListFolder, RemoteFileAccessError } from './remote-files.js';
|
|
27
|
+
import { describeUnknownPath, probePathKind } from './utils/bounded-path-probe.js';
|
|
28
|
+
import { validateSessionFilePath } from './web/route-helpers.js';
|
|
29
|
+
/** Most entries (files + subfolders) one listing returns. */
|
|
30
|
+
export const MAX_FOLDER_ENTRIES = 1000;
|
|
31
|
+
/** Folder grants kept per session; the oldest is dropped beyond this. */
|
|
32
|
+
const MAX_FOLDER_GRANTS_PER_SESSION = 32;
|
|
33
|
+
export class FolderListingError extends Error {
|
|
34
|
+
statusCode;
|
|
35
|
+
constructor(message, statusCode = 400) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.statusCode = statusCode;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
class FolderGrants {
|
|
41
|
+
bySession = new Map();
|
|
42
|
+
grant(sessionId, dir) {
|
|
43
|
+
let grants = this.bySession.get(sessionId);
|
|
44
|
+
if (!grants) {
|
|
45
|
+
grants = new Map();
|
|
46
|
+
this.bySession.set(sessionId, grants);
|
|
47
|
+
}
|
|
48
|
+
for (const existing of grants.values()) {
|
|
49
|
+
if (existing.dir === dir) {
|
|
50
|
+
// Refresh its position so an often-used folder is the last to be dropped.
|
|
51
|
+
grants.delete(existing.folderId);
|
|
52
|
+
grants.set(existing.folderId, existing);
|
|
53
|
+
return existing.folderId;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const folderId = `fld_${randomUUID()}`;
|
|
57
|
+
grants.set(folderId, { folderId, dir });
|
|
58
|
+
while (grants.size > MAX_FOLDER_GRANTS_PER_SESSION) {
|
|
59
|
+
const oldest = grants.keys().next().value;
|
|
60
|
+
if (oldest === undefined)
|
|
61
|
+
break;
|
|
62
|
+
grants.delete(oldest);
|
|
63
|
+
}
|
|
64
|
+
return folderId;
|
|
65
|
+
}
|
|
66
|
+
get(sessionId, folderId) {
|
|
67
|
+
return this.bySession.get(sessionId)?.get(folderId);
|
|
68
|
+
}
|
|
69
|
+
clearSession(sessionId) {
|
|
70
|
+
this.bySession.delete(sessionId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export const folderGrants = new FolderGrants();
|
|
74
|
+
/**
|
|
75
|
+
* A file name inside a granted folder, or null when it is not one: no path
|
|
76
|
+
* separator, not hidden (which also rules out `.`/`..`; the listing leaves hidden
|
|
77
|
+
* entries out, so the routes do too), no NUL, and a type the preview supports.
|
|
78
|
+
*/
|
|
79
|
+
export function validFolderFileName(name) {
|
|
80
|
+
if (typeof name !== 'string' || !name || name.length > 255)
|
|
81
|
+
return null;
|
|
82
|
+
if (name.startsWith('.') || name.includes('/') || name.includes('\\') || name.includes('\0'))
|
|
83
|
+
return null;
|
|
84
|
+
const extension = extname(name).toLowerCase().replace(/^\./, '');
|
|
85
|
+
return isSupportedAttachmentExtension(extension) ? name : null;
|
|
86
|
+
}
|
|
87
|
+
/** Where a granted folder's file lives (on the session's host). */
|
|
88
|
+
export function folderFilePath(dir, name) {
|
|
89
|
+
return posix.join(dir, name);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Multi-user rule for folder grants, the same one the filesystem path picker follows:
|
|
93
|
+
* a regular user reaches only their own space (`userSpacePath`) or the session's
|
|
94
|
+
* `workingDir`. User spaces live inside `homedir()`, so without it a user could list
|
|
95
|
+
* and read another user's space. Always true in single-user mode and for admins; a
|
|
96
|
+
* remote session's folders are on its host, where only the `workingDir` applies.
|
|
97
|
+
*
|
|
98
|
+
* @param path Already resolved (realpath on the session's host).
|
|
99
|
+
*/
|
|
100
|
+
export function folderAllowedForUser(path, user, scope) {
|
|
101
|
+
if (!isMultiUserMode())
|
|
102
|
+
return true;
|
|
103
|
+
if (!user)
|
|
104
|
+
return false;
|
|
105
|
+
if (user.role === 'admin')
|
|
106
|
+
return true;
|
|
107
|
+
if (scope.remote)
|
|
108
|
+
return !!scope.workingDir && isUnderTree(path, scope.workingDir);
|
|
109
|
+
return [userSpacePath(user.username), scope.workingDir].some((root) => !!root && !!validateSessionFilePath(root, path));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve, guard, list and grant a folder for `sessionId`.
|
|
113
|
+
*
|
|
114
|
+
* A local folder is probed bounded first (`probePathKind`): one on a network mount
|
|
115
|
+
* that stopped answering must not freeze the server, and "did not answer" is never
|
|
116
|
+
* reported as "not found". The guard runs on the resolved folder before it is read.
|
|
117
|
+
*
|
|
118
|
+
* @param requestedPath Absolute, or `~/…` (the home of the session's host).
|
|
119
|
+
* @throws FolderListingError 400 not absolute / not a folder, 403 blocked or outside the
|
|
120
|
+
* user's space, 404 missing, 502 remote unreachable, 503 local folder not answering.
|
|
121
|
+
*/
|
|
122
|
+
export async function listFolder(sessionId, requestedPath, options = {}) {
|
|
123
|
+
const homeRelative = typeof requestedPath === 'string' && (requestedPath === '~' || requestedPath.startsWith('~/'));
|
|
124
|
+
if (!requestedPath || typeof requestedPath !== 'string' || (!isAbsolute(requestedPath) && !homeRelative)) {
|
|
125
|
+
throw new FolderListingError('Folder path must be absolute');
|
|
126
|
+
}
|
|
127
|
+
const guard = await loadAttachmentGuardConfig();
|
|
128
|
+
const workingDir = options.sessionWorkingDir;
|
|
129
|
+
const assertAllowed = (dir) => {
|
|
130
|
+
const confined = !guard.confineToWorkspace
|
|
131
|
+
? true
|
|
132
|
+
: options.remote
|
|
133
|
+
? !!workingDir && isUnderTree(dir, workingDir)
|
|
134
|
+
: !!workingDir && !!validateSessionFilePath(workingDir, dir);
|
|
135
|
+
if (!confined || isBlockedAttachmentPath(dir, guard.blockedTrees)) {
|
|
136
|
+
throw new FolderListingError('Access to this folder is blocked', 403);
|
|
137
|
+
}
|
|
138
|
+
if (!folderAllowedForUser(dir, options.user, { workingDir, remote: options.remote })) {
|
|
139
|
+
throw new FolderListingError('Folder is outside your space', 403);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
let dir;
|
|
143
|
+
let entries;
|
|
144
|
+
let truncated = false;
|
|
145
|
+
if (options.remote) {
|
|
146
|
+
const remotePath = requestedPath === '~' ? '~/' : requestedPath;
|
|
147
|
+
let listing;
|
|
148
|
+
try {
|
|
149
|
+
listing = await remoteListFolder(options.remote, remotePath, supportedAttachmentExtensions(), MAX_FOLDER_ENTRIES);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
throw new FolderListingError(err instanceof RemoteFileAccessError ? err.message : 'remote host unreachable', 502);
|
|
153
|
+
}
|
|
154
|
+
if (!listing)
|
|
155
|
+
throw new FolderListingError('Folder not found', 404);
|
|
156
|
+
if (listing === 'not-a-folder')
|
|
157
|
+
throw new FolderListingError('Not a folder');
|
|
158
|
+
({ dir, entries, truncated } = listing);
|
|
159
|
+
// One ssh call lists and resolves together, so the guard can only run after it;
|
|
160
|
+
// nothing of a refused listing leaves this function.
|
|
161
|
+
assertAllowed(dir);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const expanded = homeRelative ? join(homedir(), requestedPath.slice(2)) : requestedPath;
|
|
165
|
+
const kind = await probePathKind(expanded, { pastCap: true });
|
|
166
|
+
if (kind === 'unknown') {
|
|
167
|
+
throw new FolderListingError(describeUnknownPath('Folder', expanded, { pastCap: true }), 503);
|
|
168
|
+
}
|
|
169
|
+
if (kind === 'absent')
|
|
170
|
+
throw new FolderListingError('Folder not found', 404);
|
|
171
|
+
if (kind !== 'directory')
|
|
172
|
+
throw new FolderListingError('Not a folder');
|
|
173
|
+
try {
|
|
174
|
+
dir = await fs.realpath(expanded);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
throw new FolderListingError('Folder not found', 404);
|
|
178
|
+
}
|
|
179
|
+
assertAllowed(dir);
|
|
180
|
+
({ entries, truncated } = await listLocalFolder(dir));
|
|
181
|
+
}
|
|
182
|
+
const files = [];
|
|
183
|
+
const folders = [];
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
if (entry.name.startsWith('.'))
|
|
186
|
+
continue;
|
|
187
|
+
// A secret that happens to sit in an open folder stays out of the grid too.
|
|
188
|
+
if (isBlockedAttachmentPath(folderFilePath(dir, entry.name), guard.blockedTrees))
|
|
189
|
+
continue;
|
|
190
|
+
if (entry.kind === 'directory') {
|
|
191
|
+
folders.push(entry.name);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const extension = extname(entry.name).toLowerCase().replace(/^\./, '');
|
|
195
|
+
if (!isSupportedAttachmentExtension(extension))
|
|
196
|
+
continue;
|
|
197
|
+
files.push({
|
|
198
|
+
name: entry.name,
|
|
199
|
+
extension,
|
|
200
|
+
attachmentType: getAttachmentType(extension),
|
|
201
|
+
size: entry.size,
|
|
202
|
+
mtimeMs: entry.mtimeMs,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
files.sort((a, b) => b.mtimeMs - a.mtimeMs || a.name.localeCompare(b.name));
|
|
206
|
+
folders.sort((a, b) => a.localeCompare(b));
|
|
207
|
+
return { folderId: folderGrants.grant(sessionId, dir), path: dir, files, folders, truncated };
|
|
208
|
+
}
|
|
209
|
+
/** Local counterpart of the remote listing script: subfolders + supported files, stat'ed through symlinks. */
|
|
210
|
+
async function listLocalFolder(dir) {
|
|
211
|
+
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
212
|
+
const entries = [];
|
|
213
|
+
let truncated = false;
|
|
214
|
+
for (const dirent of dirents) {
|
|
215
|
+
if (dirent.name.startsWith('.'))
|
|
216
|
+
continue;
|
|
217
|
+
if (entries.length >= MAX_FOLDER_ENTRIES) {
|
|
218
|
+
truncated = true;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
const extension = extname(dirent.name).toLowerCase().replace(/^\./, '');
|
|
222
|
+
const maybeFile = isSupportedAttachmentExtension(extension);
|
|
223
|
+
if (!dirent.isDirectory() && !maybeFile && !dirent.isSymbolicLink())
|
|
224
|
+
continue;
|
|
225
|
+
let stat;
|
|
226
|
+
try {
|
|
227
|
+
stat = await fs.stat(join(dir, dirent.name));
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
continue; // dangling symlink, or gone since readdir
|
|
231
|
+
}
|
|
232
|
+
if (stat.isDirectory()) {
|
|
233
|
+
entries.push({ kind: 'directory', name: dirent.name, size: 0, mtimeMs: 0 });
|
|
234
|
+
}
|
|
235
|
+
else if (stat.isFile() && maybeFile) {
|
|
236
|
+
entries.push({ kind: 'file', name: dirent.name, size: stat.size, mtimeMs: stat.mtimeMs });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { entries, truncated };
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=folder-listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-listing.js","sourceRoot":"","sources":["../src/folder-listing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Git worktrees for a LOCAL case: list, create and remove the worktrees a
|
|
3
|
+
* repository-backed case keeps under `<repo>/.claude/worktrees/<name>`, so several agents working
|
|
4
|
+
* on one repository each get their own working tree instead of sharing the case folder (sessions
|
|
5
|
+
* in one case share its folder, and two agents editing, building and committing in one checkout
|
|
6
|
+
* step on each other). Routes: `src/web/routes/worktree-routes.ts`; a session starts in one
|
|
7
|
+
* through the existing create paths (`POST /api/quick-start` with `worktree`, or
|
|
8
|
+
* `POST /api/sessions` with the worktree as `workingDir`).
|
|
9
|
+
*
|
|
10
|
+
* WHERE. `<repo>/.claude/worktrees/<name>` on a new branch `worktree-<name>` is Claude Code's own
|
|
11
|
+
* `--worktree` layout (measured in Claude Code 2.1.296: path `<repo>/.claude/worktrees/<name>`,
|
|
12
|
+
* branch `worktree-<name>`, names of letters, digits, `.`, `_`, `-`, at most 64 characters), and
|
|
13
|
+
* Codeman already recognizes it (the worktree pill, `CLAUDE_WORKTREE_PATH` in session-routes.ts).
|
|
14
|
+
* Names here are a strict subset of Claude's (one segment, starting with a letter or digit), so a
|
|
15
|
+
* worktree made here is one `claude --worktree <name>` would also find. The main checkout must not
|
|
16
|
+
* show the worktrees as untracked, so `/.claude/worktrees/` is appended to the repository's LOCAL
|
|
17
|
+
* `info/exclude` (in the real common git dir, never a tracked `.gitignore`) when git does not
|
|
18
|
+
* already ignore it. `/.claude/settings.local.json` gets the same treatment: Codeman installs its
|
|
19
|
+
* hooks there for every claude session, and an untracked copy in a worktree makes
|
|
20
|
+
* `git worktree remove` refuse forever (Claude Code itself ignores that file through the global
|
|
21
|
+
* excludes file, which is not guaranteed to be set up on a given machine). The copy of Codeman's
|
|
22
|
+
* agent skill (`<skills dir>/codeman/`, injected while `agentSkillEnabled` is on) is excluded for
|
|
23
|
+
* the same reason, for every skills folder the registry names (`WorktreeOptions.agentSkillDirs`).
|
|
24
|
+
*
|
|
25
|
+
* Split like the other git modules: a PURE half (name and branch validation, `worktree list`
|
|
26
|
+
* parsing, exclude text, the managed-path rule) unit-tested without git, and an IO half that
|
|
27
|
+
* spawns git through an injectable runner.
|
|
28
|
+
*
|
|
29
|
+
* Rules the code keeps and the tests pin:
|
|
30
|
+
* - git is spawned with an argv array, never a shell, with `--` before every path operand, the
|
|
31
|
+
* non-interactive environment of `gitNonInteractiveEnv()`, a timeout that kills the whole
|
|
32
|
+
* process group, and capped output. Read-only calls add `--no-optional-locks`.
|
|
33
|
+
* - Names and branches are validated BEFORE they reach git (charset, length, then
|
|
34
|
+
* `git check-ref-format --branch`, whose answer must equal the input: `@{-1}` style shorthands
|
|
35
|
+
* are refused up front, since `--branch` would expand them).
|
|
36
|
+
* - Only `<repo>/.claude/worktrees/<valid-name>` is ever created or removed. Every component of
|
|
37
|
+
* that path is `lstat`ed (a symlinked `.claude` or `worktrees` directory, or a symlink at the
|
|
38
|
+
* worktree itself, is refused) and its real path must be exactly the expected one, so a
|
|
39
|
+
* repository cannot redirect a create or a remove outside itself. What is left is a same-user
|
|
40
|
+
* race (an agent swapping a directory between the check and git), which is accepted: that
|
|
41
|
+
* agent already runs code as this user.
|
|
42
|
+
* - Removal is `git worktree remove` WITHOUT `--force` (git refuses a tree with modified or
|
|
43
|
+
* untracked files, and that refusal is the answer), refused while a live session runs inside
|
|
44
|
+
* the worktree, and the optional branch delete is `git branch -d` (never `-D`, so an unmerged
|
|
45
|
+
* branch survives).
|
|
46
|
+
* - git never runs for a case at or inside a Docker case workspace (or whose common git dir is
|
|
47
|
+
* there): a container could plant a checkout filter or a hook, and `git worktree add` runs the
|
|
48
|
+
* repository's post-checkout hook and filters on the HOST. On a local case that is acceptable,
|
|
49
|
+
* since the user's agent runs code there anyway. A managed worktree that is, sits inside, or
|
|
50
|
+
* holds a Docker case workspace is never removed either: `git worktree remove` runs a status
|
|
51
|
+
* inside the tree, then deletes it under the container.
|
|
52
|
+
* - A repository that merely encloses the case and is the home folder or above (a dotfiles repo
|
|
53
|
+
* in `$HOME`) is not the case's repository, and a case folder below a repository's top level
|
|
54
|
+
* is refused: the worktrees would not be where the case is.
|
|
55
|
+
*
|
|
56
|
+
* @module git-worktrees
|
|
57
|
+
*/
|
|
58
|
+
/** Claude Code's own limit for a `--worktree` name (2.1.296). */
|
|
59
|
+
export declare const MAX_WORKTREE_NAME_LENGTH = 64;
|
|
60
|
+
/** Longest accepted branch name. git allows more; 200 covers every real one. */
|
|
61
|
+
export declare const MAX_WORKTREE_BRANCH_LENGTH = 200;
|
|
62
|
+
/** A read-only git call (rev-parse, worktree list, check-ignore, check-ref-format). */
|
|
63
|
+
export declare const WORKTREE_READ_TIMEOUT_MS = 15000;
|
|
64
|
+
/**
|
|
65
|
+
* `git worktree add` checks out a whole tree and runs the repository's post-checkout hook and
|
|
66
|
+
* smudge filters (Git LFS downloads objects there), so it gets the longest budget.
|
|
67
|
+
*/
|
|
68
|
+
export declare const WORKTREE_ADD_TIMEOUT_MS = 180000;
|
|
69
|
+
/** `git worktree remove` runs a status of the tree first, then deletes it. */
|
|
70
|
+
export declare const WORKTREE_REMOVE_TIMEOUT_MS = 60000;
|
|
71
|
+
/** Worktrees listed. A repository with more is unusual; the count says when it was cut. */
|
|
72
|
+
export declare const MAX_WORKTREES_LISTED = 200;
|
|
73
|
+
/** Path segments of the managed worktrees folder, relative to the repository root. */
|
|
74
|
+
export declare const WORKTREES_DIR_SEGMENTS: readonly [".claude", "worktrees"];
|
|
75
|
+
/** The `info/exclude` line that hides the managed worktrees from the main checkout. */
|
|
76
|
+
export declare const WORKTREES_EXCLUDE_LINE = "/.claude/worktrees/";
|
|
77
|
+
/** The `info/exclude` line for the hooks file Codeman writes into every claude workspace. */
|
|
78
|
+
export declare const SETTINGS_LOCAL_EXCLUDE_LINE = "/.claude/settings.local.json";
|
|
79
|
+
/**
|
|
80
|
+
* The `info/exclude` line for the copy of Codeman's agent skill in `skillsDir` of a workspace
|
|
81
|
+
* (`<skillsDir>/codeman/`, written into the folder a session runs in while `agentSkillEnabled`
|
|
82
|
+
* is on), or null for a folder that is not a plain relative path.
|
|
83
|
+
*/
|
|
84
|
+
export declare function agentSkillExcludeLine(skillsDir: string): string | null;
|
|
85
|
+
/**
|
|
86
|
+
* Why `name` cannot be a worktree name, or null when it can. One path segment of letters, digits,
|
|
87
|
+
* `.`, `_` and `-`, starting with a letter or digit, at most 64 characters, never `..` (the
|
|
88
|
+
* session path schema refuses any path containing it), and not ending in `.` or `.lock` (git's
|
|
89
|
+
* own ref rules, since the default branch is derived from the name).
|
|
90
|
+
*/
|
|
91
|
+
export declare function worktreeNameError(name: unknown): string | null;
|
|
92
|
+
/** The branch a new worktree gets when the caller names none: Claude Code's `worktree-<name>`. */
|
|
93
|
+
export declare function defaultWorktreeBranch(name: string): string;
|
|
94
|
+
/**
|
|
95
|
+
* Why `branch` cannot be a new branch name, or null when it passes the charset gate. The final
|
|
96
|
+
* word is `git check-ref-format --branch` (see `createCaseWorktree`); this gate exists so nothing
|
|
97
|
+
* option-shaped, `@{...}`-shaped or otherwise surprising ever reaches git at all.
|
|
98
|
+
*/
|
|
99
|
+
export declare function worktreeBranchError(branch: unknown): string | null;
|
|
100
|
+
/** Why `base` cannot be a start point, or null. The same gate Clone Repo uses for a ref. */
|
|
101
|
+
export declare function worktreeBaseError(base: unknown): string | null;
|
|
102
|
+
/** `<repo>/.claude/worktrees`. */
|
|
103
|
+
export declare function managedWorktreesDir(repoRoot: string): string;
|
|
104
|
+
/** `<repo>/.claude/worktrees/<name>`. The caller validates `name` first. */
|
|
105
|
+
export declare function managedWorktreePath(repoRoot: string, name: string): string;
|
|
106
|
+
/**
|
|
107
|
+
* The managed worktree name of `path`, or null when it is not exactly
|
|
108
|
+
* `<repoRoot>/.claude/worktrees/<valid-name>`. Lexical: both sides must already be real paths.
|
|
109
|
+
*/
|
|
110
|
+
export declare function managedWorktreeName(repoRoot: string, path: string): string | null;
|
|
111
|
+
/** True when `child` is `root` or inside it (lexical, on resolved paths). */
|
|
112
|
+
export declare function isPathWithin(child: string, root: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* `path` as the repository spells it: a path under `alias` (the case folder as its case resolves
|
|
115
|
+
* it, which can run through a symlink) moved under `root` (the repository's real top level), else
|
|
116
|
+
* `path` unchanged. Lexical, so it costs no filesystem call per session. Sessions keep the
|
|
117
|
+
* `workingDir` they were created with, while the worktree paths git lists are real, so without
|
|
118
|
+
* this a session spelled through the case's link counted for no worktree and did not block its
|
|
119
|
+
* removal.
|
|
120
|
+
*/
|
|
121
|
+
export declare function respellUnder(path: string, alias: string, root: string): string;
|
|
122
|
+
export interface ParsedWorktree {
|
|
123
|
+
path: string;
|
|
124
|
+
/** Commit checked out (absent for a bare entry). */
|
|
125
|
+
head: string | null;
|
|
126
|
+
/** Short branch name (`refs/heads/` stripped), null when detached or bare. */
|
|
127
|
+
branch: string | null;
|
|
128
|
+
detached: boolean;
|
|
129
|
+
bare: boolean;
|
|
130
|
+
locked: boolean;
|
|
131
|
+
lockReason?: string;
|
|
132
|
+
prunable: boolean;
|
|
133
|
+
prunableReason?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Undo git's C-style path quoting (`"a\\"b\\303\\251"` → `a"bé`). Paths are printed quoted when
|
|
137
|
+
* they hold a `"`, a `\` or a control character even with `core.quotePath=false`; octal escapes are
|
|
138
|
+
* raw bytes, decoded as UTF-8. Text that is not quoted comes back unchanged.
|
|
139
|
+
*/
|
|
140
|
+
export declare function unquoteGitPath(text: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* Parse `git worktree list --porcelain` (newline-terminated form: `-z` needs git 2.36, and Ubuntu
|
|
143
|
+
* 22.04 ships 2.34). Records are separated by blank lines; the first is the main worktree. Paths
|
|
144
|
+
* are read through `unquoteGitPath`, which together with `-c core.quotePath=false` keeps non-ASCII
|
|
145
|
+
* repository paths readable.
|
|
146
|
+
*/
|
|
147
|
+
export declare function parseWorktreeList(text: string): ParsedWorktree[];
|
|
148
|
+
/**
|
|
149
|
+
* What to append to an `info/exclude` holding `existing` so that every line of `lines` is in it:
|
|
150
|
+
* the text ('' when nothing is missing) and the lines it adds. A file that does not end in a
|
|
151
|
+
* newline gets one first, so an appended pattern never fuses with the user's last line.
|
|
152
|
+
*/
|
|
153
|
+
export declare function excludeAppendText(existing: string, lines: readonly string[]): {
|
|
154
|
+
text: string;
|
|
155
|
+
added: string[];
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* The part of git's stderr a user should see: its `fatal:`/`error:` lines (with any `hint:` lines
|
|
159
|
+
* dropped, they repeat what the UI says), sanitized and capped. Shown through `textContent` only.
|
|
160
|
+
*/
|
|
161
|
+
export declare function gitRefusalText(stderr: string): string;
|
|
162
|
+
export interface WorktreeGitResult {
|
|
163
|
+
code: number | null;
|
|
164
|
+
stdout: string;
|
|
165
|
+
stderr: string;
|
|
166
|
+
timedOut: boolean;
|
|
167
|
+
spawnError?: string;
|
|
168
|
+
}
|
|
169
|
+
/** Runs `git <args>` in `cwd`; never throws. Injected so tests can refuse to run git at all. */
|
|
170
|
+
export type WorktreeGitRunner = (cwd: string, args: string[], opts?: {
|
|
171
|
+
timeoutMs?: number;
|
|
172
|
+
}) => Promise<WorktreeGitResult>;
|
|
173
|
+
/**
|
|
174
|
+
* The production runner: argv only (no shell), the non-interactive environment, captured output
|
|
175
|
+
* caps, and a timeout that signals the whole process group (`detached: true`), because
|
|
176
|
+
* `git worktree add` fans out into checkout children, hooks and filters that a signal to the
|
|
177
|
+
* parent alone would leave running.
|
|
178
|
+
*/
|
|
179
|
+
export declare const runWorktreeGit: WorktreeGitRunner;
|
|
180
|
+
/** Failure codes, named after the API error codes the routes map them to. */
|
|
181
|
+
export type WorktreeFailureCode = 'INVALID_INPUT' | 'NOT_FOUND' | 'ALREADY_EXISTS' | 'CONFLICT' | 'OPERATION_FAILED';
|
|
182
|
+
export interface WorktreeFailure {
|
|
183
|
+
ok: false;
|
|
184
|
+
code: WorktreeFailureCode;
|
|
185
|
+
message: string;
|
|
186
|
+
/** git's own words, when git refused (sanitized; render as text). */
|
|
187
|
+
gitMessage?: string;
|
|
188
|
+
}
|
|
189
|
+
export interface CaseRepository {
|
|
190
|
+
/** The repository's top level, which is the case folder (real path). */
|
|
191
|
+
repoRoot: string;
|
|
192
|
+
/** The real common git dir (`.git` of the main checkout), where `info/exclude` lives. */
|
|
193
|
+
commonDir: string;
|
|
194
|
+
}
|
|
195
|
+
export interface WorktreeOptions {
|
|
196
|
+
git?: WorktreeGitRunner;
|
|
197
|
+
/** Host paths of every Docker case workspace: git never runs at or inside one. */
|
|
198
|
+
dockerWorkspaces?: string[];
|
|
199
|
+
/** Overrides `os.homedir()` for the unrelated-ancestor rule (tests). */
|
|
200
|
+
home?: string;
|
|
201
|
+
/**
|
|
202
|
+
* Every workspace skills folder a session can get Codeman's agent skill in (the registry's
|
|
203
|
+
* `capabilities.agentSkillDir` values, passed in as data by the caller). A create excludes
|
|
204
|
+
* `<dir>/codeman/` for each, like the hooks file: a session in a worktree gets an untracked
|
|
205
|
+
* copy there, and `git worktree remove` would refuse that worktree forever.
|
|
206
|
+
*/
|
|
207
|
+
agentSkillDirs?: readonly string[];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The repository a case folder IS, or why it is not one Codeman makes worktrees for. Never runs
|
|
211
|
+
* git when the folder (or the repository's common git dir) is at or inside a Docker case workspace.
|
|
212
|
+
*/
|
|
213
|
+
export declare function resolveCaseRepository(casePath: string, opts?: WorktreeOptions): Promise<{
|
|
214
|
+
ok: true;
|
|
215
|
+
repo: CaseRepository;
|
|
216
|
+
} | WorktreeFailure>;
|
|
217
|
+
export interface CaseWorktree extends ParsedWorktree {
|
|
218
|
+
/** The repository's main checkout (git lists it first). */
|
|
219
|
+
main: boolean;
|
|
220
|
+
/** The case folder itself (the main checkout, or a linked worktree linked as the case). */
|
|
221
|
+
isCase: boolean;
|
|
222
|
+
/** Under `<repo>/.claude/worktrees/<valid-name>`: Codeman may start sessions in it and remove it. */
|
|
223
|
+
managed: boolean;
|
|
224
|
+
/** The managed name, when `managed`. */
|
|
225
|
+
name?: string;
|
|
226
|
+
}
|
|
227
|
+
export declare function listCaseWorktrees(repo: CaseRepository, opts?: WorktreeOptions): Promise<{
|
|
228
|
+
ok: true;
|
|
229
|
+
worktrees: CaseWorktree[];
|
|
230
|
+
truncated: boolean;
|
|
231
|
+
} | WorktreeFailure>;
|
|
232
|
+
/**
|
|
233
|
+
* The managed worktree `name` of `repo` as it is on disk now: every component a real directory
|
|
234
|
+
* (no symlinks), the real path the expected one, and git listing it as a worktree of this
|
|
235
|
+
* repository. Used by removal and by starting a session in it.
|
|
236
|
+
*
|
|
237
|
+
* `allowMissing` (removal only, never a session start) also accepts a worktree whose folder is
|
|
238
|
+
* GONE (deleted by hand, or by an agent) while git still lists it, as prunable, at exactly the
|
|
239
|
+
* managed path: no component on the way may be a symlink or a file, and `missing` says so.
|
|
240
|
+
* Without it such a worktree was a dead end: it could not be removed, and its name could not be
|
|
241
|
+
* used again (the branch exists, then git refuses "a missing but already registered worktree").
|
|
242
|
+
*/
|
|
243
|
+
export declare function resolveManagedWorktree(repo: CaseRepository, name: string, opts?: WorktreeOptions & {
|
|
244
|
+
allowMissing?: boolean;
|
|
245
|
+
}): Promise<{
|
|
246
|
+
ok: true;
|
|
247
|
+
path: string;
|
|
248
|
+
worktree: CaseWorktree;
|
|
249
|
+
missing: boolean;
|
|
250
|
+
} | WorktreeFailure>;
|
|
251
|
+
export interface CreateWorktreeRequest {
|
|
252
|
+
name: string;
|
|
253
|
+
/** New branch; default `worktree-<name>`. */
|
|
254
|
+
branch?: string;
|
|
255
|
+
/** Start point; default `HEAD` (the case's current commit). */
|
|
256
|
+
base?: string;
|
|
257
|
+
}
|
|
258
|
+
export interface CreatedWorktree {
|
|
259
|
+
name: string;
|
|
260
|
+
path: string;
|
|
261
|
+
branch: string;
|
|
262
|
+
base: string;
|
|
263
|
+
/** The commit the new branch starts at. */
|
|
264
|
+
head: string;
|
|
265
|
+
/** Lines appended to the repository's `info/exclude` (empty when git already ignored them). */
|
|
266
|
+
excludeAdded: string[];
|
|
267
|
+
warnings: string[];
|
|
268
|
+
}
|
|
269
|
+
export declare function createCaseWorktree(repo: CaseRepository, req: CreateWorktreeRequest, opts?: WorktreeOptions): Promise<{
|
|
270
|
+
ok: true;
|
|
271
|
+
worktree: CreatedWorktree;
|
|
272
|
+
} | WorktreeFailure>;
|
|
273
|
+
export interface RemoveWorktreeRequest {
|
|
274
|
+
name: string;
|
|
275
|
+
/** Also `git branch -d` the worktree's branch (never `-D`). */
|
|
276
|
+
deleteBranch?: boolean;
|
|
277
|
+
/** True when a live session runs at or inside `path`: the removal is refused. */
|
|
278
|
+
isInUse: (path: string) => boolean | Promise<boolean>;
|
|
279
|
+
}
|
|
280
|
+
export interface RemovedWorktree {
|
|
281
|
+
name: string;
|
|
282
|
+
path: string;
|
|
283
|
+
branch: string | null;
|
|
284
|
+
branchDeleted: boolean;
|
|
285
|
+
/** git's refusal to delete the branch (typically "not fully merged"); the worktree is gone regardless. */
|
|
286
|
+
branchError?: string;
|
|
287
|
+
}
|
|
288
|
+
export declare function removeCaseWorktree(repo: CaseRepository, req: RemoveWorktreeRequest, opts?: WorktreeOptions): Promise<{
|
|
289
|
+
ok: true;
|
|
290
|
+
removed: RemovedWorktree;
|
|
291
|
+
} | WorktreeFailure>;
|
|
292
|
+
//# sourceMappingURL=git-worktrees.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-worktrees.d.ts","sourceRoot":"","sources":["../src/git-worktrees.ts"],"names":[],"mappings":"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"}
|