@xingular/harness 0.1.7 → 1.0.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/CHANGELOG.md +97 -1
- package/LICENSE +202 -0
- package/NOTICE.md +22 -0
- package/README.md +187 -85
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +13701 -13498
- 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/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 +17 -0
- package/dist/sdk/browser/index.js +65 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/session.d.ts +84 -0
- package/dist/sdk/browser/session.js +192 -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 +142 -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/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 +133 -0
- package/dist/sdk/harness.js +299 -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/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/index.d.ts +63 -0
- package/dist/sdk/index.js +48 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +669 -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/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 +6 -0
- package/dist/sdk/plugins/builtin.js +68 -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 +187 -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 +174 -0
- package/dist/sdk/prompt/compile.js +396 -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/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 +370 -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 +534 -0
- package/dist/sdk/sessions/contracts.js +299 -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 +36 -0
- package/dist/sdk/sessions/planMode.js +84 -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 +102 -0
- package/dist/sdk/sessions/registry.js +383 -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 +335 -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 +140 -0
- package/dist/sdk/sessions/sessions.js +394 -0
- package/dist/sdk/sessions/subagentService.d.ts +41 -0
- package/dist/sdk/sessions/subagentService.js +247 -0
- package/dist/sdk/sessions/subagents.d.ts +50 -0
- package/dist/sdk/sessions/subagents.js +68 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -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/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +72 -0
- package/dist/sdk/terminal/controller.js +351 -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/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +392 -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 +70 -0
- package/dist/sdk/terminal/socket.js +256 -0
- package/dist/sdk/terminal/standalone.d.ts +18 -0
- package/dist/sdk/terminal/standalone.js +146 -0
- package/dist/sdk/terminal/terminals.d.ts +176 -0
- package/dist/sdk/terminal/terminals.js +767 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -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 +7 -0
- package/dist/sdk/terminal/tui/commandPages.js +63 -0
- package/dist/sdk/terminal/tui/commands.d.ts +42 -0
- package/dist/sdk/terminal/tui/commands.js +276 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
- package/dist/sdk/terminal/tui/dialogs.js +363 -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 +155 -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 +14 -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 +145 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -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 +37 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -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/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 +55 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -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/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +19 -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 +441 -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 +12 -0
- package/dist/sdk/terminal/tui/settingsModel.js +84 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +197 -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 +47 -0
- package/dist/sdk/terminal/tui/ui.js +148 -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 +74 -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 +6 -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 +49 -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/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/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/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 +65 -0
- package/dist/sdk/world/world.js +80 -0
- package/dist/terminal-opentui.js +13612 -0
- package/dist/terminal-pi.js +808 -151
- package/docs/README.md +22 -0
- package/docs/architecture/00-overview.md +159 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +146 -0
- package/docs/architecture/03-variants.md +95 -0
- package/docs/architecture/04-prompt.md +102 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +93 -0
- package/docs/architecture/07-tools-and-gate.md +88 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +90 -0
- package/docs/architecture/10-server-protocol.md +84 -0
- package/docs/architecture/11-terminal.md +81 -0
- package/docs/architecture/12-storage.md +61 -0
- package/docs/architecture/13-world-tools.md +23 -0
- package/docs/architecture/README.md +114 -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 +68 -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/README.md +76 -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 +38 -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 +21 -0
- package/docs/doctor.md +74 -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 +20 -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/releases/1.0.0.md +40 -0
- package/docs/releases/README.md +36 -0
- package/docs/study/00-index.md +78 -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/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -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/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +473 -56
- 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
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
// Rounded selector chrome follows Oh My Pi 18.11.3 (MIT; see NOTICE-oh-my-pi).
|
|
2
|
-
import type { ExtensionUIContext, ExtensionUIDialogOptions, KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent'
|
|
3
|
-
import { Editor, Input, SelectList, fuzzyFilter, matchesKey, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component, type Focusable, type OverlayHandle, type OverlayOptions, type SelectListTheme, type TUI } from '@earendil-works/pi-tui'
|
|
4
|
-
|
|
5
|
-
type DisposableComponent = Component & { dispose?(): void }
|
|
6
|
-
export function selectTheme(theme: Theme): SelectListTheme {
|
|
7
|
-
return {
|
|
8
|
-
selectedPrefix: text => theme.fg('accent', text), selectedText: text => theme.fg('accent', text.replace(/^→/, '❯')),
|
|
9
|
-
description: text => theme.fg('muted', text), scrollInfo: text => theme.fg('dim', text),
|
|
10
|
-
noMatch: text => theme.fg('warning', text.replace('commands', 'options'))
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/** Input keeps the original value; only its rendered cells are masked. */
|
|
14
|
-
export class SecretInput extends Input {
|
|
15
|
-
override render(width: number): string[] {
|
|
16
|
-
const lines = super.render(width)
|
|
17
|
-
if (!this.getValue()) return lines
|
|
18
|
-
const segments = new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
19
|
-
return lines.map(line => line.split(/(\x1b(?:\[[0-9;?]*[ -/]*[@-~]|_[^\x07]*\x07))/g).map((part, index) => index % 2
|
|
20
|
-
? part
|
|
21
|
-
: [...segments.segment(part)].map(item => '•'.repeat(visibleWidth(item.segment))).join('')).join(''))
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function safe(text: string): string {
|
|
26
|
-
return stripTerminalSequences(text).replace(/[\x00-\x1f\x7f]/g, ' ')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** OMP hook-selector.ts uses type-to-search once its options exceed the list window. */
|
|
30
|
-
export class SearchableSelect implements Component, Focusable {
|
|
31
|
-
private search = new Input({ prompt: '' })
|
|
32
|
-
private list: SelectList
|
|
33
|
-
private query = ''
|
|
34
|
-
private limit = 0
|
|
35
|
-
private items: { value: string; label: string }[]
|
|
36
|
-
private filtered: { value: string; label: string }[] = []
|
|
37
|
-
private focusedValue = false
|
|
38
|
-
private currentTheme: Theme | undefined
|
|
39
|
-
constructor(options: string[], private readonly tui: TUI, private readonly theme: () => Theme, private readonly select: (index: number) => void, private readonly cancel: () => void) {
|
|
40
|
-
this.items = options.map((option, index) => ({ value: String(index), label: safe(option) }))
|
|
41
|
-
this.list = new SelectList([], 1, selectTheme(theme()))
|
|
42
|
-
this.rebuild()
|
|
43
|
-
}
|
|
44
|
-
get focused(): boolean { return this.focusedValue }
|
|
45
|
-
set focused(value: boolean) { this.focusedValue = value; this.search.focused = value && this.searchEnabled() }
|
|
46
|
-
private searchEnabled(): boolean { return this.items.length > this.limit || this.query.length > 0 }
|
|
47
|
-
private rebuild(): void {
|
|
48
|
-
const selected = this.list.getSelectedItem()?.value
|
|
49
|
-
this.limit = Math.min(12, Math.max(1, Math.floor(this.tui.terminal.rows * 0.8) - 7))
|
|
50
|
-
this.currentTheme = this.theme()
|
|
51
|
-
this.filtered = fuzzyFilter(this.items, this.query, item => item.label)
|
|
52
|
-
this.list = new SelectList(this.filtered, this.limit, selectTheme(this.currentTheme))
|
|
53
|
-
if (selected) {
|
|
54
|
-
const index = this.filtered.findIndex(item => item.value === selected)
|
|
55
|
-
if (index >= 0) this.list.setSelectedIndex(index)
|
|
56
|
-
}
|
|
57
|
-
this.list.onSelect = item => this.select(Number(item.value))
|
|
58
|
-
this.list.onCancel = this.cancel
|
|
59
|
-
this.search.focused = this.focusedValue && this.searchEnabled()
|
|
60
|
-
}
|
|
61
|
-
invalidate(): void { this.rebuild(); this.search.invalidate() }
|
|
62
|
-
handleInput(data: string): void {
|
|
63
|
-
if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) { this.cancel(); return }
|
|
64
|
-
if (!this.query && /^[1-9]$/.test(data)) {
|
|
65
|
-
const index = this.items.findIndex(item => item.label.startsWith(`${data}. `))
|
|
66
|
-
if (index >= 0) { this.select(index); return }
|
|
67
|
-
}
|
|
68
|
-
if (matchesKey(data, 'up') || matchesKey(data, 'down') || matchesKey(data, 'enter') || matchesKey(data, 'pageUp') || matchesKey(data, 'pageDown')) {
|
|
69
|
-
this.list.handleInput(data)
|
|
70
|
-
} else if (this.searchEnabled()) {
|
|
71
|
-
this.search.handleInput(data)
|
|
72
|
-
if (this.query !== this.search.getValue()) { this.query = this.search.getValue(); this.rebuild() }
|
|
73
|
-
} else if (data === 'j' || data === 'k') this.list.handleInput(data === 'j' ? '\x1b[B' : '\x1b[A')
|
|
74
|
-
}
|
|
75
|
-
render(width: number): string[] {
|
|
76
|
-
if (this.limit !== Math.min(12, Math.max(1, Math.floor(this.tui.terminal.rows * 0.8) - 7)) || this.currentTheme !== this.theme()) this.rebuild()
|
|
77
|
-
const theme = this.theme()
|
|
78
|
-
const lines = this.list.render(Math.max(4, width)).map(line => {
|
|
79
|
-
const bounded = truncateToWidth(line, width, '')
|
|
80
|
-
return stripTerminalSequences(line).startsWith('❯')
|
|
81
|
-
? theme.bg('selectedBg', bounded + ' '.repeat(Math.max(0, width - visibleWidth(bounded))))
|
|
82
|
-
: bounded
|
|
83
|
-
})
|
|
84
|
-
if (this.searchEnabled()) {
|
|
85
|
-
const selected = this.filtered.findIndex(item => item.value === this.list.getSelectedItem()?.value) + 1
|
|
86
|
-
const count = `(${selected}/${this.filtered.length}${this.query ? ` of ${this.items.length}` : ''})`
|
|
87
|
-
lines.push(this.query ? theme.fg('dim', ` ${count} Search: `) + (this.search.render(Math.max(2, width - count.length - 11))[0] ?? '') : theme.fg('dim', ` ${count} Type to search`))
|
|
88
|
-
}
|
|
89
|
-
return lines.map(line => truncateToWidth(line, width, ''))
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** An overlay owns focus; its input emits the cursor marker through this delegate. */
|
|
94
|
-
export class DialogPanel implements Component, Focusable {
|
|
95
|
-
deadline?: number
|
|
96
|
-
maxRows?: () => number
|
|
97
|
-
private focusedValue = false
|
|
98
|
-
private detailOffset = 0
|
|
99
|
-
private detailRows = 0
|
|
100
|
-
private detailTotal = 0
|
|
101
|
-
constructor(readonly title: string, readonly content: Component, private readonly theme: () => Theme, readonly hint: string, private readonly message?: string) {}
|
|
102
|
-
get focused(): boolean { return this.focusedValue }
|
|
103
|
-
set focused(value: boolean) {
|
|
104
|
-
this.focusedValue = value
|
|
105
|
-
if ('focused' in this.content) (this.content as Component & Focusable).focused = value
|
|
106
|
-
}
|
|
107
|
-
invalidate(): void { this.content.invalidate() }
|
|
108
|
-
handleInput(data: string): void {
|
|
109
|
-
if (this.detailTotal > this.detailRows && (matchesKey(data, 'pageUp') || matchesKey(data, 'pageDown'))) {
|
|
110
|
-
const delta = matchesKey(data, 'pageUp') ? -1 : 1
|
|
111
|
-
this.detailOffset = Math.max(0, Math.min(this.detailTotal - this.detailRows, this.detailOffset + delta * Math.max(1, this.detailRows)))
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
this.content.handleInput?.(data)
|
|
115
|
-
}
|
|
116
|
-
render(width: number): string[] {
|
|
117
|
-
if (width < 1) return []
|
|
118
|
-
const theme = this.theme()
|
|
119
|
-
const inner = Math.max(2, width - 4)
|
|
120
|
-
const cap = (value: string) => truncateToWidth(value, width, '')
|
|
121
|
-
const border = (value: string) => theme.fg('border', value)
|
|
122
|
-
const title = truncateToWidth(` ${safe(this.title)} `, Math.max(0, width - 4), '')
|
|
123
|
-
const top = cap(border('╭─') + theme.bold(theme.fg('accent', title)) + border('─'.repeat(Math.max(0, width - visibleWidth(title) - 3)) + '╮'))
|
|
124
|
-
const bottom = cap(border('╰' + '─'.repeat(Math.max(0, width - 2)) + '╯'))
|
|
125
|
-
const row = (line: string) => {
|
|
126
|
-
const shown = truncateToWidth(line, inner, '')
|
|
127
|
-
return cap(border('│') + ' ' + shown + ' '.repeat(Math.max(0, width - visibleWidth(shown) - 3)) + border('│'))
|
|
128
|
-
}
|
|
129
|
-
const details = this.message
|
|
130
|
-
? wrapTextWithAnsi(stripTerminalSequences(this.message).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, '').replace(/\t/g, ' '), inner)
|
|
131
|
-
: []
|
|
132
|
-
const content = this.content.render(inner)
|
|
133
|
-
const height = this.maxRows ? Math.max(3, Math.floor(this.maxRows())) : details.length + content.length + 4
|
|
134
|
-
const budget = Math.max(0, height - 3)
|
|
135
|
-
// Keep choices and the footer mounted even when an approval contains a large command or path.
|
|
136
|
-
const minContent = Math.min(content.length, Math.min(2, budget))
|
|
137
|
-
const detailBudget = Math.min(details.length, Math.max(0, budget - Math.max(minContent, Math.min(content.length, budget))))
|
|
138
|
-
this.detailRows = detailBudget
|
|
139
|
-
this.detailTotal = details.length
|
|
140
|
-
this.detailOffset = Math.max(0, Math.min(this.detailOffset, details.length - detailBudget))
|
|
141
|
-
const body = details.slice(this.detailOffset, this.detailOffset + detailBudget).map(line => theme.fg('muted', line))
|
|
142
|
-
const available = Math.max(0, budget - body.length)
|
|
143
|
-
const cursor = content.findIndex(line => line.includes('\x1b_pi:c\x07') || stripTerminalSequences(line).startsWith('❯'))
|
|
144
|
-
const first = content.length <= available ? 0 : Math.max(0, Math.min(content.length - available, cursor - Math.floor(available / 2)))
|
|
145
|
-
body.push(...content.slice(first, first + available))
|
|
146
|
-
const seconds = this.deadline === undefined ? '' : ` · ${Math.max(0, Math.ceil((this.deadline - Date.now()) / 1000))}s`
|
|
147
|
-
let footerText = this.hint + seconds
|
|
148
|
-
if (details.length > detailBudget) {
|
|
149
|
-
footerText = `PgUp/PgDn details ${this.detailOffset + 1}-${this.detailOffset + detailBudget}/${details.length} · ${footerText}`
|
|
150
|
-
if (visibleWidth(footerText) > inner) footerText = 'PgUp/PgDn details · Enter · Esc' + seconds
|
|
151
|
-
}
|
|
152
|
-
const footer = row(theme.fg('dim', footerText))
|
|
153
|
-
return [top, ...body.map(row), footer, bottom].slice(0, height)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export class TerminalDialogs {
|
|
158
|
-
private pending = new Set<() => void>()
|
|
159
|
-
private disposed = false
|
|
160
|
-
constructor(private readonly tui: TUI, private readonly theme: () => Theme, private readonly keybindings: KeybindingsManager, private readonly changed: () => void) {}
|
|
161
|
-
|
|
162
|
-
private show<T>(build: (done: (value: T | undefined) => void) => DialogPanel, options?: ExtensionUIDialogOptions): Promise<T | undefined> {
|
|
163
|
-
if (this.disposed || options?.signal?.aborted) return Promise.resolve(undefined)
|
|
164
|
-
return new Promise((resolve, reject) => {
|
|
165
|
-
let handle: OverlayHandle | undefined
|
|
166
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
167
|
-
let countdown: ReturnType<typeof setInterval> | undefined
|
|
168
|
-
let settled = false
|
|
169
|
-
let panel: DialogPanel | undefined
|
|
170
|
-
const finish = (value: T | undefined, error?: unknown) => {
|
|
171
|
-
if (settled) return
|
|
172
|
-
settled = true
|
|
173
|
-
clearTimeout(timer)
|
|
174
|
-
clearInterval(countdown)
|
|
175
|
-
options?.signal?.removeEventListener('abort', cancel)
|
|
176
|
-
this.pending.delete(cancel)
|
|
177
|
-
handle?.hide()
|
|
178
|
-
const content = panel?.content as DisposableComponent | undefined
|
|
179
|
-
try { content?.dispose?.() } catch { /* The dialog must still release focus and settle. */ }
|
|
180
|
-
this.changed()
|
|
181
|
-
if (error !== undefined) reject(error)
|
|
182
|
-
else resolve(value)
|
|
183
|
-
}
|
|
184
|
-
const done = (value: T | undefined) => finish(value)
|
|
185
|
-
const cancel = () => done(undefined)
|
|
186
|
-
this.pending.add(cancel)
|
|
187
|
-
options?.signal?.addEventListener('abort', cancel, { once: true })
|
|
188
|
-
try {
|
|
189
|
-
panel = build(done)
|
|
190
|
-
panel.maxRows = () => Math.max(3, Math.floor(this.tui.terminal.rows * 0.8))
|
|
191
|
-
if (options?.timeout !== undefined) {
|
|
192
|
-
panel.deadline = Date.now() + Math.max(0, options.timeout)
|
|
193
|
-
timer = setTimeout(cancel, Math.max(0, options.timeout))
|
|
194
|
-
countdown = setInterval(this.changed, 250)
|
|
195
|
-
countdown.unref?.()
|
|
196
|
-
}
|
|
197
|
-
handle = this.tui.showOverlay(panel, { anchor: 'bottom-center', width: '100%', maxHeight: '80%' })
|
|
198
|
-
if (settled) handle.hide()
|
|
199
|
-
this.changed()
|
|
200
|
-
} catch (error) {
|
|
201
|
-
finish(undefined, error)
|
|
202
|
-
}
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
select(title: string, options: string[], opts?: ExtensionUIDialogOptions): Promise<string | undefined> {
|
|
207
|
-
return this.show<string>(done => {
|
|
208
|
-
const list = new SearchableSelect(options, this.tui, this.theme, index => done(options[index]), () => done(undefined))
|
|
209
|
-
return new DialogPanel(title, list, this.theme, '↑/↓ select · Enter confirm · Esc cancel')
|
|
210
|
-
}, opts)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async confirm(title: string, message: string, opts?: ExtensionUIDialogOptions): Promise<boolean> {
|
|
214
|
-
return (await this.show<boolean>(done => {
|
|
215
|
-
const list = new SelectList([{ value: 'yes', label: 'Yes' }, { value: 'no', label: 'No' }], 2, selectTheme(this.theme()))
|
|
216
|
-
list.onSelect = item => done(item.value === 'yes')
|
|
217
|
-
list.onCancel = () => done(undefined)
|
|
218
|
-
return new DialogPanel(title, list, this.theme, '↑/↓ select · Enter confirm · Esc cancel', message)
|
|
219
|
-
}, opts)) ?? false
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
input(title: string, placeholder?: string, opts?: ExtensionUIDialogOptions, secret = false): Promise<string | undefined> {
|
|
223
|
-
return this.show<string>(done => {
|
|
224
|
-
const InputType = secret ? SecretInput : Input
|
|
225
|
-
const input = new InputType({ prompt: secret ? '' : '> ', placeholder, placeholderStyle: text => this.theme().fg('dim', text) })
|
|
226
|
-
input.onSubmit = done
|
|
227
|
-
input.onEscape = () => done(undefined)
|
|
228
|
-
return new DialogPanel(title, input, this.theme, 'Enter submit · Esc cancel')
|
|
229
|
-
}, opts)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
editor(title: string, prefill?: string): Promise<string | undefined> {
|
|
233
|
-
return this.show<string>(done => {
|
|
234
|
-
const editor = new Editor(this.tui, { borderColor: text => this.theme().fg('border', text), selectList: selectTheme(this.theme()) })
|
|
235
|
-
editor.setText(prefill ?? '')
|
|
236
|
-
editor.onSubmit = done
|
|
237
|
-
const content: Component & Focusable = {
|
|
238
|
-
get focused() { return editor.focused }, set focused(value) { editor.focused = value },
|
|
239
|
-
invalidate: () => editor.invalidate(), render: width => editor.render(Math.max(4, width)).map(line => truncateToWidth(line, width, '')),
|
|
240
|
-
handleInput: data => {
|
|
241
|
-
if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) done(undefined)
|
|
242
|
-
else editor.handleInput(data)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return new DialogPanel(title, content, this.theme, 'Enter submit · Shift+Enter newline · Esc cancel')
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
custom<T>(factory: Parameters<ExtensionUIContext['custom']>[0], options?: Parameters<ExtensionUIContext['custom']>[1]): Promise<T> {
|
|
250
|
-
if (this.disposed) return Promise.resolve(undefined as T)
|
|
251
|
-
return new Promise<T>((resolve, reject) => {
|
|
252
|
-
let handle: OverlayHandle | undefined
|
|
253
|
-
let component: DisposableComponent | undefined
|
|
254
|
-
let settled = false
|
|
255
|
-
const done = (value: unknown) => {
|
|
256
|
-
if (settled) return
|
|
257
|
-
settled = true
|
|
258
|
-
this.pending.delete(cancel)
|
|
259
|
-
handle?.hide()
|
|
260
|
-
try { component?.dispose?.() } catch { /* Cleanup cannot prevent completion. */ }
|
|
261
|
-
this.changed()
|
|
262
|
-
resolve(value as T)
|
|
263
|
-
}
|
|
264
|
-
const cancel = () => done(undefined)
|
|
265
|
-
this.pending.add(cancel)
|
|
266
|
-
Promise.resolve().then(() => factory(this.tui, this.theme(), this.keybindings, done)).then(value => {
|
|
267
|
-
component = value
|
|
268
|
-
if (settled) {
|
|
269
|
-
try { component.dispose?.() } catch { /* It never mounted. */ }
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
const defaults: OverlayOptions = { anchor: 'bottom-center', width: '100%', maxHeight: '90%' }
|
|
273
|
-
const source = options?.overlayOptions
|
|
274
|
-
const overlayOptions = typeof source === 'function'
|
|
275
|
-
? new Proxy(defaults, { get: (target, key: keyof OverlayOptions) => source()[key] ?? target[key] })
|
|
276
|
-
: { ...defaults, ...source }
|
|
277
|
-
handle = this.tui.showOverlay(component, overlayOptions)
|
|
278
|
-
options?.onHandle?.(handle)
|
|
279
|
-
this.changed()
|
|
280
|
-
}).catch(error => {
|
|
281
|
-
if (!settled) {
|
|
282
|
-
settled = true
|
|
283
|
-
this.pending.delete(cancel)
|
|
284
|
-
handle?.hide()
|
|
285
|
-
try { component?.dispose?.() } catch { /* Cleanup cannot prevent completion. */ }
|
|
286
|
-
this.changed()
|
|
287
|
-
reject(error)
|
|
288
|
-
}
|
|
289
|
-
})
|
|
290
|
-
})
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
reset(): void {
|
|
294
|
-
for (const cancel of [...this.pending]) cancel()
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
dispose(): void {
|
|
298
|
-
this.disposed = true
|
|
299
|
-
this.reset()
|
|
300
|
-
}
|
|
301
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { getSelectListTheme, initTheme, type KeybindingsManager, Theme } from '@earendil-works/pi-coding-agent'
|
|
2
|
-
import { CURSOR_MARKER, KeybindingsManager as TuiKeybindingsManager, stripTerminalSequences, type TUI, TUI_KEYBINDINGS, visibleWidth } from '@earendil-works/pi-tui'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
|
|
5
|
-
import { HarnessEditor } from './editor'
|
|
6
|
-
import { createHarnessTheme } from './theme'
|
|
7
|
-
|
|
8
|
-
initTheme('dark', false)
|
|
9
|
-
const theme = createHarnessTheme({ getColorMode: () => 'truecolor' } as Theme)
|
|
10
|
-
function setup() {
|
|
11
|
-
const editor = new HarnessEditor({ terminal: { rows: 30 }, requestRender() {} } as unknown as TUI,
|
|
12
|
-
{ borderColor: (text) => text, selectList: getSelectListTheme() }, new TuiKeybindingsManager(TUI_KEYBINDINGS) as KeybindingsManager, {
|
|
13
|
-
band: (width, above, below) => `${above}:${below}`.padEnd(width, '─'),
|
|
14
|
-
theme: () => theme, showHint: () => true, thinkingKey: '⇧⇥'
|
|
15
|
-
})
|
|
16
|
-
editor.focused = true
|
|
17
|
-
return editor
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe('band composer over the native Pi editor', () => {
|
|
21
|
-
it('uses exactly a band and a curved prompt row, preserving the real cursor beside the hint', () => {
|
|
22
|
-
const editor = setup()
|
|
23
|
-
const rows = editor.render(80)
|
|
24
|
-
expect(rows).toHaveLength(2)
|
|
25
|
-
expect(stripTerminalSequences(rows[1])).toMatch(/^╰─ .*⇧⇥ to change thinking effort$/)
|
|
26
|
-
expect(rows[1]).toContain(CURSOR_MARKER)
|
|
27
|
-
expect(rows.every((row) => visibleWidth(row) <= 80)).toBe(true)
|
|
28
|
-
expect(editor.getText()).toBe('')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('closes the fake cursor before the hint, so the rest of the row is not reverse video', () => {
|
|
32
|
-
const editor = setup()
|
|
33
|
-
// Mirrors pi-tui's resolveFakeCursors: the cursor opens reverse video and its end marker closes it.
|
|
34
|
-
const resolved = editor.render(80)[1].replace(/\x1b_pi:fc\x07/g, '\x1b[7m').replace(/\x1b_pi:\/fc\x07/g, '\x1b[27m')
|
|
35
|
-
const reverseOn = resolved.indexOf('\x1b[7m')
|
|
36
|
-
const reverseOff = resolved.indexOf('\x1b[27m')
|
|
37
|
-
expect(reverseOn).toBeGreaterThan(-1)
|
|
38
|
-
expect(reverseOff).toBeGreaterThan(reverseOn)
|
|
39
|
-
expect(reverseOff).toBeLessThan(resolved.indexOf('⇧⇥'))
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('closes the cursor cell so the hint is not painted in reverse video', () => {
|
|
43
|
-
const editor = setup()
|
|
44
|
-
// Why: pi-tui paints its fake cursor as reverse video when it resolves a frame.
|
|
45
|
-
const prompt = editor.render(80)[1].replace(/\x1b_pi:fc\x07/g, '\x1b[7m').replace(/\x1b_pi:\/fc\x07/g, '\x1b[27m')
|
|
46
|
-
expect(prompt.split('\x1b[7m')).toHaveLength(prompt.split('\x1b[27m').length)
|
|
47
|
-
expect(prompt.indexOf('to change')).toBeGreaterThan(prompt.lastIndexOf('\x1b[27m'))
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('preserves editing, multiline wrapping, history, and cursor markers across resizes', () => {
|
|
51
|
-
const editor = setup()
|
|
52
|
-
const draft = 'Read 日本語 and compare the result'.repeat(3) + '\nSecond line'
|
|
53
|
-
editor.setText(draft)
|
|
54
|
-
for (const width of [120, 40, 10, 3, 1, 80]) {
|
|
55
|
-
const rows = editor.render(width)
|
|
56
|
-
expect(rows.every((row) => visibleWidth(row) <= width), `width ${width}`).toBe(true)
|
|
57
|
-
expect(rows.join('')).not.toContain('\u0000')
|
|
58
|
-
expect(rows.join('')).toContain(CURSOR_MARKER)
|
|
59
|
-
expect(editor.getText()).toBe(draft)
|
|
60
|
-
}
|
|
61
|
-
editor.setText('')
|
|
62
|
-
editor.handleInput('hello')
|
|
63
|
-
expect(editor.getText()).toBe('hello')
|
|
64
|
-
editor.addToHistory('previous prompt')
|
|
65
|
-
editor.setText('')
|
|
66
|
-
editor.handleInput('\x1b[A')
|
|
67
|
-
expect(editor.getText()).toBe('previous prompt')
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('retains autocomplete rows and selection without mistaking them for the removed bottom border', async () => {
|
|
71
|
-
const editor = setup()
|
|
72
|
-
editor.setAutocompleteProvider({
|
|
73
|
-
getSuggestions: async () => ({ items: [{ value: '/accounts', label: '/accounts' }, { value: '/plugins', label: '/plugins' }], prefix: '/' }),
|
|
74
|
-
applyCompletion: (lines, line, _col, item) => ({ lines: lines.map((value, index) => index === line ? item.value : value), cursorLine: line, cursorCol: item.value.length })
|
|
75
|
-
})
|
|
76
|
-
editor.handleInput('/')
|
|
77
|
-
await new Promise((resolve) => setTimeout(resolve, 30))
|
|
78
|
-
const rows = editor.render(50)
|
|
79
|
-
expect(rows.some((row) => stripTerminalSequences(row).includes('/accounts'))).toBe(true)
|
|
80
|
-
expect(rows.every((row) => visibleWidth(row) <= 50)).toBe(true)
|
|
81
|
-
editor.handleInput('\t')
|
|
82
|
-
expect(editor.getText()).toContain('/accounts')
|
|
83
|
-
})
|
|
84
|
-
})
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// Harness owns composer geometry and actions; Pi provides the editing primitive.
|
|
2
|
-
import { CustomEditor, type KeybindingsManager, type Theme } from '@earendil-works/pi-coding-agent'
|
|
3
|
-
import { CURSOR_MARKER, type EditorTheme, type TUI, type TuiMouseEvent, sliceByColumn, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui'
|
|
4
|
-
|
|
5
|
-
const BOTTOM = '\u0000harness-composer-bottom'
|
|
6
|
-
|
|
7
|
-
export interface ComposerView {
|
|
8
|
-
band(width: number, hiddenAbove: number, hiddenBelow: number): string
|
|
9
|
-
theme(): Theme
|
|
10
|
-
showHint(): boolean
|
|
11
|
-
thinkingKey: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class HarnessEditor extends CustomEditor {
|
|
15
|
-
private hiddenAbove = 0
|
|
16
|
-
private hiddenBelow = 0
|
|
17
|
-
private contentRows = 1
|
|
18
|
-
private gutterWidth = 3
|
|
19
|
-
|
|
20
|
-
constructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager, private readonly view: ComposerView) {
|
|
21
|
-
super(tui, theme, keybindings, { paddingX: 0 })
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
override setPaddingX(_padding: number): void {
|
|
25
|
-
super.setPaddingX(0)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected override renderTopBorder(_width: number, hiddenLineCount: number): string {
|
|
29
|
-
this.hiddenAbove = hiddenLineCount
|
|
30
|
-
return ''
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected override renderBottomBorder(_width: number, hiddenLineCount: number): string {
|
|
34
|
-
this.hiddenBelow = hiddenLineCount
|
|
35
|
-
return BOTTOM
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
override render(width: number): string[] {
|
|
39
|
-
if (width < 1) return []
|
|
40
|
-
this.gutterWidth = Math.min(3, Math.max(0, width - 1))
|
|
41
|
-
const innerWidth = Math.max(1, width - this.gutterWidth)
|
|
42
|
-
// Pi 1.2 cannot lay out a two-cell grapheme in a one-cell text area.
|
|
43
|
-
// Lay out safely, then clip the presentation without changing the draft.
|
|
44
|
-
const rows = super.render(Math.max(3, innerWidth))
|
|
45
|
-
const bottom = rows.indexOf(BOTTOM)
|
|
46
|
-
const content = rows.slice(1, bottom)
|
|
47
|
-
this.contentRows = content.length
|
|
48
|
-
const theme = this.view.theme()
|
|
49
|
-
const prompt = content.map((line, index) => {
|
|
50
|
-
const cue = index === 0 ? '╰─ '.slice(0, this.gutterWidth) : ' '.repeat(this.gutterWidth)
|
|
51
|
-
let text = line
|
|
52
|
-
if (index === 0 && this.getText() === '' && this.view.showHint()) {
|
|
53
|
-
const hint = theme.fg('accent', this.view.thinkingKey) + ' ' + theme.italic(theme.fg('dim', 'to change thinking effort'))
|
|
54
|
-
// Why: a one-cell slice cuts off the fake cursor's closing marker, so pi-tui
|
|
55
|
-
// paints the rest of the row in reverse video. Two cells keep the marker.
|
|
56
|
-
const prefix = sliceByColumn(line, 0, 2)
|
|
57
|
-
const gap = innerWidth - visibleWidth(prefix) - visibleWidth(hint)
|
|
58
|
-
if (gap >= 1) text = prefix + ' '.repeat(gap) + hint
|
|
59
|
-
}
|
|
60
|
-
return theme.fg('borderMuted', cue) + text
|
|
61
|
-
})
|
|
62
|
-
const completions = rows.slice(bottom + 1).map((line) => ' '.repeat(this.gutterWidth) + line)
|
|
63
|
-
const result = [this.view.band(width, this.hiddenAbove, this.hiddenBelow), ...prompt, ...completions].map((line) => truncateToWidth(line, width, ''))
|
|
64
|
-
if (this.focused && !result.some((line) => line.includes(CURSOR_MARKER))) {
|
|
65
|
-
const index = Math.max(1, Math.min(result.length - 1, prompt.length))
|
|
66
|
-
result[index] = truncateToWidth(result[index], Math.max(0, width - 1), '') + CURSOR_MARKER + ' '
|
|
67
|
-
}
|
|
68
|
-
return result
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
override handleMouse(event: TuiMouseEvent) {
|
|
72
|
-
return super.handleMouse({
|
|
73
|
-
...event,
|
|
74
|
-
x: event.x - this.gutterWidth,
|
|
75
|
-
y: event.y > this.contentRows ? event.y + 1 : event.y,
|
|
76
|
-
width: Math.max(1, event.width - this.gutterWidth)
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import { tmpdir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { expect, it } from 'vitest'
|
|
5
|
-
import { terminalKeybindings } from './keybindings'
|
|
6
|
-
|
|
7
|
-
it('reloads edited isolated keybindings on the existing public manager', () => {
|
|
8
|
-
const root = mkdtempSync(join(tmpdir(), 'harness-keybindings-'))
|
|
9
|
-
try {
|
|
10
|
-
const file = join(root, 'keybindings.json')
|
|
11
|
-
writeFileSync(file, JSON.stringify({ 'app.model.select': 'ctrl+k' }))
|
|
12
|
-
const manager = terminalKeybindings(root)
|
|
13
|
-
expect(manager.matches('\x0b', 'app.model.select')).toBe(true)
|
|
14
|
-
writeFileSync(file, JSON.stringify({ 'app.model.select': 'ctrl+y' }))
|
|
15
|
-
manager.reload()
|
|
16
|
-
expect(manager.matches('\x0b', 'app.model.select')).toBe(false)
|
|
17
|
-
expect(manager.matches('\x19', 'app.model.select')).toBe(true)
|
|
18
|
-
writeFileSync(file, '{}')
|
|
19
|
-
manager.reload()
|
|
20
|
-
expect(manager.matches('\x0c', 'app.model.select')).toBe(true)
|
|
21
|
-
} finally { rmSync(root, { recursive: true, force: true }) }
|
|
22
|
-
})
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { KeybindingsManager as PiKeybindingsManager } from '@earendil-works/pi-coding-agent'
|
|
2
|
-
import { readFileSync } from 'node:fs'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { KeybindingsManager, TUI_KEYBINDINGS, type KeybindingDefinitions, type KeybindingsConfig } from '@earendil-works/pi-tui'
|
|
5
|
-
|
|
6
|
-
// Harness owns application actions. Editing and selection retain Pi's reusable bindings.
|
|
7
|
-
const actions: Record<string, string | string[]> = {
|
|
8
|
-
'app.interrupt': 'escape', 'app.clear': 'ctrl+c', 'app.exit': 'ctrl+d',
|
|
9
|
-
'app.suspend': process.platform === 'win32' ? [] : 'ctrl+z',
|
|
10
|
-
'app.thinking.cycle': 'shift+tab', 'app.thinking.save': 'ctrl+s',
|
|
11
|
-
'app.model.cycleForward': 'ctrl+p', 'app.model.cycleBackward': 'alt+p',
|
|
12
|
-
'app.model.select': 'ctrl+l', 'app.tools.expand': 'ctrl+o', 'app.thinking.toggle': 'ctrl+t',
|
|
13
|
-
'app.editor.external': 'ctrl+g', 'app.message.copy': 'ctrl+x',
|
|
14
|
-
'app.message.followUp': 'alt+enter', 'app.message.dequeue': 'alt+up',
|
|
15
|
-
'app.clipboard.pasteImage': process.platform === 'darwin' ? 'alt+v' : 'ctrl+v',
|
|
16
|
-
'app.session.toggleNamedFilter': 'ctrl+n', 'app.session.togglePath': 'ctrl+p',
|
|
17
|
-
'app.session.toggleSort': 'ctrl+s', 'app.session.rename': 'ctrl+r',
|
|
18
|
-
'app.session.delete': 'ctrl+d', 'app.session.deleteNoninvasive': 'ctrl+backspace',
|
|
19
|
-
'app.tree.foldOrUp': ['alt+left', 'ctrl+left'], 'app.tree.unfoldOrDown': ['alt+right', 'ctrl+right'],
|
|
20
|
-
'app.tree.editLabel': 'shift+l', 'app.tree.toggleLabelTimestamp': 'shift+t',
|
|
21
|
-
'app.tree.filter.default': 'ctrl+d', 'app.tree.filter.noTools': 'ctrl+t',
|
|
22
|
-
'app.tree.filter.userOnly': 'ctrl+u', 'app.tree.filter.labeledOnly': 'ctrl+l',
|
|
23
|
-
'app.tree.filter.all': 'ctrl+a', 'app.tree.filter.cycleForward': 'ctrl+o',
|
|
24
|
-
'app.tree.filter.cycleBackward': 'ctrl+shift+o', 'app.models.save': 'ctrl+s',
|
|
25
|
-
'app.models.enableAll': 'ctrl+a', 'app.models.clearAll': 'ctrl+x',
|
|
26
|
-
'app.models.toggleProvider': 'ctrl+p', 'app.models.reorderUp': 'alt+up', 'app.models.reorderDown': 'alt+down',
|
|
27
|
-
'app.session.new': [], 'app.session.tree': [], 'app.session.fork': [], 'app.session.resume': []
|
|
28
|
-
}
|
|
29
|
-
export const HARNESS_KEYBINDINGS: KeybindingDefinitions = {
|
|
30
|
-
...TUI_KEYBINDINGS,
|
|
31
|
-
...Object.fromEntries(Object.entries(actions).map(([name, keys]) => [name, { defaultKeys: keys, description: name.replace(/^app\./, '').replaceAll('.', ' ') }])) as KeybindingDefinitions
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function readBindings(agentDir: string): KeybindingsConfig {
|
|
35
|
-
let bindings: KeybindingsConfig = {}
|
|
36
|
-
try {
|
|
37
|
-
const source = JSON.parse(readFileSync(join(agentDir, 'keybindings.json'), 'utf8'))
|
|
38
|
-
if (source && typeof source === 'object' && !Array.isArray(source)) {
|
|
39
|
-
bindings = Object.fromEntries(Object.entries(source).filter(([, value]) => typeof value === 'string' || Array.isArray(value) && value.every(key => typeof key === 'string'))) as KeybindingsConfig
|
|
40
|
-
}
|
|
41
|
-
} catch { /* A missing keybindings file uses the application defaults. */ }
|
|
42
|
-
return bindings
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function terminalKeybindings(agentDir: string): PiKeybindingsManager {
|
|
46
|
-
const manager = new KeybindingsManager(HARNESS_KEYBINDINGS, readBindings(agentDir))
|
|
47
|
-
// CustomEditor and selector factories type their manager as Pi's private subclass.
|
|
48
|
-
// They consume the public base plus getEffectiveConfig; Harness owns the definitions.
|
|
49
|
-
return Object.assign(manager, { getEffectiveConfig: () => manager.getResolvedBindings(), reload() { manager.setUserBindings(readBindings(agentDir)) } }) as unknown as PiKeybindingsManager
|
|
50
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// Pi 1.2.0 omits CLI builtins, rich builtin renderers, clipboard reads and the global theme setter
|
|
2
|
-
// from its public exports. Keep those version-sensitive presentation hooks here; session/runtime
|
|
3
|
-
// work uses public APIs. Clipboard helpers are only called for an explicit user paste.
|
|
4
|
-
import { getPackageDir, VERSION, type InlineExtension, type Theme, type ToolRenderers } from '@earendil-works/pi-coding-agent'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
import { pathToFileURL } from 'node:url'
|
|
7
|
-
|
|
8
|
-
export interface PiAdapter {
|
|
9
|
-
builtins: InlineExtension[]
|
|
10
|
-
setTheme(theme: Theme): void
|
|
11
|
-
withBuiltInRenderers(toolName: string, definition: ToolRenderers | undefined): ToolRenderers | undefined
|
|
12
|
-
readClipboardText(): Promise<string | null>
|
|
13
|
-
readClipboardFilePaths(): Promise<string[] | null>
|
|
14
|
-
readClipboardImage(): Promise<{ bytes: Uint8Array; mimeType: string } | null>
|
|
15
|
-
extensionForImageMimeType(mimeType: string): string | null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let adapter: PiAdapter | undefined
|
|
19
|
-
let loading: Promise<PiAdapter> | undefined
|
|
20
|
-
|
|
21
|
-
export function assertPiAdapterVersion(version: string): void {
|
|
22
|
-
if (version !== '1.2.0') throw new Error(`The Harness terminal adapter requires Pi 1.2.0; found ${version}. Install the Harness package's pinned dependencies.`)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function loadPiAdapter(): Promise<PiAdapter> {
|
|
26
|
-
loading ??= (async () => {
|
|
27
|
-
assertPiAdapterVersion(VERSION)
|
|
28
|
-
const root = getPackageDir()
|
|
29
|
-
const [extensions, theme, renderers, clipboard, images] = await Promise.all([
|
|
30
|
-
import(pathToFileURL(join(root, 'dist/extensions/index.js')).href),
|
|
31
|
-
import(pathToFileURL(join(root, 'dist/modes/interactive/theme/theme.js')).href),
|
|
32
|
-
import(pathToFileURL(join(root, 'dist/core/tools/renderers/index.js')).href),
|
|
33
|
-
import(pathToFileURL(join(root, 'dist/utils/clipboard.js')).href),
|
|
34
|
-
import(pathToFileURL(join(root, 'dist/utils/clipboard-image.js')).href)
|
|
35
|
-
])
|
|
36
|
-
if (!Array.isArray(extensions.builtInExtensions) || typeof theme.setThemeInstance !== 'function' || typeof renderers.withBuiltInRenderers !== 'function' || typeof clipboard.readClipboardText !== 'function' || typeof clipboard.readClipboardFilePaths !== 'function' || typeof images.readClipboardImage !== 'function' || typeof images.extensionForImageMimeType !== 'function') {
|
|
37
|
-
throw new Error('The pinned Pi terminal adapter no longer exposes its required builtin or theme hooks.')
|
|
38
|
-
}
|
|
39
|
-
adapter = {
|
|
40
|
-
builtins: extensions.builtInExtensions as InlineExtension[],
|
|
41
|
-
setTheme: theme.setThemeInstance,
|
|
42
|
-
withBuiltInRenderers: renderers.withBuiltInRenderers,
|
|
43
|
-
readClipboardText: clipboard.readClipboardText,
|
|
44
|
-
readClipboardFilePaths: clipboard.readClipboardFilePaths,
|
|
45
|
-
readClipboardImage: images.readClipboardImage,
|
|
46
|
-
extensionForImageMimeType: images.extensionForImageMimeType
|
|
47
|
-
}
|
|
48
|
-
return adapter
|
|
49
|
-
})()
|
|
50
|
-
return loading
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Initialize once with loadPiAdapter() before rendering native rich tool components. */
|
|
54
|
-
export function setPiTheme(theme: Theme): void {
|
|
55
|
-
if (!adapter) throw new Error('Initialize the Pi terminal adapter before setting its theme.')
|
|
56
|
-
adapter.setTheme(theme)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function withBuiltInRenderers(toolName: string, definition: ToolRenderers | undefined): ToolRenderers | undefined {
|
|
60
|
-
if (!adapter) throw new Error('Initialize the Pi terminal adapter before resolving builtin renderers.')
|
|
61
|
-
return adapter.withBuiltInRenderers(toolName, definition)
|
|
62
|
-
}
|