@xingular/harness 0.1.7 → 1.1.1
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/CHANGELOG.md +125 -1
- package/LICENSE +202 -0
- package/NOTICE.md +22 -0
- package/README.md +239 -86
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +17853 -13114
- package/dist/sdk/accounts/accounts.d.ts +165 -0
- package/dist/sdk/accounts/accounts.js +730 -0
- package/dist/sdk/accounts/choices.d.ts +23 -0
- package/dist/sdk/accounts/choices.js +62 -0
- package/dist/sdk/accounts/env.d.ts +78 -0
- package/dist/sdk/accounts/env.js +294 -0
- package/dist/sdk/accounts/secrets.d.ts +16 -0
- package/dist/sdk/accounts/secrets.js +40 -0
- package/dist/sdk/accounts/store.d.ts +114 -0
- package/dist/sdk/accounts/store.js +255 -0
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +34 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/profiles.d.ts +80 -0
- package/dist/sdk/agents/profiles.js +221 -0
- package/dist/sdk/annotations/bridge.d.ts +42 -0
- package/dist/sdk/annotations/bridge.js +264 -0
- package/dist/sdk/annotations/capture.d.ts +116 -0
- package/dist/sdk/annotations/capture.js +308 -0
- package/dist/sdk/annotations/delivery.d.ts +72 -0
- package/dist/sdk/annotations/delivery.js +130 -0
- package/dist/sdk/annotations/inspect.d.ts +10 -0
- package/dist/sdk/annotations/inspect.js +33 -0
- package/dist/sdk/annotations/isolated.d.ts +6 -0
- package/dist/sdk/annotations/isolated.js +51 -0
- package/dist/sdk/annotations/pageScript.d.ts +28 -0
- package/dist/sdk/annotations/pageScript.js +570 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +144 -0
- package/dist/sdk/annotations/queue.d.ts +4 -0
- package/dist/sdk/annotations/queue.js +40 -0
- package/dist/sdk/annotations/serverMethods.d.ts +3 -0
- package/dist/sdk/annotations/serverMethods.js +62 -0
- package/dist/sdk/annotations/store.d.ts +160 -0
- package/dist/sdk/annotations/store.js +386 -0
- package/dist/sdk/browser/config.d.ts +16 -0
- package/dist/sdk/browser/config.js +22 -0
- package/dist/sdk/browser/diagnostics.d.ts +4 -0
- package/dist/sdk/browser/diagnostics.js +38 -0
- package/dist/sdk/browser/index.d.ts +28 -0
- package/dist/sdk/browser/index.js +72 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +85 -0
- package/dist/sdk/browser/session.js +228 -0
- package/dist/sdk/browser/tools.d.ts +8 -0
- package/dist/sdk/browser/tools.js +329 -0
- package/dist/sdk/capabilities/api.d.ts +22 -0
- package/dist/sdk/capabilities/api.js +54 -0
- package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
- package/dist/sdk/capabilities/runSnapshot.js +47 -0
- package/dist/sdk/capabilities/types.d.ts +29 -0
- package/dist/sdk/capabilities/types.js +1 -0
- package/dist/sdk/cli.d.ts +2 -0
- package/dist/sdk/cli.js +150 -0
- package/dist/sdk/core/bus.d.ts +23 -0
- package/dist/sdk/core/bus.js +76 -0
- package/dist/sdk/core/caller.d.ts +22 -0
- package/dist/sdk/core/caller.js +16 -0
- package/dist/sdk/core/changes.d.ts +15 -0
- package/dist/sdk/core/changes.js +15 -0
- package/dist/sdk/core/effort.d.ts +2 -0
- package/dist/sdk/core/effort.js +1 -0
- package/dist/sdk/core/env.d.ts +18 -0
- package/dist/sdk/core/env.js +8 -0
- package/dist/sdk/core/errors.d.ts +15 -0
- package/dist/sdk/core/errors.js +24 -0
- package/dist/sdk/core/frontmatter.d.ts +12 -0
- package/dist/sdk/core/frontmatter.js +50 -0
- package/dist/sdk/core/fs.d.ts +18 -0
- package/dist/sdk/core/fs.js +106 -0
- package/dist/sdk/core/index.d.ts +24 -0
- package/dist/sdk/core/index.js +187 -0
- package/dist/sdk/core/path.d.ts +7 -0
- package/dist/sdk/core/path.js +79 -0
- package/dist/sdk/core/projects.d.ts +18 -0
- package/dist/sdk/core/projects.js +67 -0
- package/dist/sdk/core/secrets.d.ts +2 -0
- package/dist/sdk/core/secrets.js +5 -0
- package/dist/sdk/core/text.d.ts +7 -0
- package/dist/sdk/core/text.js +40 -0
- package/dist/sdk/doctor/checks.d.ts +5 -0
- package/dist/sdk/doctor/checks.js +208 -0
- package/dist/sdk/doctor/probe.d.ts +4 -0
- package/dist/sdk/doctor/probe.js +92 -0
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/doctor/service.d.ts +23 -0
- package/dist/sdk/doctor/service.js +144 -0
- package/dist/sdk/doctor/types.d.ts +141 -0
- package/dist/sdk/doctor/types.js +29 -0
- package/dist/sdk/harness.d.ts +148 -0
- package/dist/sdk/harness.js +338 -0
- package/dist/sdk/history/lessonPrompt.d.ts +2 -0
- package/dist/sdk/history/lessonPrompt.js +15 -0
- package/dist/sdk/history/lessons.d.ts +52 -0
- package/dist/sdk/history/lessons.js +173 -0
- package/dist/sdk/history/recall.d.ts +68 -0
- package/dist/sdk/history/recall.js +199 -0
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/history/trajectory.d.ts +7 -0
- package/dist/sdk/history/trajectory.js +216 -0
- package/dist/sdk/hub/catalog.json +123 -0
- package/dist/sdk/hub/cli.d.ts +7 -0
- package/dist/sdk/hub/cli.js +81 -0
- package/dist/sdk/hub/hub.d.ts +74 -0
- package/dist/sdk/hub/hub.js +474 -0
- package/dist/sdk/hub/schema.d.ts +280 -0
- package/dist/sdk/hub/schema.js +78 -0
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +73 -0
- package/dist/sdk/index.js +55 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +676 -0
- package/dist/sdk/mcp/manager.d.ts +26 -0
- package/dist/sdk/mcp/manager.js +124 -0
- package/dist/sdk/mcp/oauth.d.ts +35 -0
- package/dist/sdk/mcp/oauth.js +171 -0
- package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
- package/dist/sdk/mcp/scopeMigration.js +198 -0
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/mcp/service.d.ts +160 -0
- package/dist/sdk/mcp/service.js +510 -0
- package/dist/sdk/mcp/types.d.ts +131 -0
- package/dist/sdk/mcp/types.js +49 -0
- package/dist/sdk/pi/runtime.d.ts +13 -0
- package/dist/sdk/pi/runtime.js +31 -0
- package/dist/sdk/plugins/api.d.ts +66 -0
- package/dist/sdk/plugins/api.js +155 -0
- package/dist/sdk/plugins/attachments.d.ts +46 -0
- package/dist/sdk/plugins/attachments.js +111 -0
- package/dist/sdk/plugins/builtin.d.ts +7 -0
- package/dist/sdk/plugins/builtin.js +163 -0
- package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
- package/dist/sdk/plugins/builtinRegistry.js +49 -0
- package/dist/sdk/plugins/catalogue.d.ts +29 -0
- package/dist/sdk/plugins/catalogue.js +196 -0
- package/dist/sdk/plugins/cli.d.ts +13 -0
- package/dist/sdk/plugins/cli.js +188 -0
- package/dist/sdk/plugins/discover.d.ts +42 -0
- package/dist/sdk/plugins/discover.js +115 -0
- package/dist/sdk/plugins/firstParty.d.ts +50 -0
- package/dist/sdk/plugins/firstParty.js +113 -0
- package/dist/sdk/plugins/hooks.d.ts +94 -0
- package/dist/sdk/plugins/hooks.js +162 -0
- package/dist/sdk/plugins/identity.d.ts +19 -0
- package/dist/sdk/plugins/identity.js +48 -0
- package/dist/sdk/plugins/install.d.ts +20 -0
- package/dist/sdk/plugins/install.js +110 -0
- package/dist/sdk/plugins/inventory.d.ts +22 -0
- package/dist/sdk/plugins/inventory.js +110 -0
- package/dist/sdk/plugins/manifest.d.ts +13 -0
- package/dist/sdk/plugins/manifest.js +56 -0
- package/dist/sdk/plugins/plugins.d.ts +104 -0
- package/dist/sdk/plugins/plugins.js +164 -0
- package/dist/sdk/plugins/runtime.d.ts +9 -0
- package/dist/sdk/plugins/runtime.js +95 -0
- package/dist/sdk/plugins/trust.d.ts +44 -0
- package/dist/sdk/plugins/trust.js +120 -0
- package/dist/sdk/plugins/types.d.ts +159 -0
- package/dist/sdk/plugins/types.js +7 -0
- package/dist/sdk/prompt/compile.d.ts +177 -0
- package/dist/sdk/prompt/compile.js +413 -0
- package/dist/sdk/refine/cli.d.ts +4 -0
- package/dist/sdk/refine/cli.js +186 -0
- package/dist/sdk/refine/demo.d.ts +21 -0
- package/dist/sdk/refine/demo.js +114 -0
- package/dist/sdk/refine/evidence.d.ts +55 -0
- package/dist/sdk/refine/evidence.js +330 -0
- package/dist/sdk/refine/host.d.ts +51 -0
- package/dist/sdk/refine/host.js +76 -0
- package/dist/sdk/refine/ledger.d.ts +48 -0
- package/dist/sdk/refine/ledger.js +76 -0
- package/dist/sdk/refine/library.d.ts +55 -0
- package/dist/sdk/refine/library.js +177 -0
- package/dist/sdk/refine/pi.d.ts +72 -0
- package/dist/sdk/refine/pi.js +248 -0
- package/dist/sdk/refine/prompts.d.ts +45 -0
- package/dist/sdk/refine/prompts.js +390 -0
- package/dist/sdk/refine/recursion.d.ts +113 -0
- package/dist/sdk/refine/recursion.js +258 -0
- package/dist/sdk/refine/runner.d.ts +81 -0
- package/dist/sdk/refine/runner.js +150 -0
- package/dist/sdk/refine/selection.d.ts +57 -0
- package/dist/sdk/refine/selection.js +251 -0
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/refine/service.d.ts +84 -0
- package/dist/sdk/refine/service.js +165 -0
- package/dist/sdk/refine/suite.d.ts +67 -0
- package/dist/sdk/refine/suite.js +174 -0
- package/dist/sdk/refine/trajectory.d.ts +9 -0
- package/dist/sdk/refine/trajectory.js +68 -0
- package/dist/sdk/refine/types.d.ts +77 -0
- package/dist/sdk/refine/types.js +10 -0
- package/dist/sdk/server/methods.d.ts +3 -0
- package/dist/sdk/server/methods.js +293 -0
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/server/remote.d.ts +19 -0
- package/dist/sdk/server/remote.js +84 -0
- package/dist/sdk/server/rpc.d.ts +20 -0
- package/dist/sdk/server/rpc.js +102 -0
- package/dist/sdk/server/serve.d.ts +10 -0
- package/dist/sdk/server/serve.js +18 -0
- package/dist/sdk/sessions/approvals.d.ts +47 -0
- package/dist/sdk/sessions/approvals.js +119 -0
- package/dist/sdk/sessions/continuation.d.ts +48 -0
- package/dist/sdk/sessions/continuation.js +85 -0
- package/dist/sdk/sessions/contracts.d.ts +551 -0
- package/dist/sdk/sessions/contracts.js +338 -0
- package/dist/sdk/sessions/extensionUi.d.ts +19 -0
- package/dist/sdk/sessions/extensionUi.js +64 -0
- package/dist/sdk/sessions/history.d.ts +16 -0
- package/dist/sdk/sessions/history.js +68 -0
- package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
- package/dist/sdk/sessions/lineageJoin.js +61 -0
- package/dist/sdk/sessions/planMode.d.ts +39 -0
- package/dist/sdk/sessions/planMode.js +93 -0
- package/dist/sdk/sessions/policy.d.ts +43 -0
- package/dist/sdk/sessions/policy.js +1 -0
- package/dist/sdk/sessions/pricing.d.ts +34 -0
- package/dist/sdk/sessions/pricing.js +40 -0
- package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
- package/dist/sdk/sessions/reasoningSummaries.js +61 -0
- package/dist/sdk/sessions/registry.d.ts +103 -0
- package/dist/sdk/sessions/registry.js +386 -0
- package/dist/sdk/sessions/runIndex.d.ts +21 -0
- package/dist/sdk/sessions/runIndex.js +65 -0
- package/dist/sdk/sessions/runStore.d.ts +66 -0
- package/dist/sdk/sessions/runStore.js +337 -0
- package/dist/sdk/sessions/runner.d.ts +213 -0
- package/dist/sdk/sessions/runner.js +589 -0
- package/dist/sdk/sessions/sessions.d.ts +152 -0
- package/dist/sdk/sessions/sessions.js +461 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
- package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
- package/dist/sdk/sessions/sideQuestions.js +290 -0
- package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/subagentServerMethods.js +41 -0
- package/dist/sdk/sessions/subagentService.d.ts +52 -0
- package/dist/sdk/sessions/subagentService.js +265 -0
- package/dist/sdk/sessions/subagents.d.ts +58 -0
- package/dist/sdk/sessions/subagents.js +87 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -0
- package/dist/sdk/terminal/browser.d.ts +62 -0
- package/dist/sdk/terminal/browser.js +41 -0
- package/dist/sdk/terminal/browserControl.d.ts +29 -0
- package/dist/sdk/terminal/browserControl.js +186 -0
- package/dist/sdk/terminal/cli.d.ts +20 -0
- package/dist/sdk/terminal/cli.js +121 -0
- package/dist/sdk/terminal/client.d.ts +15 -0
- package/dist/sdk/terminal/client.js +126 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +78 -0
- package/dist/sdk/terminal/controller.js +383 -0
- package/dist/sdk/terminal/hubMenu.d.ts +10 -0
- package/dist/sdk/terminal/hubMenu.js +34 -0
- package/dist/sdk/terminal/launch.d.ts +5 -0
- package/dist/sdk/terminal/launch.js +27 -0
- package/dist/sdk/terminal/logs.d.ts +14 -0
- package/dist/sdk/terminal/logs.js +59 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +377 -0
- package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
- package/dist/sdk/terminal/pluginMenu.js +101 -0
- package/dist/sdk/terminal/socket.d.ts +73 -0
- package/dist/sdk/terminal/socket.js +287 -0
- package/dist/sdk/terminal/standalone.d.ts +20 -0
- package/dist/sdk/terminal/standalone.js +186 -0
- package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
- package/dist/sdk/terminal/subagentMonitor.js +121 -0
- package/dist/sdk/terminal/terminals.d.ts +212 -0
- package/dist/sdk/terminal/terminals.js +1108 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -0
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
- package/dist/sdk/terminal/tui/chrome.js +169 -0
- package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
- package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +11 -0
- package/dist/sdk/terminal/tui/commandPages.js +111 -0
- package/dist/sdk/terminal/tui/commands.d.ts +46 -0
- package/dist/sdk/terminal/tui/commands.js +399 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +52 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +80 -0
- package/dist/sdk/terminal/tui/dialogs.js +370 -0
- package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
- package/dist/sdk/terminal/tui/keybindings.js +49 -0
- package/dist/sdk/terminal/tui/list.d.ts +73 -0
- package/dist/sdk/terminal/tui/list.js +196 -0
- package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
- package/dist/sdk/terminal/tui/modeChoices.js +70 -0
- package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
- package/dist/sdk/terminal/tui/modelPicker.js +150 -0
- package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
- package/dist/sdk/terminal/tui/mouse.js +3 -0
- package/dist/sdk/terminal/tui/page.d.ts +17 -0
- package/dist/sdk/terminal/tui/page.js +1 -0
- package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +156 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +76 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +409 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
- package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +233 -0
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
- package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
- package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
- package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +63 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +103 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +65 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +450 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
- package/dist/sdk/terminal/tui/piAdapter.js +48 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
- package/dist/sdk/terminal/tui/planMode.js +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
- package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +21 -0
- package/dist/sdk/terminal/tui/questions.js +42 -0
- package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
- package/dist/sdk/terminal/tui/runtime.js +443 -0
- package/dist/sdk/terminal/tui/search.d.ts +22 -0
- package/dist/sdk/terminal/tui/search.js +60 -0
- package/dist/sdk/terminal/tui/selection.d.ts +16 -0
- package/dist/sdk/terminal/tui/selection.js +16 -0
- package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
- package/dist/sdk/terminal/tui/sessionUi.js +30 -0
- package/dist/sdk/terminal/tui/settingsModel.d.ts +14 -0
- package/dist/sdk/terminal/tui/settingsModel.js +114 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +230 -0
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
- package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
- package/dist/sdk/terminal/tui/status.d.ts +18 -0
- package/dist/sdk/terminal/tui/status.js +67 -0
- package/dist/sdk/terminal/tui/theme.d.ts +45 -0
- package/dist/sdk/terminal/tui/theme.js +176 -0
- package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
- package/dist/sdk/terminal/tui/treeModel.js +483 -0
- package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
- package/dist/sdk/terminal/tui/treePicker.js +251 -0
- package/dist/sdk/terminal/tui/ui.d.ts +49 -0
- package/dist/sdk/terminal/tui/ui.js +149 -0
- package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
- package/dist/sdk/terminal/tui/workingStatus.js +229 -0
- package/dist/sdk/tools/askUser.d.ts +6 -0
- package/dist/sdk/tools/askUser.js +92 -0
- package/dist/sdk/tools/checklist.d.ts +9 -0
- package/dist/sdk/tools/checklist.js +54 -0
- package/dist/sdk/tools/definitions.d.ts +30 -0
- package/dist/sdk/tools/definitions.js +117 -0
- package/dist/sdk/tools/dispatch.d.ts +16 -0
- package/dist/sdk/tools/dispatch.js +67 -0
- package/dist/sdk/tools/harnessTools.d.ts +8 -0
- package/dist/sdk/tools/harnessTools.js +20 -0
- package/dist/sdk/tools/sessionName.d.ts +7 -0
- package/dist/sdk/tools/sessionName.js +31 -0
- package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
- package/dist/sdk/tools/spawnSubagent.js +58 -0
- package/dist/sdk/tools/types.d.ts +48 -0
- package/dist/sdk/tools/types.js +1 -0
- package/dist/sdk/variant/components.d.ts +34 -0
- package/dist/sdk/variant/components.js +230 -0
- package/dist/sdk/variant/materialise.d.ts +66 -0
- package/dist/sdk/variant/materialise.js +81 -0
- package/dist/sdk/variant/migrate.d.ts +4 -0
- package/dist/sdk/variant/migrate.js +41 -0
- package/dist/sdk/variant/seed.d.ts +20 -0
- package/dist/sdk/variant/seed.js +116 -0
- package/dist/sdk/variant/store.d.ts +82 -0
- package/dist/sdk/variant/store.js +151 -0
- package/dist/sdk/variant/surface.d.ts +56 -0
- package/dist/sdk/variant/surface.js +81 -0
- package/dist/sdk/variant/trust.d.ts +27 -0
- package/dist/sdk/variant/trust.js +64 -0
- package/dist/sdk/variant/variant.d.ts +71 -0
- package/dist/sdk/variant/variant.js +331 -0
- package/dist/sdk/web/markdown.d.ts +10 -0
- package/dist/sdk/web/markdown.js +66 -0
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/safeRequest.d.ts +34 -0
- package/dist/sdk/web/safeRequest.js +175 -0
- package/dist/sdk/web/search.d.ts +53 -0
- package/dist/sdk/web/search.js +168 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -0
- package/dist/sdk/web/text.d.ts +2 -0
- package/dist/sdk/web/text.js +7 -0
- package/dist/sdk/web/tools.d.ts +22 -0
- package/dist/sdk/web/tools.js +195 -0
- package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
- package/dist/sdk/wiki/notes.d.ts +68 -0
- package/dist/sdk/wiki/notes.js +297 -0
- package/dist/sdk/wiki/serverMethods.d.ts +3 -0
- package/dist/sdk/wiki/serverMethods.js +26 -0
- package/dist/sdk/worktrees/cli.d.ts +11 -0
- package/dist/sdk/worktrees/cli.js +146 -0
- package/dist/sdk/worktrees/copy.d.ts +9 -0
- package/dist/sdk/worktrees/copy.js +111 -0
- package/dist/sdk/worktrees/git.d.ts +16 -0
- package/dist/sdk/worktrees/git.js +81 -0
- package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
- package/dist/sdk/worktrees/serverMethods.js +30 -0
- package/dist/sdk/worktrees/service.d.ts +73 -0
- package/dist/sdk/worktrees/service.js +483 -0
- package/dist/sdk/worktrees/setup.d.ts +23 -0
- package/dist/sdk/worktrees/setup.js +65 -0
- package/dist/sdk/worktrees/types.d.ts +91 -0
- package/dist/sdk/worktrees/types.js +1 -0
- package/dist/sdk/world/actions.d.ts +23 -0
- package/dist/sdk/world/actions.js +31 -0
- package/dist/sdk/world/cli.d.ts +12 -0
- package/dist/sdk/world/cli.js +166 -0
- package/dist/sdk/world/images.d.ts +20 -0
- package/dist/sdk/world/images.js +48 -0
- package/dist/sdk/world/untrusted.d.ts +12 -0
- package/dist/sdk/world/untrusted.js +20 -0
- package/dist/sdk/world/world.d.ts +67 -0
- package/dist/sdk/world/world.js +79 -0
- package/dist/terminal-opentui.js +20778 -0
- package/dist/terminal-pi.js +794 -518
- package/docs/README.md +42 -0
- package/docs/architecture/00-overview.md +171 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +203 -0
- package/docs/architecture/03-variants.md +103 -0
- package/docs/architecture/04-prompt.md +122 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +129 -0
- package/docs/architecture/07-tools-and-gate.md +103 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +98 -0
- package/docs/architecture/10-server-protocol.md +193 -0
- package/docs/architecture/11-terminal.md +261 -0
- package/docs/architecture/12-storage.md +75 -0
- package/docs/architecture/13-world-tools.md +45 -0
- package/docs/architecture/14-surfaces-and-parity.md +89 -0
- package/docs/architecture/README.md +125 -0
- package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
- package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
- package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
- package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
- package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +70 -0
- package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
- package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
- package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
- package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
- package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
- package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
- package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
- package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
- package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
- package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
- package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
- package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
- package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
- package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
- package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
- package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
- package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
- package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
- package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
- package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
- package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
- package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
- package/docs/architecture/proposals/README.md +101 -0
- package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
- package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
- package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
- package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
- package/docs/decisions/ADR-0007-reporting-section.md +33 -0
- package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
- package/docs/decisions/ADR-0009-xharness-command.md +39 -0
- package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
- package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
- package/docs/decisions/ADR-0012-native-session-names.md +34 -0
- package/docs/decisions/ADR-0013-doctor.md +27 -0
- package/docs/decisions/ADR-0014-public-release-baseline.md +22 -0
- package/docs/decisions/ADR-0015-resource-browser.md +28 -0
- package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
- package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
- package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
- package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
- package/docs/decisions/ADR-0020-page-annotations.md +37 -0
- package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
- package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
- package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
- package/docs/decisions/ADR-0024-xagent-command.md +40 -0
- package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
- package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
- package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
- package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
- package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
- package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
- package/docs/doctor.md +80 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
- package/docs/features/README.md +46 -0
- package/docs/features/automations/README.md +41 -0
- package/docs/features/cores/README.md +39 -0
- package/docs/features/google-harness2/README.md +52 -0
- package/docs/features/integrations/README.md +40 -0
- package/docs/features/interfaces/README.md +29 -0
- package/docs/features/memory/README.md +28 -0
- package/docs/future-work.md +48 -0
- package/docs/parity.md +78 -0
- package/docs/releases/1.0.0.md +40 -0
- package/docs/releases/4.0.0.md +46 -0
- package/docs/releases/README.md +41 -0
- package/docs/study/00-index.md +83 -0
- package/docs/study/arms/00-matrix.md +20 -0
- package/docs/study/arms/01-system-prompt.md +211 -0
- package/docs/study/arms/02-tools.md +79 -0
- package/docs/study/arms/03-subagents.md +90 -0
- package/docs/study/arms/04-skills.md +75 -0
- package/docs/study/arms/05-context-management.md +90 -0
- package/docs/study/arms/06-memory.md +90 -0
- package/docs/study/arms/07-governance.md +71 -0
- package/docs/study/arms/08-execution.md +69 -0
- package/docs/study/arms/09-observability.md +60 -0
- package/docs/study/arms/10-refinement.md +110 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/roadmap.md +72 -0
- package/docs/study/sources/claude-code.md +44 -0
- package/docs/study/sources/deepseek-harness.md +102 -0
- package/docs/study/sources/harness2.md +50 -0
- package/docs/study/sources/kimi-cli.md +42 -0
- package/docs/study/sources/omp.md +93 -0
- package/docs/study/sources/opencode.md +43 -0
- package/docs/study/sources/openhands.md +46 -0
- package/docs/study/sources/smolagents.md +40 -0
- package/docs/study/sources/swe-agent.md +34 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +707 -58
- package/package.json +43 -13
- package/docs/evidence/terminal-2026-10-17/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/verification.json +0 -130
- package/docs/evidence/terminal-2026-10-17-followup/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/README.md +0 -27
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command.json +0 -18
- package/docs/evidence/terminal-2026-10-17-followup/global-install/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-final-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-scenario.json +0 -60
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/README.md +0 -24
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/complete-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/verification.json +0 -57
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24-early.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-scenario.json +0 -58
- package/docs/evidence/terminal-2026-10-17-followup/omp-supplemental-static.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/rea-artifacts.json +0 -320
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.json +0 -30
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.md +0 -133
- package/docs/evidence/terminal-2026-10-17-followup/reference-source-observations.json +0 -20
- package/docs/evidence/terminal-2026-10-17-followup/verification.json +0 -207
- package/src/accounts/accounts.test.ts +0 -333
- package/src/accounts/accounts.ts +0 -658
- package/src/accounts/env.test.ts +0 -95
- package/src/accounts/env.ts +0 -338
- package/src/accounts/secrets.ts +0 -50
- package/src/accounts/store.ts +0 -338
- package/src/capabilities/api.ts +0 -75
- package/src/capabilities/runSnapshot.ts +0 -59
- package/src/capabilities/types.ts +0 -34
- package/src/cli.ts +0 -118
- package/src/core/bus.ts +0 -34
- package/src/core/changes.ts +0 -33
- package/src/core/env.ts +0 -27
- package/src/core/errors.ts +0 -32
- package/src/core/frontmatter.ts +0 -47
- package/src/core/fs.test.ts +0 -38
- package/src/core/fs.ts +0 -92
- package/src/core/index.ts +0 -193
- package/src/core/path.test.ts +0 -14
- package/src/core/path.ts +0 -79
- package/src/core/projects.ts +0 -90
- package/src/core/text.ts +0 -39
- package/src/harness.ts +0 -312
- package/src/hub/catalog.json +0 -123
- package/src/hub/cli.ts +0 -59
- package/src/hub/hub.test.ts +0 -205
- package/src/hub/hub.ts +0 -256
- package/src/hub/schema.ts +0 -81
- package/src/index.ts +0 -75
- package/src/mcp/connections.ts +0 -614
- package/src/mcp/manager.ts +0 -130
- package/src/mcp/oauth.ts +0 -195
- package/src/mcp/scopeMigration.test.ts +0 -79
- package/src/mcp/scopeMigration.ts +0 -204
- package/src/mcp/service.test.ts +0 -376
- package/src/mcp/service.ts +0 -549
- package/src/mcp/types.ts +0 -108
- package/src/memory/api.test.ts +0 -82
- package/src/memory/api.ts +0 -303
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -408
- package/src/memory/caller.ts +0 -69
- package/src/memory/curation.test.ts +0 -163
- package/src/memory/curation.ts +0 -64
- package/src/memory/dispatch.ts +0 -59
- package/src/memory/facts.test.ts +0 -41
- package/src/memory/facts.ts +0 -248
- package/src/memory/graph.ts +0 -103
- package/src/memory/health.test.ts +0 -130
- package/src/memory/health.ts +0 -74
- package/src/memory/maintainer.ts +0 -272
- package/src/memory/maintenance.test.ts +0 -176
- package/src/memory/maintenance.ts +0 -518
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -150
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -584
- package/src/memory/recall.test.ts +0 -115
- package/src/memory/recall.ts +0 -278
- package/src/memory/references.ts +0 -77
- package/src/memory/runContext.ts +0 -93
- package/src/memory/secrets.ts +0 -6
- package/src/memory/store.test.ts +0 -66
- package/src/memory/store.ts +0 -385
- package/src/memory/wiki.test.ts +0 -47
- package/src/memory/wiki.ts +0 -422
- package/src/memory/wikiIndex.test.ts +0 -88
- package/src/memory/wikiIndex.ts +0 -116
- package/src/plugins/api.ts +0 -181
- package/src/plugins/attachments.test.ts +0 -84
- package/src/plugins/attachments.ts +0 -141
- package/src/plugins/builtin.ts +0 -82
- package/src/plugins/catalogue.test.ts +0 -254
- package/src/plugins/catalogue.ts +0 -251
- package/src/plugins/cli.ts +0 -184
- package/src/plugins/discover.ts +0 -151
- package/src/plugins/firstParty.test.ts +0 -143
- package/src/plugins/firstParty.ts +0 -129
- package/src/plugins/hooks.test.ts +0 -250
- package/src/plugins/hooks.ts +0 -239
- package/src/plugins/identity.ts +0 -37
- package/src/plugins/install.test.ts +0 -107
- package/src/plugins/install.ts +0 -106
- package/src/plugins/inventory.ts +0 -123
- package/src/plugins/manifest.ts +0 -76
- package/src/plugins/plugins.test.ts +0 -205
- package/src/plugins/plugins.ts +0 -189
- package/src/plugins/proposal.ts +0 -121
- package/src/plugins/runtime.test.ts +0 -40
- package/src/plugins/runtime.ts +0 -82
- package/src/plugins/trust.ts +0 -142
- package/src/plugins/types.ts +0 -149
- package/src/prompt/compile.test.ts +0 -261
- package/src/prompt/compile.ts +0 -732
- package/src/server/methods.ts +0 -349
- package/src/server/remote.ts +0 -77
- package/src/server/rpc.ts +0 -116
- package/src/server/serve.ts +0 -28
- package/src/server/server.test.ts +0 -174
- package/src/sessions/approvals.ts +0 -118
- package/src/sessions/contracts.ts +0 -336
- package/src/sessions/conversation.test.ts +0 -177
- package/src/sessions/extensionSession.test.ts +0 -74
- package/src/sessions/extensionUi.test.ts +0 -65
- package/src/sessions/extensionUi.ts +0 -102
- package/src/sessions/history.ts +0 -51
- package/src/sessions/lineageJoin.ts +0 -75
- package/src/sessions/policy.ts +0 -36
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/questionBatches.test.ts +0 -110
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -381
- package/src/sessions/runIndex.ts +0 -99
- package/src/sessions/runStore.test.ts +0 -152
- package/src/sessions/runStore.ts +0 -332
- package/src/sessions/runner.ts +0 -604
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -549
- package/src/sessions/sessions.ts +0 -339
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -413
- package/src/terminal/accountsMenu.test.ts +0 -225
- package/src/terminal/accountsMenu.ts +0 -382
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/control.test.ts +0 -23
- package/src/terminal/control.ts +0 -23
- package/src/terminal/controller.ts +0 -291
- package/src/terminal/hubMenu.ts +0 -24
- package/src/terminal/launch.test.ts +0 -25
- package/src/terminal/launch.ts +0 -48
- package/src/terminal/piExtension.lifecycle.test.ts +0 -55
- package/src/terminal/piExtension.ts +0 -259
- package/src/terminal/pluginControl.test.ts +0 -146
- package/src/terminal/pluginMenu.ts +0 -112
- package/src/terminal/socket.test.ts +0 -71
- package/src/terminal/socket.ts +0 -232
- package/src/terminal/standalone.test.ts +0 -167
- package/src/terminal/standalone.ts +0 -153
- package/src/terminal/terminal.test.ts +0 -399
- package/src/terminal/terminals.ts +0 -553
- package/src/terminal/tui/application.test.ts +0 -114
- package/src/terminal/tui/application.ts +0 -491
- package/src/terminal/tui/commands.test.ts +0 -218
- package/src/terminal/tui/commands.ts +0 -361
- package/src/terminal/tui/dialogs.ts +0 -301
- package/src/terminal/tui/editor.test.ts +0 -84
- package/src/terminal/tui/editor.ts +0 -79
- package/src/terminal/tui/keybindings.test.ts +0 -22
- package/src/terminal/tui/keybindings.ts +0 -50
- package/src/terminal/tui/piAdapter.ts +0 -62
- package/src/terminal/tui/presentation.test.ts +0 -79
- package/src/terminal/tui/questions.test.ts +0 -41
- package/src/terminal/tui/questions.ts +0 -47
- package/src/terminal/tui/runtime.socket.test.ts +0 -104
- package/src/terminal/tui/runtime.test.ts +0 -215
- package/src/terminal/tui/runtime.ts +0 -388
- package/src/terminal/tui/sessionUi.ts +0 -24
- package/src/terminal/tui/settings.test.ts +0 -150
- package/src/terminal/tui/settings.ts +0 -188
- package/src/terminal/tui/status.test.ts +0 -70
- package/src/terminal/tui/status.ts +0 -109
- package/src/terminal/tui/theme.ts +0 -174
- package/src/terminal/tui/tools.test.ts +0 -145
- package/src/terminal/tui/tools.ts +0 -142
- package/src/terminal/tui/transcript.test.ts +0 -25
- package/src/terminal/tui/transcript.ts +0 -124
- package/src/terminal/tui/ui.test.ts +0 -313
- package/src/terminal/tui/ui.ts +0 -166
- package/src/terminal/tui/welcome.ts +0 -102
- package/src/tools/askUser.test.ts +0 -69
- package/src/tools/askUser.ts +0 -59
- package/src/tools/definitions.ts +0 -187
- package/src/tools/maintenance.ts +0 -24
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- package/src/tools/registryWrite.test.ts +0 -101
- package/src/tools/registryWrite.ts +0 -182
- package/src/tools/spawnSubagent.ts +0 -92
- package/src/tools/types.ts +0 -39
- package/src/web/safeRequest.ts +0 -171
- package/src/web/text.ts +0 -53
- package/src/web/tools.ts +0 -134
- package/src/web/web.test.ts +0 -141
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
// Managed git worktrees (PROP-0016). A session can work in its own checkout of a repository so its
|
|
2
|
+
// edits never touch the person's main checkout: managed worktrees are disposable and pruned to the
|
|
3
|
+
// most recent few, permanent ones stay until a person removes them, and nothing with work in it is
|
|
4
|
+
// removed without a snapshot it can be restored from.
|
|
5
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
6
|
+
import { existsSync, mkdirSync, realpathSync, rmSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
9
|
+
import { HarnessError, invalid, notFound } from "../core/errors.js";
|
|
10
|
+
import { newId, now, readJsonOr, writeJson } from "../core/fs.js";
|
|
11
|
+
import { copyFiles, ignoredFiles, includedFiles, untrackedFiles } from "./copy.js";
|
|
12
|
+
import { git, gitText, nulList } from "./git.js";
|
|
13
|
+
import { runSetup, SetupTrust } from "./setup.js";
|
|
14
|
+
const NAME = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
15
|
+
const SNAPSHOT_REF = 'refs/xharness/snapshots';
|
|
16
|
+
const IDENTITY = { GIT_AUTHOR_NAME: 'Xingular Harness', GIT_AUTHOR_EMAIL: 'harness@localhost', GIT_COMMITTER_NAME: 'Xingular Harness', GIT_COMMITTER_EMAIL: 'harness@localhost' };
|
|
17
|
+
function alive(pid) {
|
|
18
|
+
try {
|
|
19
|
+
process.kill(pid, 0);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return error.code === 'EPERM';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function real(path) {
|
|
27
|
+
try {
|
|
28
|
+
return realpathSync(path);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return resolve(path);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function slug(text) {
|
|
35
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'repo';
|
|
36
|
+
}
|
|
37
|
+
/** Paths in `git status --porcelain=v1 -z` output; a rename's original path is not counted. */
|
|
38
|
+
function statusPaths(output) {
|
|
39
|
+
const items = nulList(output);
|
|
40
|
+
const paths = [];
|
|
41
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
42
|
+
const entry = items[index];
|
|
43
|
+
paths.push(entry.slice(3));
|
|
44
|
+
if (entry[0] === 'R' || entry[0] === 'C')
|
|
45
|
+
index += 1;
|
|
46
|
+
}
|
|
47
|
+
return paths;
|
|
48
|
+
}
|
|
49
|
+
export class Worktrees {
|
|
50
|
+
root;
|
|
51
|
+
branchPrefix;
|
|
52
|
+
keep;
|
|
53
|
+
registryFile;
|
|
54
|
+
trust;
|
|
55
|
+
leaseCounts = new Map();
|
|
56
|
+
chain = Promise.resolve();
|
|
57
|
+
constructor(stateDir, options = {}) {
|
|
58
|
+
this.root = options.root ?? join(stateDir, 'worktrees');
|
|
59
|
+
this.branchPrefix = options.branchPrefix ?? 'wt/';
|
|
60
|
+
this.keep = options.keep ?? 15;
|
|
61
|
+
this.registryFile = join(stateDir, 'worktrees.json');
|
|
62
|
+
this.trust = new SetupTrust(join(stateDir, 'worktree-setup-trust.json'));
|
|
63
|
+
this.recover(true);
|
|
64
|
+
}
|
|
65
|
+
// ---- Registry -------------------------------------------------------------------------------
|
|
66
|
+
read() {
|
|
67
|
+
const registry = readJsonOr(this.registryFile, { version: 1, worktrees: [] });
|
|
68
|
+
return Array.isArray(registry.worktrees) ? registry : { version: 1, worktrees: [] };
|
|
69
|
+
}
|
|
70
|
+
write(registry) {
|
|
71
|
+
writeJson(this.registryFile, registry);
|
|
72
|
+
}
|
|
73
|
+
update(id, change) {
|
|
74
|
+
const registry = this.read();
|
|
75
|
+
const record = registry.worktrees.find((entry) => entry.id === id);
|
|
76
|
+
if (!record)
|
|
77
|
+
throw notFound(`Worktree ${id}`);
|
|
78
|
+
change(record);
|
|
79
|
+
this.write(registry);
|
|
80
|
+
return record;
|
|
81
|
+
}
|
|
82
|
+
/** Runs changes one at a time, so two requests never race on the registry or on git. */
|
|
83
|
+
serial(work) {
|
|
84
|
+
const next = this.chain.then(work, work);
|
|
85
|
+
this.chain = next.catch(() => undefined);
|
|
86
|
+
return next;
|
|
87
|
+
}
|
|
88
|
+
/** Releases leases whose process is gone. At start-up, this process's own leases are stale too. */
|
|
89
|
+
recover(startup = false) {
|
|
90
|
+
const registry = this.read();
|
|
91
|
+
const released = [];
|
|
92
|
+
for (const record of registry.worktrees) {
|
|
93
|
+
if (!record.lease)
|
|
94
|
+
continue;
|
|
95
|
+
if (alive(record.lease.pid) && !(startup && record.lease.pid === process.pid))
|
|
96
|
+
continue;
|
|
97
|
+
record.lease = null;
|
|
98
|
+
this.leaseCounts.delete(record.id);
|
|
99
|
+
released.push(record.id);
|
|
100
|
+
if (existsSync(record.path))
|
|
101
|
+
void git(record.path, ['worktree', 'unlock', record.path], { allowFail: true }).catch(() => undefined);
|
|
102
|
+
}
|
|
103
|
+
if (released.length)
|
|
104
|
+
this.write(registry);
|
|
105
|
+
return released;
|
|
106
|
+
}
|
|
107
|
+
get(id) {
|
|
108
|
+
const record = this.read().worktrees.find((entry) => entry.id === id);
|
|
109
|
+
if (!record)
|
|
110
|
+
throw notFound(`Worktree ${id}`);
|
|
111
|
+
return record;
|
|
112
|
+
}
|
|
113
|
+
active(id) {
|
|
114
|
+
const record = this.get(id);
|
|
115
|
+
if (record.removed)
|
|
116
|
+
throw new HarnessError('worktree_removed', `Worktree ${record.name} was removed.`, 'Restore it first.');
|
|
117
|
+
return record;
|
|
118
|
+
}
|
|
119
|
+
/** The folder for a registered worktree; unknown ids remain opaque to the caller. */
|
|
120
|
+
pathOf(id) {
|
|
121
|
+
const record = this.read().worktrees.find((entry) => entry.id === id);
|
|
122
|
+
if (!record)
|
|
123
|
+
return null;
|
|
124
|
+
if (record.removed)
|
|
125
|
+
throw new HarnessError('worktree_removed', `Worktree ${record.name} was removed.`, 'Restore it first.');
|
|
126
|
+
if (!existsSync(record.path))
|
|
127
|
+
throw new HarnessError('worktree_missing', `The folder of worktree ${record.name} is gone.`, 'Remove it, or restore it.');
|
|
128
|
+
return record.path;
|
|
129
|
+
}
|
|
130
|
+
/** The top folder of the repository a folder belongs to (the main checkout, for a linked worktree). */
|
|
131
|
+
async repositoryOf(folder) {
|
|
132
|
+
const known = this.read().worktrees.find((entry) => !entry.removed && real(entry.path) === real(folder));
|
|
133
|
+
if (known)
|
|
134
|
+
return known.repoRoot;
|
|
135
|
+
const top = await git(folder, ['rev-parse', '--show-toplevel'], { allowFail: true });
|
|
136
|
+
if (top.code !== 0)
|
|
137
|
+
throw new HarnessError('not_a_repository', `${folder} is not inside a git repository.`, 'Worktrees need a git repository with at least one commit.');
|
|
138
|
+
const gitDir = await gitText(folder, ['rev-parse', '--absolute-git-dir']);
|
|
139
|
+
const common = resolve(top.stdout.trim(), await gitText(folder, ['rev-parse', '--git-common-dir']));
|
|
140
|
+
if (real(gitDir) === real(common))
|
|
141
|
+
return real(top.stdout.trim());
|
|
142
|
+
const main = (await gitText(folder, ['worktree', 'list', '--porcelain'])).split('\n').find((line) => line.startsWith('worktree '));
|
|
143
|
+
return real(main ? main.slice('worktree '.length) : top.stdout.trim());
|
|
144
|
+
}
|
|
145
|
+
async list(repo) {
|
|
146
|
+
const root = repo ? await this.repositoryOf(repo) : null;
|
|
147
|
+
return this.read().worktrees.filter((record) => !record.removed && (!root || record.repoRoot === root));
|
|
148
|
+
}
|
|
149
|
+
/** Worktrees removed with a snapshot, which `restore` can bring back. */
|
|
150
|
+
removed(repo) {
|
|
151
|
+
return this.read().worktrees.filter((record) => record.removed && (!repo || record.repoRoot === real(repo)));
|
|
152
|
+
}
|
|
153
|
+
async status(id) {
|
|
154
|
+
const record = this.get(id);
|
|
155
|
+
if (record.removed || !existsSync(record.path))
|
|
156
|
+
return { record, exists: false, head: null, changes: 0, ahead: 0 };
|
|
157
|
+
const head = (await git(record.path, ['rev-parse', 'HEAD'], { allowFail: true })).stdout.trim() || null;
|
|
158
|
+
const changes = statusPaths((await git(record.path, ['status', '--porcelain=v1', '-z', '--untracked-files=all'])).stdout).length;
|
|
159
|
+
const ahead = head ? Number((await git(record.path, ['rev-list', '--count', `${record.baseCommit}..HEAD`], { allowFail: true })).stdout.trim() || 0) : 0;
|
|
160
|
+
return { record, exists: true, head, changes, ahead };
|
|
161
|
+
}
|
|
162
|
+
// ---- Create and adopt -----------------------------------------------------------------------
|
|
163
|
+
repoKey(repoRoot) {
|
|
164
|
+
return `${slug(basename(repoRoot))}-${createHash('sha256').update(repoRoot).digest('hex').slice(0, 8)}`;
|
|
165
|
+
}
|
|
166
|
+
uniqueName(repoRoot, wanted) {
|
|
167
|
+
const taken = new Set(this.read().worktrees.filter((record) => record.repoRoot === repoRoot && !record.removed).map((record) => record.name));
|
|
168
|
+
const folder = join(this.root, this.repoKey(repoRoot));
|
|
169
|
+
if (wanted !== undefined) {
|
|
170
|
+
if (!NAME.test(wanted))
|
|
171
|
+
throw invalid('A worktree name uses lowercase letters, digits and dashes (at most 63).');
|
|
172
|
+
if (taken.has(wanted) || existsSync(join(folder, wanted)))
|
|
173
|
+
throw new HarnessError('worktree_exists', `A worktree named ${wanted} already exists.`);
|
|
174
|
+
return wanted;
|
|
175
|
+
}
|
|
176
|
+
for (;;) {
|
|
177
|
+
const name = `wt-${randomBytes(3).toString('hex')}`;
|
|
178
|
+
if (!taken.has(name) && !existsSync(join(folder, name)))
|
|
179
|
+
return name;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async branchName(repoRoot, name) {
|
|
183
|
+
const full = `${this.branchPrefix}${name}`;
|
|
184
|
+
const check = await git(repoRoot, ['check-ref-format', '--branch', full], { allowFail: true });
|
|
185
|
+
if (check.code !== 0)
|
|
186
|
+
throw invalid(`${full} is not a valid branch name.`);
|
|
187
|
+
if ((await git(repoRoot, ['rev-parse', '--verify', '-q', `refs/heads/${full}`], { allowFail: true })).code === 0)
|
|
188
|
+
throw new HarnessError('branch_exists', `Branch ${full} already exists.`);
|
|
189
|
+
return full;
|
|
190
|
+
}
|
|
191
|
+
create(input) {
|
|
192
|
+
return this.serial(async () => {
|
|
193
|
+
if (!input?.repo)
|
|
194
|
+
throw invalid('A repository folder is required.');
|
|
195
|
+
const repoRoot = await this.repositoryOf(input.repo);
|
|
196
|
+
if ((await git(repoRoot, ['rev-parse', '--verify', '-q', 'HEAD^{commit}'], { allowFail: true })).code !== 0) {
|
|
197
|
+
throw new HarnessError('repository_empty', 'This repository has no commit yet, so a worktree cannot be made from it.');
|
|
198
|
+
}
|
|
199
|
+
const name = this.uniqueName(repoRoot, input.name);
|
|
200
|
+
const baseRef = input.baseRef || 'HEAD';
|
|
201
|
+
const baseCommit = (await git(repoRoot, ['rev-parse', '--verify', '-q', `${baseRef}^{commit}`], { allowFail: true })).stdout.trim();
|
|
202
|
+
if (!baseCommit)
|
|
203
|
+
throw invalid(`${baseRef} does not name a commit.`);
|
|
204
|
+
const branch = input.branch ? await this.branchName(repoRoot, input.branch) : null;
|
|
205
|
+
const path = join(this.root, this.repoKey(repoRoot), name);
|
|
206
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
207
|
+
await git(repoRoot, ['worktree', 'add', ...(branch ? ['-b', branch] : ['--detach']), path, baseCommit]);
|
|
208
|
+
const at = now();
|
|
209
|
+
const record = {
|
|
210
|
+
id: newId(), repoRoot, path, name, branch, baseRef, baseCommit, kind: input.kind ?? 'managed', origin: 'created',
|
|
211
|
+
createdAt: at, lastUsedAt: at, lease: null, removed: null
|
|
212
|
+
};
|
|
213
|
+
const registry = this.read();
|
|
214
|
+
registry.worktrees.push(record);
|
|
215
|
+
this.write(registry);
|
|
216
|
+
let patched = false;
|
|
217
|
+
let carried = { copied: [], skipped: [] };
|
|
218
|
+
if (input.carryChanges ?? baseRef === 'HEAD') {
|
|
219
|
+
const patch = (await git(repoRoot, ['diff', '--binary', 'HEAD'])).stdout;
|
|
220
|
+
if (patch.trim())
|
|
221
|
+
patched = (await git(path, ['apply', '--binary', '--whitespace=nowarn'], { input: patch, allowFail: true })).code === 0;
|
|
222
|
+
carried = copyFiles(repoRoot, path, await untrackedFiles(repoRoot));
|
|
223
|
+
}
|
|
224
|
+
const included = copyFiles(repoRoot, path, await includedFiles(repoRoot));
|
|
225
|
+
const setup = input.setup === false
|
|
226
|
+
? { state: 'none', hash: null, log: null }
|
|
227
|
+
: await runSetup({ checkout: path, source: repoRoot, name, repoRoot, logFile: join(this.root, 'logs', `${record.id}.setup.log`), trust: this.trust });
|
|
228
|
+
return { worktree: record, carried: { ...carried, patched }, included, setup };
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
adopt(path) {
|
|
232
|
+
return this.serial(async () => {
|
|
233
|
+
if (!path || !isAbsolute(path))
|
|
234
|
+
throw invalid('An absolute worktree folder is required.');
|
|
235
|
+
const folder = real(path);
|
|
236
|
+
const known = this.read().worktrees.find((record) => !record.removed && real(record.path) === folder);
|
|
237
|
+
if (known)
|
|
238
|
+
return known;
|
|
239
|
+
const top = await git(folder, ['rev-parse', '--show-toplevel'], { allowFail: true });
|
|
240
|
+
if (top.code !== 0 || real(top.stdout.trim()) !== folder)
|
|
241
|
+
throw new HarnessError('not_a_worktree', `${path} is not the top folder of a git worktree.`);
|
|
242
|
+
const repoRoot = await this.repositoryOf(folder);
|
|
243
|
+
if (repoRoot === folder)
|
|
244
|
+
throw new HarnessError('not_a_worktree', `${path} is a repository's main checkout, not a linked worktree.`);
|
|
245
|
+
const branch = (await git(folder, ['symbolic-ref', '-q', '--short', 'HEAD'], { allowFail: true })).stdout.trim() || null;
|
|
246
|
+
const head = await gitText(folder, ['rev-parse', 'HEAD']);
|
|
247
|
+
const taken = new Set(this.read().worktrees.filter((record) => record.repoRoot === repoRoot && !record.removed).map((record) => record.name));
|
|
248
|
+
let name = slug(basename(folder)).slice(0, 56) || 'adopted';
|
|
249
|
+
if (!/^[a-z0-9]/.test(name))
|
|
250
|
+
name = `w-${name}`;
|
|
251
|
+
for (let suffix = 2; taken.has(name); suffix += 1)
|
|
252
|
+
name = `${name.replace(/-\d+$/, '')}-${suffix}`;
|
|
253
|
+
const at = now();
|
|
254
|
+
const record = {
|
|
255
|
+
id: newId(), repoRoot, path: folder, name, branch, baseRef: 'HEAD', baseCommit: head, kind: 'permanent', origin: 'adopted',
|
|
256
|
+
createdAt: at, lastUsedAt: at, lease: null, removed: null
|
|
257
|
+
};
|
|
258
|
+
const registry = this.read();
|
|
259
|
+
registry.worktrees.push(record);
|
|
260
|
+
this.write(registry);
|
|
261
|
+
return record;
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// ---- Use ------------------------------------------------------------------------------------
|
|
265
|
+
/** A session starts using a worktree. Another live session's lease refuses it. */
|
|
266
|
+
acquire(id, holder) {
|
|
267
|
+
return this.serial(async () => {
|
|
268
|
+
const record = this.active(id);
|
|
269
|
+
if (!existsSync(record.path))
|
|
270
|
+
throw new HarnessError('worktree_missing', `The folder of worktree ${record.name} is gone.`, 'Remove it, or restore it.');
|
|
271
|
+
if (record.lease && record.lease.holder !== holder && alive(record.lease.pid)) {
|
|
272
|
+
throw new HarnessError('worktree_in_use', `Worktree ${record.name} is in use by another session.`);
|
|
273
|
+
}
|
|
274
|
+
const sameLiveLease = record.lease?.holder === holder && alive(record.lease.pid);
|
|
275
|
+
const previousCount = sameLiveLease ? this.leaseCounts.get(id) ?? 1 : 0;
|
|
276
|
+
const updated = this.update(id, (entry) => {
|
|
277
|
+
entry.lease = { holder, pid: process.pid, at: now() };
|
|
278
|
+
entry.lastUsedAt = now();
|
|
279
|
+
});
|
|
280
|
+
this.leaseCounts.set(id, previousCount + 1);
|
|
281
|
+
try {
|
|
282
|
+
await git(record.path, ['worktree', 'lock', '--reason', 'in use by a Xingular Harness session', record.path], { allowFail: true });
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (previousCount)
|
|
286
|
+
this.leaseCounts.set(id, previousCount);
|
|
287
|
+
else {
|
|
288
|
+
this.leaseCounts.delete(id);
|
|
289
|
+
this.update(id, (entry) => { entry.lease = null; });
|
|
290
|
+
}
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
return updated;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
release(id, holder) {
|
|
297
|
+
return this.serial(async () => {
|
|
298
|
+
const record = this.read().worktrees.find((entry) => entry.id === id);
|
|
299
|
+
if (!record?.lease || record.lease.holder !== holder)
|
|
300
|
+
return;
|
|
301
|
+
const count = this.leaseCounts.get(id) ?? 1;
|
|
302
|
+
if (count > 1) {
|
|
303
|
+
this.leaseCounts.set(id, count - 1);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.leaseCounts.delete(id);
|
|
307
|
+
this.update(id, (entry) => { entry.lease = null; });
|
|
308
|
+
if (existsSync(record.path))
|
|
309
|
+
await git(record.path, ['worktree', 'unlock', record.path], { allowFail: true });
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
branch(id, name) {
|
|
313
|
+
return this.serial(async () => {
|
|
314
|
+
const record = this.active(id);
|
|
315
|
+
if (record.branch)
|
|
316
|
+
throw new HarnessError('branch_exists', `Worktree ${record.name} is already on branch ${record.branch}.`);
|
|
317
|
+
const full = await this.branchName(record.repoRoot, name);
|
|
318
|
+
await git(record.path, ['switch', '-c', full]);
|
|
319
|
+
return this.update(id, (entry) => { entry.branch = full; });
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// ---- Remove, restore, prune -----------------------------------------------------------------
|
|
323
|
+
async reachable(checkout, commit) {
|
|
324
|
+
const refs = await git(checkout, ['for-each-ref', '--count=1', `--contains=${commit}`, 'refs/heads', 'refs/tags', 'refs/remotes'], { allowFail: true });
|
|
325
|
+
return refs.code === 0 && refs.stdout.trim().length > 0;
|
|
326
|
+
}
|
|
327
|
+
/** Records the worktree's commits and working changes under a snapshot ref. */
|
|
328
|
+
async snapshot(record, ignored) {
|
|
329
|
+
const index = join(tmpdir(), `xharness-index-${record.id}-${randomBytes(4).toString('hex')}`);
|
|
330
|
+
const env = { ...IDENTITY, GIT_INDEX_FILE: index };
|
|
331
|
+
try {
|
|
332
|
+
await git(record.path, ['read-tree', 'HEAD'], { env });
|
|
333
|
+
await git(record.path, ['add', '-A'], { env });
|
|
334
|
+
if (ignored.length) {
|
|
335
|
+
const pathspecs = Buffer.from(ignored.map((path) => `:(literal)${path}\0`).join(''));
|
|
336
|
+
await git(record.path, ['add', '--force', '--pathspec-from-file=-', '--pathspec-file-nul'], { env, input: pathspecs });
|
|
337
|
+
}
|
|
338
|
+
const tree = await gitText(record.path, ['write-tree'], { env });
|
|
339
|
+
const commit = await gitText(record.path, ['commit-tree', tree, '-p', 'HEAD', '-m', `Xingular Harness snapshot of worktree ${record.name}`], { env });
|
|
340
|
+
const ref = `${SNAPSHOT_REF}/${record.id}`;
|
|
341
|
+
await git(record.path, ['update-ref', ref, commit]);
|
|
342
|
+
return ref;
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
rmSync(index, { force: true });
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
remove(id, options = {}) {
|
|
349
|
+
return this.serial(() => this.removeNow(id, options.force === true));
|
|
350
|
+
}
|
|
351
|
+
async removeNow(id, force) {
|
|
352
|
+
const record = this.active(id);
|
|
353
|
+
// Several sessions share one process in an embedding host, so a lease of a live process always counts.
|
|
354
|
+
if (record.lease && alive(record.lease.pid)) {
|
|
355
|
+
throw new HarnessError('worktree_in_use', `Worktree ${record.name} is in use by a session.`, 'End that session first.');
|
|
356
|
+
}
|
|
357
|
+
let head = record.baseCommit;
|
|
358
|
+
let snapshot = null;
|
|
359
|
+
if (existsSync(record.path)) {
|
|
360
|
+
const ignored = await ignoredFiles(record.path);
|
|
361
|
+
const status = await this.status(id);
|
|
362
|
+
head = status.head ?? head;
|
|
363
|
+
const work = status.changes > 0 || ignored.length > 0 || !(await this.reachable(record.path, head));
|
|
364
|
+
if (work && !force) {
|
|
365
|
+
throw new HarnessError('worktree_has_work', `Worktree ${record.name} has ${status.changes} changed path(s), ${ignored.length} ignored file(s), or commits on no branch.`, 'Remove it with force to keep a snapshot you can restore.');
|
|
366
|
+
}
|
|
367
|
+
if (work)
|
|
368
|
+
snapshot = await this.snapshot(record, ignored);
|
|
369
|
+
await git(record.repoRoot, ['worktree', 'unlock', record.path], { allowFail: true });
|
|
370
|
+
const removed = await git(record.repoRoot, ['worktree', 'remove', '--force', record.path], { allowFail: true });
|
|
371
|
+
if (removed.code !== 0 && existsSync(record.path) && record.path.startsWith(this.root + '/'))
|
|
372
|
+
rmSync(record.path, { recursive: true, force: true });
|
|
373
|
+
}
|
|
374
|
+
await git(record.repoRoot, ['worktree', 'prune'], { allowFail: true });
|
|
375
|
+
const worktree = this.update(id, (entry) => {
|
|
376
|
+
entry.lease = null;
|
|
377
|
+
entry.removed = { at: now(), head, snapshot };
|
|
378
|
+
});
|
|
379
|
+
this.leaseCounts.delete(id);
|
|
380
|
+
return { worktree, snapshot };
|
|
381
|
+
}
|
|
382
|
+
restore(id) {
|
|
383
|
+
return this.serial(async () => {
|
|
384
|
+
const record = this.get(id);
|
|
385
|
+
if (!record.removed)
|
|
386
|
+
throw new HarnessError('worktree_active', `Worktree ${record.name} was not removed.`);
|
|
387
|
+
if (existsSync(record.path))
|
|
388
|
+
throw new HarnessError('worktree_exists', `${record.path} already exists.`);
|
|
389
|
+
const { snapshot, head } = record.removed;
|
|
390
|
+
const start = snapshot ? await gitText(record.repoRoot, ['rev-parse', `${snapshot}^`]) : head;
|
|
391
|
+
const onBranch = record.branch && (await git(record.repoRoot, ['rev-parse', '--verify', '-q', `refs/heads/${record.branch}`], { allowFail: true })).stdout.trim() === start;
|
|
392
|
+
mkdirSync(dirname(record.path), { recursive: true });
|
|
393
|
+
await git(record.repoRoot, ['worktree', 'add', ...(onBranch ? [record.path, record.branch] : ['--detach', record.path, start])]);
|
|
394
|
+
if (snapshot) {
|
|
395
|
+
await git(record.path, ['read-tree', '-u', '--reset', snapshot]);
|
|
396
|
+
await git(record.path, ['reset', '-q']);
|
|
397
|
+
}
|
|
398
|
+
return this.update(id, (entry) => {
|
|
399
|
+
entry.removed = null;
|
|
400
|
+
entry.branch = onBranch ? entry.branch : null;
|
|
401
|
+
entry.lastUsedAt = now();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
/** Keeps the most recently used managed worktrees of each repository and removes the rest, with snapshots. */
|
|
406
|
+
prune(options = {}) {
|
|
407
|
+
return this.serial(async () => {
|
|
408
|
+
const keep = Math.max(0, options.keep ?? this.keep);
|
|
409
|
+
const root = options.repo ? await this.repositoryOf(options.repo) : null;
|
|
410
|
+
const groups = new Map();
|
|
411
|
+
for (const record of this.read().worktrees) {
|
|
412
|
+
if (record.removed || record.kind !== 'managed' || (root && record.repoRoot !== root))
|
|
413
|
+
continue;
|
|
414
|
+
groups.set(record.repoRoot, [...(groups.get(record.repoRoot) ?? []), record]);
|
|
415
|
+
}
|
|
416
|
+
const removed = [];
|
|
417
|
+
const failed = [];
|
|
418
|
+
for (const records of groups.values()) {
|
|
419
|
+
const older = records.sort((left, right) => right.lastUsedAt.localeCompare(left.lastUsedAt)).slice(keep);
|
|
420
|
+
for (const record of older) {
|
|
421
|
+
if (record.lease && alive(record.lease.pid))
|
|
422
|
+
continue;
|
|
423
|
+
try {
|
|
424
|
+
removed.push(await this.removeNow(record.id, true));
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
failed.push({ id: record.id, error: error instanceof Error ? error.message : String(error) });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { removed, failed };
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
// ---- Handoff --------------------------------------------------------------------------------
|
|
435
|
+
/**
|
|
436
|
+
* Moves work between the worktree and the main checkout. `toSource` brings the worktree's changes
|
|
437
|
+
* since it started (commits and working changes) to the main checkout; `toWorktree` brings the
|
|
438
|
+
* main checkout's uncommitted changes to the worktree. The target must have no tracked changes,
|
|
439
|
+
* and nothing changes when the patch does not apply. With `move` (the default), what was handed
|
|
440
|
+
* off is stashed away from the origin, where `git stash` can bring it back.
|
|
441
|
+
*/
|
|
442
|
+
handoff(id, direction, options = {}) {
|
|
443
|
+
return this.serial(async () => {
|
|
444
|
+
if (direction !== 'toSource' && direction !== 'toWorktree')
|
|
445
|
+
throw invalid('direction is toSource or toWorktree.');
|
|
446
|
+
const record = this.active(id);
|
|
447
|
+
if (!existsSync(record.path))
|
|
448
|
+
throw new HarnessError('worktree_missing', `The folder of worktree ${record.name} is gone.`);
|
|
449
|
+
const origin = direction === 'toSource' ? record.path : record.repoRoot;
|
|
450
|
+
const target = direction === 'toSource' ? record.repoRoot : record.path;
|
|
451
|
+
const dirty = (await gitText(target, ['status', '--porcelain', '--untracked-files=no'])).length > 0;
|
|
452
|
+
if (dirty)
|
|
453
|
+
throw new HarnessError('handoff_target_dirty', `The ${direction === 'toSource' ? 'main checkout' : 'worktree'} has uncommitted changes.`, 'Commit or stash them, then hand off again.');
|
|
454
|
+
const patch = (await git(origin, ['diff', '--binary', direction === 'toSource' ? record.baseCommit : 'HEAD'])).stdout;
|
|
455
|
+
const untracked = await untrackedFiles(origin);
|
|
456
|
+
const collisions = untracked.filter((path) => existsSync(join(target, path)));
|
|
457
|
+
if (collisions.length)
|
|
458
|
+
throw new HarnessError('handoff_conflict', `${collisions.length} untracked file(s) already exist in the target, for example ${collisions[0]}.`);
|
|
459
|
+
if (patch.trim()) {
|
|
460
|
+
const check = await git(target, ['apply', '--check', '--binary', '--whitespace=nowarn'], { input: patch, allowFail: true });
|
|
461
|
+
if (check.code !== 0)
|
|
462
|
+
throw new HarnessError('handoff_conflict', `The changes do not apply cleanly: ${check.stderr.trim()}`, 'Bring the two checkouts to the same commit, or commit and merge instead.');
|
|
463
|
+
await git(target, ['apply', '--binary', '--whitespace=nowarn'], { input: patch });
|
|
464
|
+
}
|
|
465
|
+
const copied = copyFiles(origin, target, untracked).copied;
|
|
466
|
+
let stash = null;
|
|
467
|
+
if (options.move !== false && (patch.trim() || untracked.length)) {
|
|
468
|
+
const before = (await git(origin, ['rev-parse', '-q', '--verify', 'refs/stash'], { allowFail: true })).stdout.trim();
|
|
469
|
+
await git(origin, ['stash', 'push', '--include-untracked', '-m', `Xingular Harness handoff of worktree ${record.name}`], { env: IDENTITY, allowFail: true });
|
|
470
|
+
const after = (await git(origin, ['rev-parse', '-q', '--verify', 'refs/stash'], { allowFail: true })).stdout.trim();
|
|
471
|
+
stash = after && after !== before ? after : null;
|
|
472
|
+
}
|
|
473
|
+
this.update(id, (entry) => { entry.lastUsedAt = now(); });
|
|
474
|
+
return { direction, patched: patch.trim().length > 0, copied, stash };
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
// ---- Setup trust ----------------------------------------------------------------------------
|
|
478
|
+
async trustSetup(repo, hash) {
|
|
479
|
+
if (!/^[0-9a-f]{64}$/.test(hash))
|
|
480
|
+
throw invalid('A setup script hash is 64 hexadecimal characters.');
|
|
481
|
+
this.trust.trust(await this.repositoryOf(repo), hash);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SetupReport } from "./types.js";
|
|
2
|
+
export declare const SETUP_SCRIPT = ".xharness/worktree-setup";
|
|
3
|
+
export declare function scriptHash(text: string | Buffer): string;
|
|
4
|
+
export declare class SetupTrust {
|
|
5
|
+
private readonly file;
|
|
6
|
+
constructor(file: string);
|
|
7
|
+
private read;
|
|
8
|
+
trusted(repoRoot: string, hash: string): boolean;
|
|
9
|
+
trust(repoRoot: string, hash: string): void;
|
|
10
|
+
}
|
|
11
|
+
/** The setup script a checkout carries, with its hash, or null. */
|
|
12
|
+
export declare function setupScript(checkout: string): {
|
|
13
|
+
path: string;
|
|
14
|
+
hash: string;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function runSetup(input: {
|
|
17
|
+
checkout: string;
|
|
18
|
+
source: string;
|
|
19
|
+
name: string;
|
|
20
|
+
repoRoot: string;
|
|
21
|
+
logFile: string;
|
|
22
|
+
trust: SetupTrust;
|
|
23
|
+
}): Promise<SetupReport>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// A repository's setup script runs in each new worktree (installing dependencies, say), but only
|
|
2
|
+
// once a person trusted its exact text. The hash is the trust; a changed script asks again.
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { readJsonOr, writeJson } from "../core/fs.js";
|
|
8
|
+
export const SETUP_SCRIPT = '.xharness/worktree-setup';
|
|
9
|
+
const TIMEOUT_MS = 10 * 60 * 1000;
|
|
10
|
+
export function scriptHash(text) {
|
|
11
|
+
return createHash('sha256').update(text).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
export class SetupTrust {
|
|
14
|
+
file;
|
|
15
|
+
constructor(file) {
|
|
16
|
+
this.file = file;
|
|
17
|
+
}
|
|
18
|
+
read() {
|
|
19
|
+
return readJsonOr(this.file, {});
|
|
20
|
+
}
|
|
21
|
+
trusted(repoRoot, hash) {
|
|
22
|
+
return this.read()[repoRoot]?.includes(hash) ?? false;
|
|
23
|
+
}
|
|
24
|
+
trust(repoRoot, hash) {
|
|
25
|
+
const all = this.read();
|
|
26
|
+
all[repoRoot] = [...new Set([...(all[repoRoot] ?? []), hash])];
|
|
27
|
+
writeJson(this.file, all);
|
|
28
|
+
chmodSync(this.file, 0o600);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** The setup script a checkout carries, with its hash, or null. */
|
|
32
|
+
export function setupScript(checkout) {
|
|
33
|
+
const path = join(checkout, SETUP_SCRIPT);
|
|
34
|
+
if (!existsSync(path) || !statSync(path).isFile())
|
|
35
|
+
return null;
|
|
36
|
+
return { path, hash: scriptHash(readFileSync(path)) };
|
|
37
|
+
}
|
|
38
|
+
export function runSetup(input) {
|
|
39
|
+
const script = setupScript(input.checkout);
|
|
40
|
+
if (!script)
|
|
41
|
+
return Promise.resolve({ state: 'none', hash: null, log: null });
|
|
42
|
+
if (!input.trust.trusted(input.repoRoot, script.hash))
|
|
43
|
+
return Promise.resolve({ state: 'untrusted', hash: script.hash, log: null });
|
|
44
|
+
mkdirSync(dirname(input.logFile), { recursive: true });
|
|
45
|
+
const log = openSync(input.logFile, 'w');
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const child = spawn('/bin/sh', [script.path], {
|
|
48
|
+
cwd: input.checkout,
|
|
49
|
+
env: { ...process.env, HARNESS_WORKTREE_PATH: input.checkout, HARNESS_WORKTREE_SOURCE: input.source, HARNESS_WORKTREE_NAME: input.name },
|
|
50
|
+
stdio: ['ignore', log, log]
|
|
51
|
+
});
|
|
52
|
+
const timer = setTimeout(() => child.kill('SIGTERM'), TIMEOUT_MS);
|
|
53
|
+
let settled = false;
|
|
54
|
+
const finish = (exitCode) => {
|
|
55
|
+
if (settled)
|
|
56
|
+
return;
|
|
57
|
+
settled = true;
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
closeSync(log);
|
|
60
|
+
resolve({ state: exitCode === 0 ? 'ran' : 'failed', hash: script.hash, log: input.logFile, exitCode });
|
|
61
|
+
};
|
|
62
|
+
child.once('error', () => finish(null));
|
|
63
|
+
child.once('close', (code) => finish(code));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** A managed worktree is disposable and pruned; a permanent one is kept until a person removes it. */
|
|
2
|
+
export type WorktreeKind = 'managed' | 'permanent';
|
|
3
|
+
export interface WorktreeLease {
|
|
4
|
+
/** Who uses the worktree: a session id. */
|
|
5
|
+
holder: string;
|
|
6
|
+
/** The process that holds the lease; a lease whose process is gone is released on recovery. */
|
|
7
|
+
pid: number;
|
|
8
|
+
at: string;
|
|
9
|
+
}
|
|
10
|
+
export interface WorktreeRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
/** The source checkout's top folder. */
|
|
13
|
+
repoRoot: string;
|
|
14
|
+
path: string;
|
|
15
|
+
name: string;
|
|
16
|
+
/** Null while the worktree is on a detached HEAD. */
|
|
17
|
+
branch: string | null;
|
|
18
|
+
baseRef: string;
|
|
19
|
+
baseCommit: string;
|
|
20
|
+
kind: WorktreeKind;
|
|
21
|
+
origin: 'created' | 'adopted';
|
|
22
|
+
createdAt: string;
|
|
23
|
+
lastUsedAt: string;
|
|
24
|
+
lease: WorktreeLease | null;
|
|
25
|
+
/** Set once the checkout is removed; the record stays so the worktree can be restored. */
|
|
26
|
+
removed: {
|
|
27
|
+
at: string;
|
|
28
|
+
head: string;
|
|
29
|
+
snapshot: string | null;
|
|
30
|
+
} | null;
|
|
31
|
+
}
|
|
32
|
+
export interface WorktreeStatus {
|
|
33
|
+
record: WorktreeRecord;
|
|
34
|
+
exists: boolean;
|
|
35
|
+
head: string | null;
|
|
36
|
+
/** Changed, added or untracked paths in the working tree. */
|
|
37
|
+
changes: number;
|
|
38
|
+
/** Commits made since the worktree started. */
|
|
39
|
+
ahead: number;
|
|
40
|
+
}
|
|
41
|
+
export interface CopyReport {
|
|
42
|
+
copied: string[];
|
|
43
|
+
skipped: {
|
|
44
|
+
path: string;
|
|
45
|
+
reason: 'exists' | 'symlink' | 'too_large' | 'over_total' | 'not_file';
|
|
46
|
+
}[];
|
|
47
|
+
}
|
|
48
|
+
export interface SetupReport {
|
|
49
|
+
state: 'none' | 'untrusted' | 'ran' | 'failed';
|
|
50
|
+
/** The script's content hash, to trust it with `setup.trust`. */
|
|
51
|
+
hash: string | null;
|
|
52
|
+
log: string | null;
|
|
53
|
+
exitCode?: number | null;
|
|
54
|
+
}
|
|
55
|
+
export interface CreateWorktreeInput {
|
|
56
|
+
/** Any folder inside the source repository. */
|
|
57
|
+
repo: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
/** What the worktree starts from. Defaults to the source checkout's HEAD. */
|
|
60
|
+
baseRef?: string;
|
|
61
|
+
kind?: WorktreeKind;
|
|
62
|
+
/** A branch to create instead of a detached HEAD. The configured prefix is added. */
|
|
63
|
+
branch?: string;
|
|
64
|
+
/** Bring the source checkout's uncommitted changes along (default when starting from HEAD). */
|
|
65
|
+
carryChanges?: boolean;
|
|
66
|
+
/** Run the repository's trusted setup script (default yes). */
|
|
67
|
+
setup?: boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface CreateWorktreeResult {
|
|
70
|
+
worktree: WorktreeRecord;
|
|
71
|
+
carried: CopyReport & {
|
|
72
|
+
patched: boolean;
|
|
73
|
+
};
|
|
74
|
+
included: CopyReport;
|
|
75
|
+
setup: SetupReport;
|
|
76
|
+
}
|
|
77
|
+
export interface HandoffResult {
|
|
78
|
+
direction: 'toSource' | 'toWorktree';
|
|
79
|
+
patched: boolean;
|
|
80
|
+
copied: string[];
|
|
81
|
+
/** The stash that holds what was moved away from the origin, when `move` was asked. */
|
|
82
|
+
stash: string | null;
|
|
83
|
+
}
|
|
84
|
+
export interface WorktreesOptions {
|
|
85
|
+
/** Where checkouts are made. Defaults to `<state dir>/worktrees`. */
|
|
86
|
+
root?: string;
|
|
87
|
+
/** Prefix of branches the harness creates. Defaults to `wt/`. */
|
|
88
|
+
branchPrefix?: string;
|
|
89
|
+
/** Managed worktrees kept per repository by `prune`. Defaults to 15. */
|
|
90
|
+
keep?: number;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|