@xingular/harness 0.1.6 → 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 +107 -0
- package/LICENSE +202 -0
- package/NOTICE.md +60 -0
- package/README.md +265 -71
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +14842 -9445
- 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 +1170 -62
- package/docs/README.md +34 -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-0001-owned-terminal.md +87 -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 +671 -0
- package/package.json +48 -12
- package/src/accounts/accounts.test.ts +0 -223
- package/src/accounts/accounts.ts +0 -522
- 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 -114
- 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 -299
- package/src/index.ts +0 -70
- 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 -289
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -406
- package/src/memory/caller.ts +0 -57
- 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 -330
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -138
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -576
- package/src/memory/recall.test.ts +0 -104
- package/src/memory/recall.ts +0 -271
- 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 -314
- 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 -310
- package/src/sessions/conversation.test.ts +0 -150
- 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 -32
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -294
- 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 -569
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -455
- package/src/sessions/sessions.ts +0 -295
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -407
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/controller.ts +0 -227
- package/src/terminal/launch.test.ts +0 -34
- package/src/terminal/launch.ts +0 -88
- package/src/terminal/piExtension.ts +0 -210
- 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 -221
- package/src/terminal/standalone.test.ts +0 -130
- package/src/terminal/standalone.ts +0 -144
- package/src/terminal/terminal.test.ts +0 -330
- package/src/terminal/terminals.ts +0 -443
- package/src/tools/askUser.ts +0 -45
- package/src/tools/definitions.ts +0 -187
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- 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,60 @@
|
|
|
1
|
+
import { Input, matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
2
|
+
export const SEARCH_GLYPH = '⌕';
|
|
3
|
+
export const SEARCH_PLACEHOLDER = 'Type to search';
|
|
4
|
+
export const SEARCH_FIELD_ROWS = 5;
|
|
5
|
+
export function searchFieldRowsFor(availableRows) {
|
|
6
|
+
const fieldRoom = Math.max(0, Math.floor(availableRows) - 1);
|
|
7
|
+
if (fieldRoom >= SEARCH_FIELD_ROWS)
|
|
8
|
+
return SEARCH_FIELD_ROWS;
|
|
9
|
+
return fieldRoom >= 3 ? 3 : 1;
|
|
10
|
+
}
|
|
11
|
+
const SCREEN_KEYS = ['left', 'right', 'up', 'down', 'enter', 'tab', 'shift+tab', 'escape', 'pageUp', 'pageDown', 'home', 'end'];
|
|
12
|
+
export class SearchField {
|
|
13
|
+
theme;
|
|
14
|
+
input;
|
|
15
|
+
constructor(theme, placeholder = SEARCH_PLACEHOLDER) {
|
|
16
|
+
this.theme = theme;
|
|
17
|
+
this.input = new Input({ prompt: '', placeholder, placeholderStyle: text => this.theme().fg('dim', text) });
|
|
18
|
+
}
|
|
19
|
+
get value() { return this.input.getValue(); }
|
|
20
|
+
set focused(value) { this.input.focused = value; }
|
|
21
|
+
/** Presets the text, for a command that was given something to search for. */
|
|
22
|
+
setValue(text) { this.input.setValue(text); }
|
|
23
|
+
/** Empties the field; says whether there was anything to empty. */
|
|
24
|
+
clear() {
|
|
25
|
+
if (!this.value)
|
|
26
|
+
return false;
|
|
27
|
+
this.input.setValue('');
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** Takes typed text, paste, Backspace and Ctrl+U. Says whether the text changed. */
|
|
31
|
+
handleInput(data) {
|
|
32
|
+
if (SCREEN_KEYS.some(key => matchesKey(data, key)))
|
|
33
|
+
return false;
|
|
34
|
+
const before = this.value;
|
|
35
|
+
this.input.handleInput(data);
|
|
36
|
+
return this.value !== before;
|
|
37
|
+
}
|
|
38
|
+
invalidate() { this.input.invalidate(); }
|
|
39
|
+
/** One row, `width` cells: the glyph, the text or placeholder, and `note` at the right edge. */
|
|
40
|
+
render(width, note = '') {
|
|
41
|
+
if (width < 1)
|
|
42
|
+
return '';
|
|
43
|
+
const theme = this.theme();
|
|
44
|
+
const glyph = theme.fg('text', `${SEARCH_GLYPH} `);
|
|
45
|
+
const room = Math.max(0, width - visibleWidth(glyph));
|
|
46
|
+
const shownNote = note && room > visibleWidth(note) + 8 ? note : '';
|
|
47
|
+
const field = this.input.render(Math.max(1, room - (shownNote ? visibleWidth(shownNote) + 1 : 0)))[0] ?? '';
|
|
48
|
+
return truncateToWidth(glyph + field + (shownNote ? ' ' + theme.fg('muted', shownNote) : ''), width, '');
|
|
49
|
+
}
|
|
50
|
+
renderLines(width, note = '', rows = SEARCH_FIELD_ROWS) {
|
|
51
|
+
if (width < 1)
|
|
52
|
+
return [];
|
|
53
|
+
if (rows < 3)
|
|
54
|
+
return [this.render(width, note)];
|
|
55
|
+
const rule = this.theme().fg('border', '─'.repeat(width));
|
|
56
|
+
return rows >= SEARCH_FIELD_ROWS
|
|
57
|
+
? ['', rule, this.render(width, note), rule, '']
|
|
58
|
+
: [rule, this.render(width, note), rule];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One selection palette for every terminal surface. A selected row is a muted blue field with
|
|
3
|
+
* explicit near-white text, so the accent colour never sits on the selection background.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SELECTION_COLORS: {
|
|
6
|
+
readonly dark: {
|
|
7
|
+
readonly background: "#1b314d";
|
|
8
|
+
readonly text: "#f4f8ff";
|
|
9
|
+
readonly muted: "#c4d3e6";
|
|
10
|
+
};
|
|
11
|
+
readonly light: {
|
|
12
|
+
readonly background: "#dbe8fa";
|
|
13
|
+
readonly text: "#10233a";
|
|
14
|
+
readonly muted: "#455a72";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One selection palette for every terminal surface. A selected row is a muted blue field with
|
|
3
|
+
* explicit near-white text, so the accent colour never sits on the selection background.
|
|
4
|
+
*/
|
|
5
|
+
export const SELECTION_COLORS = {
|
|
6
|
+
dark: {
|
|
7
|
+
background: '#1b314d',
|
|
8
|
+
text: '#f4f8ff',
|
|
9
|
+
muted: '#c4d3e6'
|
|
10
|
+
},
|
|
11
|
+
light: {
|
|
12
|
+
background: '#dbe8fa',
|
|
13
|
+
text: '#10233a',
|
|
14
|
+
muted: '#455a72'
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const dialogs = new Set(['select', 'input', 'editor', 'custom']);
|
|
2
|
+
/** Retained UI methods belong to the resource generation that received them. */
|
|
3
|
+
export function sessionUiContext(ui, current) {
|
|
4
|
+
return new Proxy(ui, {
|
|
5
|
+
get(target, key, receiver) {
|
|
6
|
+
const value = Reflect.get(target, key, receiver);
|
|
7
|
+
if (typeof value !== 'function')
|
|
8
|
+
return value;
|
|
9
|
+
return (...args) => {
|
|
10
|
+
if (current())
|
|
11
|
+
return Reflect.apply(value, target, args);
|
|
12
|
+
if (key === 'confirm')
|
|
13
|
+
return Promise.resolve(false);
|
|
14
|
+
if (dialogs.has(String(key)))
|
|
15
|
+
return Promise.resolve(undefined);
|
|
16
|
+
if (key === 'onTerminalInput')
|
|
17
|
+
return () => { };
|
|
18
|
+
if (key === 'setTheme')
|
|
19
|
+
return { success: false, error: 'This terminal session has ended.' };
|
|
20
|
+
if (key === 'getEditorText')
|
|
21
|
+
return '';
|
|
22
|
+
if (key === 'getToolsExpanded')
|
|
23
|
+
return false;
|
|
24
|
+
if (key === 'getAllThemes')
|
|
25
|
+
return [];
|
|
26
|
+
return undefined;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentSession, ExtensionUIContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { TerminalSettingItem } from "./pages/settingsPage.js";
|
|
3
|
+
export interface SettingsModelHost {
|
|
4
|
+
session(): AgentSession;
|
|
5
|
+
ui: ExtensionUIContext;
|
|
6
|
+
hotkeys?(): string;
|
|
7
|
+
refresh(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createSettingsModel(host: SettingsModelHost): {
|
|
10
|
+
items: () => TerminalSettingItem[];
|
|
11
|
+
apply: (id: string) => Promise<void>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const GROUP_ORDER = ['Theme', 'Display', 'Images', 'Thinking', 'Recovery', 'Editor', 'Queues', 'Commands', 'Terminal', 'Keyboard', 'Compaction'];
|
|
2
|
+
export function createSettingsModel(host) {
|
|
3
|
+
const booleans = (session) => {
|
|
4
|
+
const settings = session.settingsManager;
|
|
5
|
+
return [
|
|
6
|
+
['Auto compaction', () => session.autoCompactionEnabled, (value) => session.setAutoCompactionEnabled(value)],
|
|
7
|
+
['Auto retry', () => session.autoRetryEnabled, (value) => session.setAutoRetryEnabled(value)],
|
|
8
|
+
['Show images', () => settings.getShowImages(), (value) => settings.setShowImages(value)],
|
|
9
|
+
['Resize images', () => settings.getImageAutoResize(), (value) => settings.setImageAutoResize(value)],
|
|
10
|
+
['Block images', () => settings.getBlockImages(), (value) => settings.setBlockImages(value)],
|
|
11
|
+
['Skill commands', () => settings.getEnableSkillCommands(), (value) => settings.setEnableSkillCommands(value)],
|
|
12
|
+
['Hide thinking', () => settings.getHideThinkingBlock(), (value) => settings.setHideThinkingBlock(value)],
|
|
13
|
+
['Hardware cursor', () => settings.getShowHardwareCursor(), (value) => settings.setShowHardwareCursor(value)],
|
|
14
|
+
['Terminal progress', () => settings.getShowTerminalProgress(), (value) => settings.setShowTerminalProgress(value)]
|
|
15
|
+
];
|
|
16
|
+
};
|
|
17
|
+
const locations = [
|
|
18
|
+
['Context', 'Compaction'], ['Model', 'Recovery'], ['Appearance', 'Images'], ['Appearance', 'Images'],
|
|
19
|
+
['Context', 'Images'], ['Interaction', 'Commands'], ['Appearance', 'Display'], ['Interaction', 'Editor'],
|
|
20
|
+
['Interaction', 'Terminal'], ['Model', 'Thinking'], ['Interaction', 'Queues'], ['Interaction', 'Queues'],
|
|
21
|
+
['Appearance', 'Theme'], ['Interaction', 'Keyboard']
|
|
22
|
+
];
|
|
23
|
+
const descriptions = [
|
|
24
|
+
'Compact the context automatically when the session approaches its limit.', 'Retry eligible provider failures automatically.',
|
|
25
|
+
'Display actual image content returned by tools.', 'Resize image attachments before provider requests.',
|
|
26
|
+
'Block image attachments in provider requests.', 'Expose commands for the skills loaded in this session.',
|
|
27
|
+
'Collapse assistant thinking blocks in the transcript.', 'Display the terminal hardware cursor for editing and input methods.',
|
|
28
|
+
'Report active work through supported terminal progress controls.', 'Reasoning effort for the current model.',
|
|
29
|
+
'Delivery mode for messages that steer an active run.', 'Delivery mode for messages queued after a run.',
|
|
30
|
+
'Theme loaded from the trusted resource catalog.', 'View the current application keyboard bindings.'
|
|
31
|
+
];
|
|
32
|
+
/** One line per setting, `Label: value`; its position is the setting's id. */
|
|
33
|
+
const options = (session) => [
|
|
34
|
+
...booleans(session).map(([label, get]) => `${label}: ${get() ? 'on' : 'off'}`),
|
|
35
|
+
`Thinking: ${session.thinkingLevel}`, `Steering: ${session.steeringMode}`, `Follow-up: ${session.followUpMode}`,
|
|
36
|
+
`Theme: ${host.ui.theme.name}`, 'Keyboard controls'
|
|
37
|
+
];
|
|
38
|
+
function items() {
|
|
39
|
+
const result = options(host.session()).map((option, index) => {
|
|
40
|
+
const split = option.indexOf(':');
|
|
41
|
+
return { id: String(index), tab: locations[index][0], group: locations[index][1], label: split < 0 ? option : option.slice(0, split), value: split < 0 ? '' : option.slice(split + 1).trim(), description: descriptions[index] };
|
|
42
|
+
});
|
|
43
|
+
return result.sort((a, b) => GROUP_ORDER.indexOf(a.group) - GROUP_ORDER.indexOf(b.group));
|
|
44
|
+
}
|
|
45
|
+
async function apply(id) {
|
|
46
|
+
const session = host.session();
|
|
47
|
+
const index = Number(id);
|
|
48
|
+
const switches = booleans(session);
|
|
49
|
+
const selected = options(session)[index];
|
|
50
|
+
if (selected === undefined)
|
|
51
|
+
return;
|
|
52
|
+
if (index < switches.length) {
|
|
53
|
+
const [, get, set] = switches[index];
|
|
54
|
+
set(!get());
|
|
55
|
+
}
|
|
56
|
+
else if (selected.startsWith('Thinking:')) {
|
|
57
|
+
const level = await host.ui.select('Thinking effort', session.getAvailableThinkingLevels());
|
|
58
|
+
if (level)
|
|
59
|
+
session.setThinkingLevel(level, { persist: true });
|
|
60
|
+
}
|
|
61
|
+
else if (selected.startsWith('Steering:') || selected.startsWith('Follow-up:')) {
|
|
62
|
+
const mode = await host.ui.select('Queue delivery', ['all', 'one-at-a-time']);
|
|
63
|
+
if (mode === 'all' || mode === 'one-at-a-time') {
|
|
64
|
+
if (selected.startsWith('Steering:'))
|
|
65
|
+
session.setSteeringMode(mode);
|
|
66
|
+
else
|
|
67
|
+
session.setFollowUpMode(mode);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (selected.startsWith('Theme:')) {
|
|
71
|
+
const theme = await host.ui.select('Theme', host.ui.getAllThemes().map(item => item.name));
|
|
72
|
+
if (theme) {
|
|
73
|
+
const result = host.ui.setTheme(theme);
|
|
74
|
+
if (!result.success)
|
|
75
|
+
throw new Error(result.error);
|
|
76
|
+
session.settingsManager.setTheme(theme);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
host.ui.notify(host.hotkeys?.() ?? 'Use /help for commands. Keyboard bindings are managed by the terminal application.', 'info');
|
|
81
|
+
host.refresh();
|
|
82
|
+
}
|
|
83
|
+
return { items, apply };
|
|
84
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type AgentSession, type AgentSessionServices, type Theme } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { type Component } from '@earendil-works/pi-tui';
|
|
3
|
+
export declare const SIDE_PROMPT: string;
|
|
4
|
+
export declare const ANSWER_ROWS = 8;
|
|
5
|
+
/**
|
|
6
|
+
* A bounded plain-text excerpt of the main conversation: the last few user and assistant turns, the
|
|
7
|
+
* tools an assistant turn called by name, and whether the reply is still running. Hidden reasoning,
|
|
8
|
+
* tool inputs and results, and images are left out.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sideQuestionSnapshot(messages: readonly unknown[], running: boolean): string;
|
|
11
|
+
/** What the controller needs from a session; a Pi `AgentSession` satisfies it through `createSideSession`. */
|
|
12
|
+
export interface SideSession {
|
|
13
|
+
prompt(text: string): Promise<void>;
|
|
14
|
+
abort(): Promise<void>;
|
|
15
|
+
subscribe(listener: () => void): () => void;
|
|
16
|
+
answer(): string;
|
|
17
|
+
/** Why the provider ended the turn in error, if it did. */
|
|
18
|
+
failure(): string | undefined;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
}
|
|
21
|
+
/** A separate in-memory session on the same account and model, with no tools, extensions, skills or project files. */
|
|
22
|
+
export declare function createSideSession(services: AgentSessionServices, main: AgentSession): Promise<SideSession>;
|
|
23
|
+
export type SideState = 'thinking' | 'answered' | 'failed';
|
|
24
|
+
export interface SideQuestionOptions {
|
|
25
|
+
create(): Promise<SideSession>;
|
|
26
|
+
snapshot(): string;
|
|
27
|
+
theme(): Theme;
|
|
28
|
+
/** Puts the dock above the composer, or takes it away. */
|
|
29
|
+
mount(dock: Component | undefined): void;
|
|
30
|
+
changed(): void;
|
|
31
|
+
}
|
|
32
|
+
export declare class SideQuestion {
|
|
33
|
+
private readonly options;
|
|
34
|
+
private session;
|
|
35
|
+
private unsubscribe;
|
|
36
|
+
private generation;
|
|
37
|
+
private question;
|
|
38
|
+
private answerText;
|
|
39
|
+
private error;
|
|
40
|
+
private current;
|
|
41
|
+
private startedAt;
|
|
42
|
+
private readonly dock;
|
|
43
|
+
constructor(options: SideQuestionOptions);
|
|
44
|
+
get visible(): boolean;
|
|
45
|
+
get state(): SideState | undefined;
|
|
46
|
+
/** Asks one question, replacing any earlier one. Empty text dismisses. */
|
|
47
|
+
ask(question: string): Promise<void>;
|
|
48
|
+
/** Interrupts a running side question and removes the dock. */
|
|
49
|
+
dismiss(): Promise<void>;
|
|
50
|
+
private release;
|
|
51
|
+
/** The dock: a neutral rule, the inline question, its answer, and a hint, separated by blank rows. */
|
|
52
|
+
private render;
|
|
53
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// `/btw <question>`: a side question answered by a separate, tool-free session while the main
|
|
2
|
+
// conversation carries on. It lives in a dock above the composer, never enters the transcript, the
|
|
3
|
+
// queue or the run history, and is gone on dismiss, on a session change, or when a new one replaces it.
|
|
4
|
+
import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager } from '@earendil-works/pi-coding-agent';
|
|
5
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
6
|
+
import { contentWidth, footerRow, neutralRule, padded, safeText } from "./chrome.js";
|
|
7
|
+
import { getWorkingSpinnerFrames, SPINNER_INTERVAL_MS, spinnerFrameAt } from "./workingStatus.js";
|
|
8
|
+
export const SIDE_PROMPT = [
|
|
9
|
+
'You answer one short side question from a person whose main task is still running in another session.',
|
|
10
|
+
'You have no tools: you cannot read or change files, run commands, browse, or call anything, and you must not say you did.',
|
|
11
|
+
'Use the conversation excerpt for context and do not invent what it does not show. Answer briefly and plainly.'
|
|
12
|
+
].join(' ');
|
|
13
|
+
const TURNS = 6;
|
|
14
|
+
const TURN_CHARS = 1_200;
|
|
15
|
+
const TOTAL_CHARS = 6_000;
|
|
16
|
+
const QUESTION_ROWS = 2;
|
|
17
|
+
export const ANSWER_ROWS = 8;
|
|
18
|
+
function blocks(content) {
|
|
19
|
+
if (typeof content === 'string')
|
|
20
|
+
return [{ type: 'text', text: content }];
|
|
21
|
+
return Array.isArray(content) ? content.filter((block) => typeof block === 'object' && block !== null) : [];
|
|
22
|
+
}
|
|
23
|
+
const clip = (text, limit) => text.length > limit ? `${text.slice(0, limit)}…` : text;
|
|
24
|
+
/**
|
|
25
|
+
* A bounded plain-text excerpt of the main conversation: the last few user and assistant turns, the
|
|
26
|
+
* tools an assistant turn called by name, and whether the reply is still running. Hidden reasoning,
|
|
27
|
+
* tool inputs and results, and images are left out.
|
|
28
|
+
*/
|
|
29
|
+
export function sideQuestionSnapshot(messages, running) {
|
|
30
|
+
const turns = [];
|
|
31
|
+
for (const message of messages) {
|
|
32
|
+
const { role, content } = (message ?? {});
|
|
33
|
+
if (role !== 'user' && role !== 'assistant')
|
|
34
|
+
continue;
|
|
35
|
+
const parts = blocks(content);
|
|
36
|
+
const text = parts.filter(block => block.type === 'text' && typeof block.text === 'string').map(block => String(block.text)).join('\n').trim();
|
|
37
|
+
const tools = [...new Set(parts.filter(block => block.type === 'toolCall' && typeof block.name === 'string').map(block => String(block.name)))];
|
|
38
|
+
if (!text && !tools.length)
|
|
39
|
+
continue;
|
|
40
|
+
turns.push(`${role === 'user' ? 'User' : 'Assistant'}: ${clip(safeText(text).replace(/\s+/g, ' ').trim(), TURN_CHARS)}${tools.length ? ` [used tools: ${tools.join(', ')}]` : ''}`);
|
|
41
|
+
}
|
|
42
|
+
let recent = turns.slice(-TURNS);
|
|
43
|
+
while (recent.length > 1 && recent.join('\n').length > TOTAL_CHARS)
|
|
44
|
+
recent = recent.slice(1);
|
|
45
|
+
const lines = ['Conversation excerpt, oldest first:', ...(recent.length ? recent : ['(nothing yet)'])];
|
|
46
|
+
if (running)
|
|
47
|
+
lines.push('', 'The main reply is still running; its latest turn above may be unfinished.');
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
/** A separate in-memory session on the same account and model, with no tools, extensions, skills or project files. */
|
|
51
|
+
export async function createSideSession(services, main) {
|
|
52
|
+
const model = main.model;
|
|
53
|
+
if (!model || model.id === 'unknown')
|
|
54
|
+
throw new Error('Choose a model before asking a side question.');
|
|
55
|
+
const settingsManager = SettingsManager.inMemory({});
|
|
56
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
57
|
+
cwd: services.cwd, agentDir: services.agentDir, settingsManager,
|
|
58
|
+
noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true, systemPrompt: SIDE_PROMPT
|
|
59
|
+
});
|
|
60
|
+
await resourceLoader.reload();
|
|
61
|
+
const { session } = await createAgentSession({
|
|
62
|
+
cwd: services.cwd, agentDir: services.agentDir, modelRuntime: services.modelRuntime, settingsManager, resourceLoader,
|
|
63
|
+
sessionManager: SessionManager.inMemory(services.cwd), model, thinkingLevel: 'off', noTools: 'all'
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
prompt: text => session.prompt(text),
|
|
67
|
+
abort: () => session.abort(),
|
|
68
|
+
subscribe: listener => session.subscribe(() => listener()),
|
|
69
|
+
answer: () => session.getLastAssistantText() ?? '',
|
|
70
|
+
failure: () => {
|
|
71
|
+
const last = session.messages.at(-1);
|
|
72
|
+
return last?.role === 'assistant' && last.stopReason === 'error' ? last.errorMessage ?? 'The provider returned an error.' : undefined;
|
|
73
|
+
},
|
|
74
|
+
dispose: () => session.dispose()
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export class SideQuestion {
|
|
78
|
+
options;
|
|
79
|
+
session;
|
|
80
|
+
unsubscribe;
|
|
81
|
+
generation = 0;
|
|
82
|
+
question = '';
|
|
83
|
+
answerText = '';
|
|
84
|
+
error;
|
|
85
|
+
current;
|
|
86
|
+
startedAt = 0;
|
|
87
|
+
dock;
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.options = options;
|
|
90
|
+
this.dock = { invalidate: () => { }, render: width => this.render(width) };
|
|
91
|
+
}
|
|
92
|
+
get visible() { return this.current !== undefined; }
|
|
93
|
+
get state() { return this.current; }
|
|
94
|
+
/** Asks one question, replacing any earlier one. Empty text dismisses. */
|
|
95
|
+
async ask(question) {
|
|
96
|
+
const text = question.trim();
|
|
97
|
+
if (!text) {
|
|
98
|
+
await this.dismiss();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
await this.release();
|
|
102
|
+
const generation = ++this.generation;
|
|
103
|
+
this.question = text;
|
|
104
|
+
this.startedAt = Date.now();
|
|
105
|
+
this.answerText = '';
|
|
106
|
+
this.error = undefined;
|
|
107
|
+
this.current = 'thinking';
|
|
108
|
+
this.options.mount(this.dock);
|
|
109
|
+
this.options.changed();
|
|
110
|
+
try {
|
|
111
|
+
const session = await this.options.create();
|
|
112
|
+
if (generation !== this.generation) {
|
|
113
|
+
session.dispose();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.session = session;
|
|
117
|
+
this.unsubscribe = session.subscribe(() => {
|
|
118
|
+
if (generation !== this.generation)
|
|
119
|
+
return;
|
|
120
|
+
this.answerText = session.answer();
|
|
121
|
+
this.options.changed();
|
|
122
|
+
});
|
|
123
|
+
await session.prompt(`${this.options.snapshot()}\n\nSide question: ${text}`);
|
|
124
|
+
if (generation !== this.generation)
|
|
125
|
+
return;
|
|
126
|
+
this.answerText = session.answer();
|
|
127
|
+
const failure = session.failure();
|
|
128
|
+
if (failure) {
|
|
129
|
+
this.current = 'failed';
|
|
130
|
+
this.error = failure;
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
this.current = 'answered';
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (generation !== this.generation)
|
|
137
|
+
return;
|
|
138
|
+
this.current = 'failed';
|
|
139
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
140
|
+
}
|
|
141
|
+
this.options.changed();
|
|
142
|
+
}
|
|
143
|
+
/** Interrupts a running side question and removes the dock. */
|
|
144
|
+
async dismiss() {
|
|
145
|
+
this.generation++;
|
|
146
|
+
this.current = undefined;
|
|
147
|
+
await this.release();
|
|
148
|
+
this.options.mount(undefined);
|
|
149
|
+
this.options.changed();
|
|
150
|
+
}
|
|
151
|
+
async release() {
|
|
152
|
+
const session = this.session;
|
|
153
|
+
this.session = undefined;
|
|
154
|
+
this.unsubscribe?.();
|
|
155
|
+
this.unsubscribe = undefined;
|
|
156
|
+
if (!session)
|
|
157
|
+
return;
|
|
158
|
+
try {
|
|
159
|
+
await session.abort();
|
|
160
|
+
}
|
|
161
|
+
catch { /* A side run that cannot be stopped is still dropped. */ }
|
|
162
|
+
try {
|
|
163
|
+
session.dispose();
|
|
164
|
+
}
|
|
165
|
+
catch { /* Cleanup cannot keep the dock open. */ }
|
|
166
|
+
}
|
|
167
|
+
/** The dock: a neutral rule, the inline question, its answer, and a hint, separated by blank rows. */
|
|
168
|
+
render(width) {
|
|
169
|
+
if (width < 1 || !this.current)
|
|
170
|
+
return [];
|
|
171
|
+
const theme = this.options.theme();
|
|
172
|
+
const inner = contentWidth(width);
|
|
173
|
+
const spinner = this.current === 'thinking'
|
|
174
|
+
? spinnerFrameAt(Date.now() - this.startedAt, getWorkingSpinnerFrames(process.env.TERM), SPINNER_INTERVAL_MS)
|
|
175
|
+
: '';
|
|
176
|
+
const marker = `${spinner ? `${spinner} ` : ''}/btw`;
|
|
177
|
+
const markerWidth = visibleWidth(marker) + 1;
|
|
178
|
+
const asked = wrapTextWithAnsi(safeText(this.question), Math.max(1, inner - markerWidth));
|
|
179
|
+
const question = asked.slice(0, QUESTION_ROWS).map((line, index) => {
|
|
180
|
+
const prefix = index === 0 ? `${theme.bold(theme.fg('accent', marker))} ` : ' '.repeat(markerWidth);
|
|
181
|
+
return prefix + theme.fg('muted', line);
|
|
182
|
+
});
|
|
183
|
+
if (asked.length > QUESTION_ROWS)
|
|
184
|
+
question[QUESTION_ROWS - 1] = truncateToWidth(question[QUESTION_ROWS - 1], inner - 1, '') + theme.fg('muted', '…');
|
|
185
|
+
const text = this.current === 'failed' ? `${this.answerText ? `${this.answerText}\n` : ''}${this.error ?? 'The side question failed.'}` : this.answerText;
|
|
186
|
+
const wrapped = text ? text.split('\n').flatMap(line => line ? wrapTextWithAnsi(safeText(line), inner) : ['']) : [];
|
|
187
|
+
const shown = wrapped.slice(0, ANSWER_ROWS).map(line => this.current === 'failed' ? theme.fg('error', line) : line);
|
|
188
|
+
const more = wrapped.length - shown.length;
|
|
189
|
+
return [
|
|
190
|
+
neutralRule(width, theme),
|
|
191
|
+
...question.map(line => padded(line, width)),
|
|
192
|
+
...(shown.length ? ['', ...shown.map(line => padded(line, width))] : []),
|
|
193
|
+
'',
|
|
194
|
+
footerRow(`${more > 0 ? `+${more} more lines · ` : ''}/btw or Esc to dismiss`, width, theme)
|
|
195
|
+
].map(line => truncateToWidth(line, width, ''));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AutocompleteProvider } from '@earendil-works/pi-tui';
|
|
2
|
+
export interface SkillEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
/** Rewrites a leading `$name` of a loaded skill into `/skill:name`. Anything else passes through, so `$5` stays text. */
|
|
7
|
+
export declare function expandSkillInvocation(text: string, skills: ReadonlySet<string>): string;
|
|
8
|
+
/** Offers the loaded skills at a `$` that starts a word. Every other completion is `base`'s. */
|
|
9
|
+
export declare function skillCompletions(base: AutocompleteProvider, skills: () => readonly SkillEntry[]): AutocompleteProvider;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Rewrites a leading `$name` of a loaded skill into `/skill:name`. Anything else passes through, so `$5` stays text. */
|
|
2
|
+
export function expandSkillInvocation(text, skills) {
|
|
3
|
+
const trimmed = text.trimStart();
|
|
4
|
+
const match = /^\$(\S+)(?=\s|$)/.exec(trimmed);
|
|
5
|
+
if (!match || !skills.has(match[1]))
|
|
6
|
+
return text;
|
|
7
|
+
return `/skill:${match[1]}${trimmed.slice(match[0].length)}`;
|
|
8
|
+
}
|
|
9
|
+
/** A `$` that starts a word, and the partial name after it, at the cursor. */
|
|
10
|
+
const SKILL_TOKEN = /(?:^|\s)\$([^\s$]*)$/;
|
|
11
|
+
/** A line whose first word is a skill invocation. */
|
|
12
|
+
const SKILL_LINE = /^\s*\$\S/;
|
|
13
|
+
/** Offers the loaded skills at a `$` that starts a word. Every other completion is `base`'s. */
|
|
14
|
+
export function skillCompletions(base, skills) {
|
|
15
|
+
return {
|
|
16
|
+
triggerCharacters: [...(base.triggerCharacters ?? []), '$'],
|
|
17
|
+
async getSuggestions(lines, cursorLine, cursorCol, options) {
|
|
18
|
+
const line = lines[cursorLine] ?? '';
|
|
19
|
+
const token = SKILL_TOKEN.exec(line.slice(0, cursorCol));
|
|
20
|
+
if (token) {
|
|
21
|
+
const partial = token[1];
|
|
22
|
+
const items = skills()
|
|
23
|
+
.filter((skill) => skill.name.startsWith(partial))
|
|
24
|
+
.map((skill) => ({ value: skill.name, label: `$${skill.name}`, description: skill.description }));
|
|
25
|
+
return items.length ? { items, prefix: `$${partial}` } : null;
|
|
26
|
+
}
|
|
27
|
+
// The arguments of a line that starts with a skill invocation are free text, not file paths.
|
|
28
|
+
if (SKILL_LINE.test(line))
|
|
29
|
+
return null;
|
|
30
|
+
return base.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
31
|
+
},
|
|
32
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
33
|
+
if (!prefix.startsWith('$'))
|
|
34
|
+
return base.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
35
|
+
const line = lines[cursorLine] ?? '';
|
|
36
|
+
const start = cursorCol - prefix.length;
|
|
37
|
+
const inserted = `$${item.value} `;
|
|
38
|
+
const newLines = [...lines];
|
|
39
|
+
newLines[cursorLine] = `${line.slice(0, start)}${inserted}${line.slice(cursorCol)}`;
|
|
40
|
+
return { lines: newLines, cursorLine, cursorCol: start + inserted.length };
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Theme } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
export interface BandState {
|
|
3
|
+
cwd: string;
|
|
4
|
+
model?: {
|
|
5
|
+
name: string;
|
|
6
|
+
reasoning: boolean;
|
|
7
|
+
contextWindow: number;
|
|
8
|
+
};
|
|
9
|
+
thinkingLevel: string;
|
|
10
|
+
branch: string | null;
|
|
11
|
+
account?: string;
|
|
12
|
+
inputTokens: number;
|
|
13
|
+
outputTokens: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function singleLine(text: string): string;
|
|
16
|
+
export declare function displayProjectName(cwd: string): string;
|
|
17
|
+
export declare function formatTokenCount(value: number): string;
|
|
18
|
+
export declare function renderStatusBand(state: BandState, width: number, theme: Theme): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Presentation adapted from Oh My Pi 18.11.3's status line; it sits in the footer below the prompt frame.
|
|
2
|
+
// Upstream MIT license and evidence are recorded in NOTICE.md and docs/terminal-tui.md.
|
|
3
|
+
import { basename, sep } from 'node:path';
|
|
4
|
+
import { parseColor, stripTerminalSequences, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
5
|
+
import { TITANIUM } from "./theme.js";
|
|
6
|
+
// OMP's shipped light theme supplies status colors separately from transcript colors.
|
|
7
|
+
const LIGHT_STATUS = {
|
|
8
|
+
...TITANIUM, darkTitanium: '#e0e0e0', electricBlue: '#875f87', subtleGray: '#808080',
|
|
9
|
+
brightAluminum: '#005f87', readoutGreen: '#005f00', titaniumGold: '#005f5f'
|
|
10
|
+
};
|
|
11
|
+
const palette = (theme) => theme.appearance === 'light' ? LIGHT_STATUS : TITANIUM;
|
|
12
|
+
export function singleLine(text) {
|
|
13
|
+
return stripTerminalSequences(text).replace(/[\x00-\x1f\x7f-\x9f]/g, ' ').replace(/ +/g, ' ').trim();
|
|
14
|
+
}
|
|
15
|
+
export function displayProjectName(cwd) {
|
|
16
|
+
return singleLine(basename(cwd) || cwd || sep);
|
|
17
|
+
}
|
|
18
|
+
export function formatTokenCount(value) {
|
|
19
|
+
const count = Math.max(0, Number.isFinite(value) ? Math.floor(value) : 0);
|
|
20
|
+
if (count >= 1_000_000_000)
|
|
21
|
+
return `${Math.floor(count / 1_000_000_000)}B`;
|
|
22
|
+
if (count >= 1_000_000)
|
|
23
|
+
return `${Math.floor(count / 1_000_000)}M`;
|
|
24
|
+
if (count >= 1_000)
|
|
25
|
+
return `${Math.floor(count / 1_000)}K`;
|
|
26
|
+
return count.toLocaleString('en-US');
|
|
27
|
+
}
|
|
28
|
+
function fitParts(parts, width) {
|
|
29
|
+
const fitted = parts.map(part => ({ ...part }));
|
|
30
|
+
const separator = ' | ';
|
|
31
|
+
const plain = () => fitted.map(part => part.text).join(separator);
|
|
32
|
+
while (fitted.length > 1 && visibleWidth(plain()) > width) {
|
|
33
|
+
const last = fitted[fitted.length - 1];
|
|
34
|
+
const otherWidth = visibleWidth(plain()) - visibleWidth(separator) - visibleWidth(last.text);
|
|
35
|
+
const available = width - otherWidth - visibleWidth(separator);
|
|
36
|
+
if (available > 0 && available < visibleWidth(last.text)) {
|
|
37
|
+
last.text = truncateToWidth(last.text, available, '', false);
|
|
38
|
+
if (visibleWidth(plain()) <= width)
|
|
39
|
+
return fitted;
|
|
40
|
+
}
|
|
41
|
+
fitted.pop();
|
|
42
|
+
}
|
|
43
|
+
if (fitted.length && visibleWidth(plain()) > width) {
|
|
44
|
+
fitted[0].text = truncateToWidth(fitted[0].text, width, '', false);
|
|
45
|
+
}
|
|
46
|
+
return fitted;
|
|
47
|
+
}
|
|
48
|
+
function color(theme, hex, text) {
|
|
49
|
+
return theme.style(text, { fg: parseColor(hex) });
|
|
50
|
+
}
|
|
51
|
+
export function renderStatusBand(state, width, theme) {
|
|
52
|
+
const size = Math.max(0, Math.floor(width));
|
|
53
|
+
if (!size)
|
|
54
|
+
return '';
|
|
55
|
+
const colors = palette(theme);
|
|
56
|
+
const model = state.model ? singleLine(state.model.name).replace(/^Claude /, '') || 'no-model' : 'no-model';
|
|
57
|
+
const account = singleLine(state.account ?? '') || 'account unknown';
|
|
58
|
+
const effort = singleLine(state.thinkingLevel) || 'off';
|
|
59
|
+
const parts = fitParts([
|
|
60
|
+
{ text: displayProjectName(state.cwd), color: colors.brightAluminum },
|
|
61
|
+
{ text: singleLine(state.branch ?? '') || '—', color: colors.readoutGreen },
|
|
62
|
+
{ text: `${account} · ${model} · ${effort}`, color: colors.electricBlue },
|
|
63
|
+
{ text: `↑ ${formatTokenCount(state.inputTokens)} ↓ ${formatTokenCount(state.outputTokens)}`, color: colors.titaniumGold }
|
|
64
|
+
], size);
|
|
65
|
+
const separator = color(theme, colors.subtleGray, ' | ');
|
|
66
|
+
return parts.map(part => color(theme, part.color, part.text)).join(separator);
|
|
67
|
+
}
|