@leanandmean/coding-agent 0.74.1-scramjet.7 → 0.74.1-scramjet.70
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 +32 -0
- package/LICENSE +202 -0
- package/README.md +23 -9
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -0
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js +3 -0
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts +2 -0
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +4 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/config.d.ts +9 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +33 -22
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +12 -3
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +64 -38
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +78 -15
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1059 -203
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +21 -17
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +3 -5
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +43 -10
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/exec.d.ts +12 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +74 -28
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/template.js +4 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +9 -5
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +160 -3
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +85 -8
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +8 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +179 -28
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +24 -6
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/output-throughput-history.d.ts +25 -0
- package/dist/core/output-throughput-history.d.ts.map +1 -0
- package/dist/core/output-throughput-history.js +240 -0
- package/dist/core/output-throughput-history.js.map +1 -0
- package/dist/core/output-throughput.d.ts +43 -0
- package/dist/core/output-throughput.d.ts.map +1 -0
- package/dist/core/output-throughput.js +161 -0
- package/dist/core/output-throughput.js.map +1 -0
- package/dist/core/resource-loader.d.ts +6 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +76 -21
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/scramjet-command-parser.d.ts +4 -0
- package/dist/core/scramjet-command-parser.d.ts.map +1 -1
- package/dist/core/scramjet-command-parser.js +72 -1
- package/dist/core/scramjet-command-parser.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +61 -10
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +46 -11
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +19 -6
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +131 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +6 -2
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +4 -3
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +16 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +38 -10
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts +4 -2
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +14 -8
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +19 -8
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts +4 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +14 -6
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +5 -3
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +4 -3
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +4 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +24 -20
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +3 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +9 -3
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +3 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +8 -2
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +17 -3
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +87 -16
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +2 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +19 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +129 -36
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +3 -2
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js +2 -2
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +37 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +901 -176
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/spellcheck.d.ts +26 -0
- package/dist/modes/interactive/spellcheck.d.ts.map +1 -0
- package/dist/modes/interactive/spellcheck.js +144 -0
- package/dist/modes/interactive/spellcheck.js.map +1 -0
- package/dist/modes/interactive/theme/{dark.json → pi-dark.json} +3 -2
- package/dist/modes/interactive/theme/{light.json → pi-light.json} +27 -26
- package/dist/modes/interactive/theme/theme-schema.json +6 -1
- package/dist/modes/interactive/theme/theme.d.ts +24 -4
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +123 -22
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +5 -0
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +6 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +33 -14
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +6 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +278 -10
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +7 -6
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +17 -5
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +88 -21
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +21 -3
- package/docs/custom-provider.md +33 -4
- package/docs/extensions.md +150 -24
- package/docs/json.md +2 -0
- package/docs/keybindings.md +25 -1
- package/docs/models.md +34 -2
- package/docs/packages.md +2 -0
- package/docs/prompt-templates.md +2 -0
- package/docs/quickstart.md +1 -4
- package/docs/rpc.md +8 -6
- package/docs/sdk.md +31 -3
- package/docs/session-format.md +5 -1
- package/docs/settings.md +19 -5
- package/docs/skills.md +7 -5
- package/docs/terminal-setup.md +79 -2
- package/docs/terminal-verification.md +71 -0
- package/docs/themes.md +63 -10
- package/docs/tui.md +120 -25
- package/docs/usage.md +6 -4
- package/examples/extensions/border-status-editor.ts +2 -3
- package/examples/extensions/doom-overlay/doom-component.ts +3 -3
- package/examples/extensions/mac-system-theme.ts +3 -2
- package/examples/extensions/modal-editor.ts +13 -8
- package/examples/extensions/overlay-qa-tests.ts +22 -9
- package/examples/extensions/overlay-test.ts +5 -3
- package/examples/extensions/snake.ts +3 -1
- package/examples/extensions/space-invaders.ts +6 -3
- package/examples/extensions/subagent/index.ts +20 -17
- package/package.json +69 -58
package/docs/sdk.md
CHANGED
|
@@ -93,6 +93,12 @@ interface AgentSession {
|
|
|
93
93
|
cycleModel(): Promise<ModelCycleResult | undefined>;
|
|
94
94
|
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
95
95
|
|
|
96
|
+
// Harness-originated tool execution (resolves after the tool-result is persisted)
|
|
97
|
+
invokeHarnessTool(name: string, args: unknown, options?: { toolCallId?: string }): Promise<void>;
|
|
98
|
+
|
|
99
|
+
// Whole-journal assistant/tool costs plus live reports awaiting final append
|
|
100
|
+
getRecordedSessionCost(): number;
|
|
101
|
+
|
|
96
102
|
// State access
|
|
97
103
|
agent: Agent;
|
|
98
104
|
model: Model | undefined;
|
|
@@ -117,6 +123,8 @@ interface AgentSession {
|
|
|
117
123
|
|
|
118
124
|
Session replacement APIs such as new-session, resume, fork, and import live on `AgentSessionRuntime`, not on `AgentSession`.
|
|
119
125
|
|
|
126
|
+
`invokeHarnessTool(name, args, options?)` runs a registered tool as a harness-originated call (not model-requested) through the real execution pipeline — same events, persistence, and hooks, but no run/turn framing. Its promise resolves only after the resulting tool-result message has been persisted to the session, so a consumer can `await` it before replacing or tearing down the session. It rejects on unknown tool, a matching event failing to persist, disposal before settlement or a call after disposal, or a malformed or duplicate explicit `toolCallId`; rejection does not prove the artifact is absent, so do not blindly retry. A tool's own `execute()` error resolves as an `isError` result rather than rejecting.
|
|
127
|
+
|
|
120
128
|
### createAgentSessionRuntime() and AgentSessionRuntime
|
|
121
129
|
|
|
122
130
|
Use the runtime API when you need to replace the active session and rebuild cwd-bound runtime state.
|
|
@@ -360,7 +368,7 @@ const { session } = await createAgentSession({
|
|
|
360
368
|
- `.scramjet/skills/`
|
|
361
369
|
- `.agents/skills/` in `cwd` and ancestor directories (up to git repo root, or filesystem root when not in a repo)
|
|
362
370
|
- Project prompts (`.scramjet/prompts/`)
|
|
363
|
-
- Context files (`AGENTS.md` walking up from cwd)
|
|
371
|
+
- Context files (`CLAUDE.md` and `AGENTS.md` walking up from cwd)
|
|
364
372
|
- Session directory naming
|
|
365
373
|
|
|
366
374
|
`agentDir` is used by `DefaultResourceLoader` for:
|
|
@@ -369,7 +377,7 @@ const { session } = await createAgentSession({
|
|
|
369
377
|
- `skills/` under `agentDir` (for example `~/.scramjet/agent/skills/`)
|
|
370
378
|
- `~/.agents/skills/`
|
|
371
379
|
- Global prompts (`prompts/`)
|
|
372
|
-
- Global context
|
|
380
|
+
- Global context files (`CLAUDE.md` and `AGENTS.md`)
|
|
373
381
|
- Settings (`settings.json`)
|
|
374
382
|
- Custom models (`models.json`)
|
|
375
383
|
- Credentials (`auth.json`)
|
|
@@ -420,12 +428,30 @@ If no model is provided:
|
|
|
420
428
|
|
|
421
429
|
`cacheRetention` controls prompt-cache retention on providers that support it (Anthropic: `long` -> 1h cache TTL where the model supports it, `short` -> the default 5m TTL, `none` -> no caching). Precedence: the `cacheRetention` option (or the `--cache-retention` CLI flag), then the `PI_CACHE_RETENTION` environment variable, then the built-in default `long`. Short-lived sessions such as subagents should pass `short` since a 1h cache write costs more than a 5m one and rarely pays off for a single run.
|
|
422
430
|
|
|
423
|
-
Scramjet builds the system prompt as ordered sections and isolates the volatile environment tail (current date, working directory) into a final uncached section. On Anthropic, the stable sections are folded into a single system block carrying the cache breakpoint, so the cached prefix survives across turns even though the tail varies between sessions. Extensions can contribute stable sections via `before_agent_start` — see [extensions.md](extensions.md#system-prompt-sections).
|
|
431
|
+
Scramjet builds the system prompt as ordered sections and isolates the volatile environment tail (current date, working directory, session journal path) into a final uncached section. On Anthropic, the stable sections are folded into a single system block carrying the cache breakpoint, so the cached prefix survives across turns even though the tail varies between sessions. Extensions can contribute stable sections via `before_agent_start` — see [extensions.md](extensions.md#system-prompt-sections).
|
|
432
|
+
|
|
433
|
+
Within one accepted top-level prompt, changing the active tools regenerates the generated tool guidance while preserving that run's `before_agent_start` composition. Contributed sections are reapplied once against the regenerated base; an authoritative `systemPrompt` string remains byte-identical and receives no generated guidance. The same composition remains in effect for intra-run continuations and automatic retries. Each later accepted top-level prompt runs `before_agent_start` against a fresh generated base, and a successful same-session reload clears the prior extension instance's composition before rebuilding the runtime. Tree navigation changes messages only; the next accepted prompt performs the normal fresh composition.
|
|
434
|
+
|
|
435
|
+
For each built-in provider request, `createAgentSession()` binds the current extension runner when `before_provider_call` begins, then carries that runner through provider-neutral preparation, serialization, payload rewriting, and observation. It also captures a request-local copy of provider-neutral tool names, a bounded routed-model identity (`provider`, `id`, and `api` only), and the run-captured public `Agent.onPayload` callback without changing that callback's signature. Payload processing order is public callback → captured runner's `before_provider_request` chain → immutable `provider_request_tool_inventory` observation → transport. The public callback runs exactly once; `undefined` keeps its input unchanged, while any other return value becomes the input to extension handlers. If successful reload supersedes the captured runner before payload dispatch, the public callback still runs but the old request skips extension rewriting and observation rather than entering the replacement runtime; if replacement occurs while the old runner's chain is already in flight, its completed replacement reaches transport and only observation is suppressed. The wrapper inspects only tool names from the actual final payload and returns that exact same value to transport. Known malformed shapes expose only a bounded reason, custom APIs are `unsupported`, and custom providers that never invoke `onPayload` provide no final-schema proof. The observation does not prove transport or model behavior; see [extensions.md](extensions.md#provider_request_tool_inventory). Because this adds a member to the exported `ExtensionEvent` union, exhaustive TypeScript switches must handle it or include a default/unknown-event branch.
|
|
424
436
|
|
|
425
437
|
> Before sectioned system prompts landed, the effective default was `short` (the env var could only upgrade it to `long`). Sessions created through the SDK or CLI now default to `long`; set `PI_CACHE_RETENTION=short` or pass `cacheRetention: "short"` to restore the old behavior.
|
|
426
438
|
|
|
427
439
|
> See [examples/sdk/02-custom-model.ts](../examples/sdk/02-custom-model.ts)
|
|
428
440
|
|
|
441
|
+
### Context and output allocation
|
|
442
|
+
|
|
443
|
+
Models and usage expose one authoritative total `contextWindow`; `contextWindowBudget` and `getContextWindowBudget()` are removed. See [the migration guide](models.md#breaking-migration-from-context-budgets) before updating external model definitions or consumers.
|
|
444
|
+
|
|
445
|
+
The `createAgentSession()` stream path checks the current request's estimated input against any genuine `maxInputTokens`, then bounds output by remaining total context and the applicable output maximum. It does not subtract compaction reserve again. The same accounting applies to both built-in summarizer implementations. These are approximate checks, not exact serialized-payload tokenization; custom stream functions and later payload rewrites must honor their own constraints.
|
|
446
|
+
|
|
447
|
+
OpenRouter's implicit aggregate output maximum is omitted because it can exclude the long-context endpoint; explicit summary output limits are retained. Vercel's Anthropic route still requires an output limit. Codex still omits `max_output_tokens`. Aggregate route compatibility is not guaranteed by scalar metadata. Anthropic/Bedrock simple adapters treat explicit `maxTokens` as a combined output ceiling including thinking, rather than increasing it for reasoning.
|
|
448
|
+
|
|
449
|
+
### Provider failure recovery
|
|
450
|
+
|
|
451
|
+
AgentSession classifies the finalized persisted assistant snapshot. Built-in adapters distinguish provider failures from payload/response callback failures without changing message origin; callback exceptions persist only a fixed stage-specific error and closed diagnostic, not private exception text. Typed local allocation overflows retain compaction recovery, while arbitrary runtime errors cannot request recovery through suggestive prose.
|
|
452
|
+
|
|
453
|
+
Recognized incomplete responses can use the existing bounded agent retry loop; tools from a failed response do not execute. Provider/SDK retries remain a separate layer. See [settings.md](settings.md#retry) for numeric validation, zero semantics and Codex-only server-delay suppression, and [custom-provider.md](custom-provider.md#failure-evidence) for evidence precedence. Neither these attempt limits nor forwarding provider options guarantees a universal request-count or elapsed-time bound.
|
|
454
|
+
|
|
429
455
|
### API Keys and OAuth
|
|
430
456
|
|
|
431
457
|
API key resolution priority (handled by AuthStorage):
|
|
@@ -652,6 +678,8 @@ const { session } = await createAgentSession({ resourceLoader: loader });
|
|
|
652
678
|
|
|
653
679
|
### Context Files
|
|
654
680
|
|
|
681
|
+
Default discovery loads context broadest-to-most-specific and orders `CLAUDE.md` before `AGENTS.md` within each directory. When both same-directory files decode to exactly equal text, only `CLAUDE.md` is retained; equality suppression never crosses directory boundaries.
|
|
682
|
+
|
|
655
683
|
```typescript
|
|
656
684
|
import { createAgentSession, DefaultResourceLoader } from "@leanandmean/coding-agent";
|
|
657
685
|
|
package/docs/session-format.md
CHANGED
|
@@ -81,6 +81,7 @@ interface UserMessage {
|
|
|
81
81
|
interface AssistantMessage {
|
|
82
82
|
role: "assistant";
|
|
83
83
|
content: (TextContent | ThinkingContent | ToolCall)[];
|
|
84
|
+
origin?: "provider" | "harness"; // Explicit for new messages; absent in legacy sessions
|
|
84
85
|
api: string;
|
|
85
86
|
provider: string;
|
|
86
87
|
model: string;
|
|
@@ -96,6 +97,7 @@ interface ToolResultMessage {
|
|
|
96
97
|
toolName: string;
|
|
97
98
|
content: (TextContent | ImageContent)[];
|
|
98
99
|
details?: any; // Tool-specific metadata
|
|
100
|
+
cost?: number; // Cumulative reported invocation USD, including descendants
|
|
99
101
|
isError: boolean;
|
|
100
102
|
timestamp: number;
|
|
101
103
|
}
|
|
@@ -116,6 +118,8 @@ interface Usage {
|
|
|
116
118
|
}
|
|
117
119
|
```
|
|
118
120
|
|
|
121
|
+
Tool-result `cost` is optional additive metadata: it excludes the requesting assistant's usage and is independent of `details` and error status. Valid amounts are finite and nonnegative; explicit zero is authoritative, while absence means no generic cost was recorded, not that execution was free. This addition does not change the session format version or rewrite older records. Closed-schema readers must allow the optional field.
|
|
122
|
+
|
|
119
123
|
### Extended Message Types (from `@leanandmean/coding-agent`)
|
|
120
124
|
|
|
121
125
|
```typescript
|
|
@@ -203,7 +207,7 @@ A message in the conversation. The `message` field contains an `AgentMessage`.
|
|
|
203
207
|
|
|
204
208
|
```json
|
|
205
209
|
{"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
|
|
206
|
-
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
|
|
210
|
+
{"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"origin":"provider","provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
|
|
207
211
|
{"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}
|
|
208
212
|
```
|
|
209
213
|
|
package/docs/settings.md
CHANGED
|
@@ -38,7 +38,7 @@ Edit directly or use `/settings` for common options.
|
|
|
38
38
|
|
|
39
39
|
| Setting | Type | Default | Description |
|
|
40
40
|
|---------|------|---------|-------------|
|
|
41
|
-
| `theme` | string |
|
|
41
|
+
| `theme` | string | auto-detected | Theme name (`"dark"`, `"light"`, or custom). When unset, detected from terminal background on each startup (see [themes.md](themes.md#automatic-theme-detection)) |
|
|
42
42
|
| `quietStartup` | boolean | `false` | Hide startup header |
|
|
43
43
|
| `collapseChangelog` | boolean | `false` | Show condensed changelog after updates |
|
|
44
44
|
| `enableInstallTelemetry` | boolean | `true` | Send an anonymous install/update version ping after first install or changelog-detected updates. This does not control update checks |
|
|
@@ -47,6 +47,17 @@ Edit directly or use `/settings` for common options.
|
|
|
47
47
|
| `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) |
|
|
48
48
|
| `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
|
|
49
49
|
| `showHardwareCursor` | boolean | `false` | Show terminal cursor |
|
|
50
|
+
| `tuiMode` | string | `"retained"` | Startup-only `"retained"` or `"committed"` renderer; changes require restarting Scramjet |
|
|
51
|
+
| `dockEditor` | boolean | `true` | Keep input, adjacent widgets and footer docked in retained mode; live `/settings` control |
|
|
52
|
+
| `retainTranscriptOnExit` | boolean | `false` | Leave the full current styled transcript, widgets, rendered editor and footer in terminal scrollback on final exit from retained mode; live `/settings` control |
|
|
53
|
+
| `editorMaxHeightPercent` | number | `30` | Maximum wrapped input-text rows as 10–50% of terminal height; further reduced to fit the available space |
|
|
54
|
+
| `scrollWheelStep` | number | `3` | Retained transcript rows per wheel event, 1–20; live `/settings` control |
|
|
55
|
+
|
|
56
|
+
Exit retention preserves the current presentation, including offscreen transcript rows, colors, styling and collapsed/expanded state. It includes the rendered editor contents, not draft rows hidden by its height limit, and does not expand compacted or other-branch history. Images become labelled placeholders; selection highlighting, overlays, the scrollbar and browsing hints are excluded. This exposes rendered session and draft text to native terminal history, whose own scrollback capacity still applies. Suspension, external-editor handoffs and crash cleanup never request retention. Committed compatibility mode already writes native history and does not replay a duplicate. Session saving and `/export` are unchanged.
|
|
57
|
+
|
|
58
|
+
The editor-height percentage is a ceiling, not blank reserved space. Borders, autocomplete, widgets and the footer also require room; the input window shrinks before docking is suspended. Oversized extension content remains reachable in the undocked retained flow with a visible explanation. Undocking does not change renderer or terminal-buffer ownership. `committed` is an explicit compatibility choice with the older tail-windowed mutable-output limitation, not equivalent live browsing or a verified screen-reader mode; dock and wheel controls are unavailable there.
|
|
59
|
+
|
|
60
|
+
Numeric layout preferences are floored and clamped to their stated ranges; invalid types/non-finite values use safe defaults with settings diagnostics. Invalid explicit `tuiMode` values are rejected. Invalid explicit `retainTranscriptOnExit` values fall back to `false` with a diagnostic. These controls preserve global-write/project-override semantics: the selector identifies project overrides and shows the effective value even when editing the global preference. Failed writes show an unsaved notice and diagnostic; an in-memory value alone does not establish successful persistence. A malformed global settings file is preserved, and every blocked save attempt reports its load error even after startup diagnostics have been consumed.
|
|
50
61
|
|
|
51
62
|
### Telemetry and update checks
|
|
52
63
|
|
|
@@ -97,14 +108,18 @@ The upstream runtime can check `https://pi.dev/api/latest-version` for updates,
|
|
|
97
108
|
|
|
98
109
|
| Setting | Type | Default | Description |
|
|
99
110
|
|---------|------|---------|-------------|
|
|
100
|
-
| `retry.enabled` | boolean | `true` | Enable
|
|
111
|
+
| `retry.enabled` | boolean | `true` | Enable bounded agent retries for transient and specifically recognized incomplete responses |
|
|
101
112
|
| `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
|
|
102
113
|
| `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
|
|
103
114
|
| `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
|
|
104
115
|
| `retry.provider.maxRetries` | number | SDK default | Provider/SDK retry attempts |
|
|
105
116
|
| `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
|
|
106
117
|
|
|
107
|
-
|
|
118
|
+
Agent retries include validated missing-terminal Responses streams and accepted responses with missing bodies. Failed-turn tool calls never execute. Known quota/authentication/permission/content rejection, callback failures, and invalid evidence do not authorize retries. Provider/SDK request attempts are separate from the agent's consecutive and cumulative limits; these settings do not establish a universal HTTP-attempt or elapsed-time budget.
|
|
119
|
+
|
|
120
|
+
Codex SSE honors `retry.provider.maxRetries` (default three) and rejects server-requested waits above `retry.provider.maxRetryDelayMs`, including after the final inner attempt. That rejection suppresses outer agent retries too. Set the delay cap to `0` to disable the policy cap; long representable waits remain cancellable, and unrepresentable waits fail without retry. Other SDKs do not necessarily implement this delay cap or these request controls; forwarding an option is not proof of enforcement.
|
|
121
|
+
|
|
122
|
+
Counts must be nonnegative safe integers; millisecond settings must be integers from 0 through 2,147,483,647. Invalid values generate scoped settings diagnostics and inherit a valid lower-priority value or the default without rewriting the file. Nested `retry.provider` leaves merge individually. Zero retries disables that layer's retries; zero base delay removes intentional backoff; timeout zero retains provider/SDK semantics and does not universally disable timeouts. Codex direct API callers receive a local validation failure for invalid retry options.
|
|
108
123
|
|
|
109
124
|
```json
|
|
110
125
|
{
|
|
@@ -125,7 +140,7 @@ When a provider requests a retry delay longer than `retry.provider.maxRetryDelay
|
|
|
125
140
|
|
|
126
141
|
| Setting | Type | Default | Description |
|
|
127
142
|
|---------|------|---------|-------------|
|
|
128
|
-
| `steeringMode` | string | `"
|
|
143
|
+
| `steeringMode` | string | `"all"` | How steering messages are sent: `"all"` or `"one-at-a-time"` |
|
|
129
144
|
| `followUpMode` | string | `"one-at-a-time"` | How follow-up messages are sent: `"all"` or `"one-at-a-time"` |
|
|
130
145
|
| `transport` | string | `"sse"` | Preferred transport for providers that support multiple transports: `"sse"`, `"websocket"`, or `"auto"` |
|
|
131
146
|
|
|
@@ -135,7 +150,6 @@ When a provider requests a retry delay longer than `retry.provider.maxRetryDelay
|
|
|
135
150
|
|---------|------|---------|-------------|
|
|
136
151
|
| `terminal.showImages` | boolean | `true` | Show images in terminal (if supported) |
|
|
137
152
|
| `terminal.imageWidthCells` | number | `60` | Preferred inline image width in terminal cells |
|
|
138
|
-
| `terminal.clearOnShrink` | boolean | `false` | Clear empty rows when content shrinks (can cause flicker) |
|
|
139
153
|
| `images.autoResize` | boolean | `true` | Resize images to 2000x2000 max |
|
|
140
154
|
| `images.blockImages` | boolean | `false` | Block all images from being sent to LLM |
|
|
141
155
|
|
package/docs/skills.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Skills are self-contained capability packages that the agent loads on-demand. A skill provides specialized workflows, setup instructions, helper scripts, and reference documentation for specific tasks.
|
|
6
6
|
|
|
7
|
+
A skill is supporting material, not a Scramjet command. Loading a skill—including through `/skill:name`—does not wrap it in `<scramjet-command>`, associate it with Scramjet's command lifecycle, give it declared next-step behavior, or guarantee that it is an actively invoked executable task. When an active command uses a skill, the command's explicit Goals (or inferred outcomes for a legacy command), user decisions, trust boundaries, consumer contracts, and required side effects remain controlling; the skill supplies capabilities or guidance in support of them. Prompt templates are different again: they expand into ordinary user prompts. See the [Scramjet command authoring guide](../../scramjet/docs/command-authoring.md) for the command contract.
|
|
8
|
+
|
|
7
9
|
Scramjet implements the [Agent Skills standard](https://agentskills.io/specification), warning about most violations but remaining lenient. Scramjet allows skill names to differ from their parent directory even though the standard disallows it; that rule is suboptimal for shared skill directories used across multiple agent harnesses.
|
|
8
10
|
|
|
9
11
|
## Table of Contents
|
|
@@ -75,11 +77,11 @@ This is progressive disclosure: only descriptions are always in context, full in
|
|
|
75
77
|
Skills register as `/skill:name` commands:
|
|
76
78
|
|
|
77
79
|
```bash
|
|
78
|
-
/skill:brave-search # Load
|
|
79
|
-
/skill:pdf-tools extract # Load skill with arguments
|
|
80
|
+
/skill:brave-search # Load the skill instructions
|
|
81
|
+
/skill:pdf-tools extract # Load skill instructions with arguments
|
|
80
82
|
```
|
|
81
83
|
|
|
82
|
-
Arguments after the command are appended to the skill
|
|
84
|
+
Arguments after the command are appended to the expanded skill instructions in the same ordinary user prompt.
|
|
83
85
|
|
|
84
86
|
Toggle skill commands via `/settings` in interactive mode or in `settings.json`:
|
|
85
87
|
|
|
@@ -141,7 +143,7 @@ Per the [Agent Skills specification](https://agentskills.io/specification#frontm
|
|
|
141
143
|
| Field | Required | Description |
|
|
142
144
|
|-------|----------|-------------|
|
|
143
145
|
| `name` | Yes | Max 64 chars. Lowercase a-z, 0-9, hyphens. Unlike the standard, Scramjet does not require this to match the parent directory because that standard requirement is suboptimal for shared skill directories. |
|
|
144
|
-
| `description` | Yes |
|
|
146
|
+
| `description` | Yes | The Agent Skills specification maximum is 1024 chars. Scramjet warns above this limit but retains the complete description in the generated model-visible skills list unless model invocation is disabled. |
|
|
145
147
|
| `license` | No | License name or reference to bundled file. |
|
|
146
148
|
| `compatibility` | No | Max 500 chars. Environment requirements. |
|
|
147
149
|
| `metadata` | No | Arbitrary key-value mapping. |
|
|
@@ -179,7 +181,7 @@ Scramjet validates skills against the Agent Skills standard. Most issues produce
|
|
|
179
181
|
|
|
180
182
|
- Name exceeds 64 characters or contains invalid characters
|
|
181
183
|
- Name starts/ends with hyphen or has consecutive hyphens
|
|
182
|
-
- Description exceeds 1024 characters
|
|
184
|
+
- Description exceeds the Agent Skills specification maximum of 1024 characters. This warning is advisory: Scramjet retains the complete description in the generated model-visible skills list unless `disable-model-invocation` is `true`.
|
|
183
185
|
|
|
184
186
|
Unknown frontmatter fields are ignored.
|
|
185
187
|
|
package/docs/terminal-setup.md
CHANGED
|
@@ -2,9 +2,84 @@
|
|
|
2
2
|
|
|
3
3
|
Scramjet uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) for reliable modifier key detection. Most modern terminals support this protocol, but some require configuration.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Transcript browsing and copying
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Retained mode is the interactive default.** Scramjet owns the live transcript, scrollbar and selection instead of using terminal scrollback. Your terminal's Find feature cannot search retained history that exists only in Scramjet's memory; searching the currently painted screen, where supported, is not a full-transcript search. Use `/export` for a durable HTML view.
|
|
8
|
+
|
|
9
|
+
To use the older native-history renderer, set `"tuiMode": "committed"` in your [settings file](settings.md#ui--display) and restart Scramjet. This is a compatibility choice, not equivalent live browsing: mutable output taller than the live canvas is tail-windowed until finalized. Changing docking in `/settings` does not change renderer. The interactions below describe retained mode.
|
|
10
|
+
|
|
11
|
+
During an interactive session, Scramjet owns the alternate screen, mouse reporting, the **rightmost transcript scrollbar**, and drag selection. Wheel/trackpad input and scrollbar dragging browse retained output, including running cards. The terminal's native scrollbar/history is not the live transcript. Returning to the document bottom resumes following; passive updates do not pull you away from what you are reading. The input area is docked by default; typing there preserves the transcript anchor. `/settings` can change docking, the input-text height ceiling (30%, range 10–50%), and wheel step (3, range 1–20). Input shrinks around adjacent widgets before an oversized band falls back to explained, undocked retained browsing. Selection remains live during output and layout changes. Endpoints track component-local positions on a best-effort basis; the displayed highlight, not an earlier intended range, determines copying.
|
|
12
|
+
|
|
13
|
+
The leading dock gap shows `Session: N lines below · Ctrl+End: latest` when rendered session rows remain below the viewport. It uses existing whitespace, not another rule or the editor's hidden-draft indicators, and returns to a blank row at the tail. Narrow layouts shorten the hint while retaining **Session**, or show `Session ↓…` when the exact number cannot fit. It is not an unread count and is excluded from application copying. Scramjet choice controls have paired rules and row-based selection-following windows; partial neighboring choices remain visible, while arrows still select whole options. These changes do not alter selector cancellation, countdowns, transcript wheel ownership or approval safeguards.
|
|
14
|
+
|
|
15
|
+
Double-click to select a word or triple-click to select one displayed line, not adjacent wrapped lines. Clicks must land on the same cell within 500 ms of the preceding press; whitespace and punctuation select their own contiguous runs. Select longer ranges by ordinary dragging, including across a screen edge. After starting a drag, keep the left button held and use the wheel to extend the transcript selection, either with a stationary pointer or while adjusting its horizontal and vertical position; reversing the wheel adjusts the endpoint. Release before browsing if you want to keep the selection unchanged. A press without an initial drag does not start wheel selection. Drags cross between transcript and dock in either direction: a transcript-origin downward drag autoscrolls to the bottom before entering the dock; a dock-origin upward drag selects the displayed transcript portion without including the hidden gap below it. Wheel scrolling does not extend a selection confined to the dock. Right-click a nonempty selection or use `tui.input.copy` (Ctrl+C by default) to copy displayed text without ANSI controls or scrollbar cells. Built-in text, Markdown and editor copying omit known component padding and rejoin soft wraps, retaining genuine indentation, hard line breaks and structural boundaries. Tables, repeated quote prefixes and unannotated custom renderers retain their physical layout rather than guessing at their source. Output and waiting controls continue updating while selected. Copy snapshots the last painted selection immediately, before clipboard delivery; unpainted pointer changes cannot change that copy. A new selection must have painted before it can be copied. Resizing ends an active drag and remaps its selection; successful copy or Escape clears selection. Copy failures retain the selection and temporarily display a diagnostic on the last screen row without changing geometry; clicking that diagnostic dismisses it without selecting the covered text. Without selection, Ctrl+C retains its normal application behavior. Right-clicking any content area—including transcript, blank space, widgets, footer or editor—reads local clipboard text into the visible, focused editor at its caret without submitting. The scrollbar, overlays, selectors and approval controls do not paste. A pending repaint is flushed before reading; changing the input owner while waiting cancels the request. Repeated eligible right-clicks during one clipboard read share that read; only the latest still-current request inserts text. Other intervening input, focus loss, editing or session changes cancel a pending paste; focus-in, terminal cell-size notifications and unmodified right-button motion do not count as editing input. WSL prewarms one PowerShell process while the retained TUI runs and uses direct Windows Unicode clipboard access on explicit paste requests. It does not poll or cache clipboard contents. Startup and each read have five-second timeouts and text is limited to 10 MiB of UTF-8; brief clipboard-lock contention has a bounded acquisition retry, but failed reads are not automatically repeated. The reader closes on exit, suspension or external-editor handoff and prewarms again on resume. This avoids per-click process startup; clipboard contention and initial startup can still add latency. macOS, Wayland/X11 and Termux use their local backends. Unsupported or remote clipboard reads show an actionable error; use your terminal's Paste command instead.
|
|
16
|
+
|
|
17
|
+
Terminal-owned Copy/context menus cannot see application selection. A terminal or multiplexer that intercepts pointer input must be configured to forward it. In tmux, enable `set -g mouse on` for the application's wheel/drag path; tmux's own copy mode is a separate interaction. See [tmux.md](tmux.md) for modified-key setup.
|
|
18
|
+
|
|
19
|
+
Home/End retain editor line-start/end behavior at every scroll position. Ctrl+Home navigates to the transcript beginning; Ctrl+End navigates to the bottom and resumes following. Submitting nonempty editor input with Enter or Alt+Enter resumes following immediately, before routing bash/slash commands or queueing messages. New user-message delivery also resumes following, while passive output preserves browsing. Alt+PageUp/Alt+PageDown provide keyboard-only entry into transcript browsing; when detached, PageUp/PageDown browse and Escape returns to the tail. Docked editing and presentation toggles keep the reading anchor, while ordinary undocked editing reveals its cursor. At the tail, normal editor/list bindings apply, and focused overlays retain keyboard precedence. See [keybindings.md](keybindings.md#transcript-browsing-precedence).
|
|
20
|
+
|
|
21
|
+
Clipboard delivery uses the existing platform backend (for example `pbcopy`, `wl-copy`, `xclip`/`xsel`) or OSC 52 where appropriate. Wayland copying awaits stdin completion and a successful `wl-copy` parent exit, with a five-second subprocess timeout; observable failure tries the existing X11/OSC 52 fallbacks before releasing selection. Terminals can reject OSC 52 without acknowledgement: a successful request alone is not proof that a desktop clipboard changed. Remote sessions, clipboard security policies and other profiles require their own verification.
|
|
22
|
+
|
|
23
|
+
Orderly exit restores the shell's normal buffer without printing a transcript or clearing prior shell output by default. Enable **Retain transcript on exit** in `/settings` (`retainTranscriptOnExit: true`) to append the full current styled transcript, including offscreen rows, widgets, rendered editor contents and footer. It preserves collapsed/expanded state, excludes viewport decorations and overlays, and replaces images with labelled placeholders. Hidden draft rows and earlier compacted/other-branch content are not reconstructed; native scrollback capacity still applies. Model-scope and tool-provisioning startup notices belong to the TUI rather than leaking into the shell. Session saving is unchanged. Suspension and external-editor handoffs restore normal terminal modes without dumping transcript copies, regardless of the setting. Crash cleanup prioritizes mode restoration; terminal loss cannot guarantee a flush. For a durable rich view, use `/export`—HTML rendering is unchanged. A retained terminal smaller than 12 columns or three rows shows a resize notice and blocks ordinary input until a usable frame is restored; configured interrupt and empty-draft exit remain available.
|
|
24
|
+
|
|
25
|
+
Built-in Kitty/iTerm2 images fit the viewport without changing retained source data. Partially visible placements and images behind overlays/selection show placeholders rather than painting through other regions; scroll to reveal the full placement or clear selection. Images remain disabled inside tmux. Custom graphics wrappers must forward the optional image-height bound described in [tui.md](tui.md); arbitrary graphics envelopes are not proven compatible.
|
|
26
|
+
|
|
27
|
+
After an expired raw Escape/CSI prefix, active mouse reporting recognizes one immediately following mouse-shaped suffix even after a long idle gap. Matching suffixes are discarded rather than inserted or acted on; other typing is replayed, with incomplete candidates released after 10 ms. Consequently, identical mouse-shaped literal text in that position is consumed, and mouse suffixes themselves fragmented beyond 10 ms are not guaranteed recovery. Bracketed paste is kept separate. This bounded ambiguity policy avoids indefinitely withholding ordinary typing; it is not universal lossless framing over arbitrarily delayed connections.
|
|
28
|
+
|
|
29
|
+
### Native compatibility evidence
|
|
30
|
+
|
|
31
|
+
Native checks cover browsing, copying, live updates, resizing, approval controls, terminal handoffs and restoration; separate pixel checks cover built-in Kitty/iTerm2 images. These configurations describe earlier retained-renderer journeys, not acceptance of every subsequent change. See [Terminal verification](terminal-verification.md) for revision-specific evidence, driver details and historical limitations.
|
|
32
|
+
|
|
33
|
+
Recorded native configurations (2026-09-21):
|
|
34
|
+
|
|
35
|
+
| Path | Tested version and configuration |
|
|
36
|
+
|------|----------------------------------|
|
|
37
|
+
| Windows Terminal / WSL | Terminal 1.24.11911.0, Windows 11 build 22631, WSL2 5.15.153.1, Ubuntu 20.04.6; 120×30; existing profile unchanged |
|
|
38
|
+
| Apple Terminal | 2.14 / 455.1, macOS 15.7.9 / 24G830 ARM64; 80×24 |
|
|
39
|
+
| iTerm2 | 3.6.11 on that macOS; 80×25; `ReportRightClick=true`; inline-image consent for graphics checks |
|
|
40
|
+
| Linux VTE | Xfce Terminal 1.1.3, Ubuntu 24.04.5, X11/Xvfb/Openbox; DejaVu Sans Mono 12, 80×24, native scrollbar hidden |
|
|
41
|
+
| tmux | 3.4 on the preceding VTE path; isolated config with mouse on and status off |
|
|
42
|
+
| xterm | XTerm 390 on Ubuntu 24.04.5/X11; 80×24; `selectToClipboard=true` with its native Shift+Insert paste binding |
|
|
43
|
+
| Kitty | 0.32.2 on Ubuntu 24.04.5/X11; 80×24 interaction and 80×30 graphics profiles |
|
|
44
|
+
|
|
45
|
+
xterm's native paste binding/source differ from VTE's Ctrl+Shift+V; the recorded paste configuration is not a new Scramjet shortcut. Native xterm evidence does not verify VS Code's integrated terminal, nor does Kitty verify every Kitty-family emulator.
|
|
46
|
+
|
|
47
|
+
These checks do not establish every emulator/profile, physical trackpad's gesture characteristics, remote desktop, multiplexer, custom extension or graphics renderer. OS-generated wheel events are not physical trackpad testing; headless tests and ordinary CI do not substitute for native desktop/clipboard evidence.
|
|
48
|
+
|
|
49
|
+
### Native selection differences — deferred
|
|
50
|
+
|
|
51
|
+
Terminal-owned selection is separate from Scramjet selection. Windows Terminal reserves Shift-mouse for its own screen-cell selection; [iTerm2 uses Option to bypass mouse reporting](https://iterm2.com/faq.html). These modifiers are terminal/profile-specific, not a universal Scramjet shortcut. They do not expose unpainted retained history or extend an application selection, and native copying can include visible padding or scrollbar cells. Scramjet's copy cleanup applies only to its ordinary drag/Ctrl+C/right-click path.
|
|
52
|
+
|
|
53
|
+
Native-selection parity remains deferred. Retained edge scrolling uses a fixed speed, not the native distance-dependent acceleration demonstrated on Windows Terminal. Changing modifiers during an active edge drag can withhold its release from the application; clear the application selection to stop a stranded gesture. See the [historical comparison](terminal-verification.md#historical-windows-selection-comparison) for the observations and their limits.
|
|
54
|
+
|
|
55
|
+
## Apple Terminal
|
|
56
|
+
|
|
57
|
+
In the tested Apple Terminal 2.14 profile, Option+PageUp arrived as unmodified PageUp. Scramjet cannot distinguish those identical bytes without stealing ordinary editor paging. Use an explicit application keybinding profile instead:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"tui.viewport.pageUp": "f8",
|
|
62
|
+
"tui.viewport.pageDown": "f9"
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Set this in `~/.scramjet/agent/keybindings.json`; keyboards with media-key defaults may require Fn+F8/Fn+F9. The Apple native journey declares and exercises this temporary profile. Other tested terminal paths exercise the default Alt+PageUp/Alt+PageDown bindings. This is not an automatic runtime terminal allowlist or a claim about every keyboard layout.
|
|
67
|
+
|
|
68
|
+
## Kitty
|
|
69
|
+
|
|
70
|
+
Keyboard reporting works out of the box.
|
|
71
|
+
|
|
72
|
+
## iTerm2
|
|
73
|
+
|
|
74
|
+
Keyboard reporting works out of the box. **Right-click copying requires iTerm2's `ReportRightClick` preference** so mouse-reporting applications receive right-clicks instead of iTerm2's native context menu. The default-off profile was tested and does not support Scramjet's right-click-copy path. This is an explicit setup requirement, not a runtime allowlist or automatic fallback.
|
|
75
|
+
|
|
76
|
+
Enable the right-click-reporting option in iTerm2's Pointer settings, or quit iTerm2 and set its existing preference from another terminal:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
defaults write com.googlecode.iterm2 ReportRightClick -bool true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Restart iTerm2 afterward. This changes right-click behavior for mouse-reporting applications, not just Scramjet; Scramjet never changes the preference for you. Ctrl+C remains the application-selection copy alternative. Native iTerm2 menus still cannot access application-owned selection.
|
|
8
83
|
|
|
9
84
|
## Ghostty
|
|
10
85
|
|
|
@@ -63,6 +138,8 @@ Add to `keybindings.json` to enable `Shift+Enter` for multi-line input:
|
|
|
63
138
|
|
|
64
139
|
## Windows Terminal
|
|
65
140
|
|
|
141
|
+
With mouse reporting active, Windows Terminal 1.24.11911.0 delivers Ctrl+wheel to the application instead of applying its font-zoom shortcut. Ignoring the received event cannot hand it back to the terminal. Use the terminal's Ctrl+plus/Ctrl+minus shortcuts to zoom; these were verified on the same Windows Terminal/WSL path. Scramjet does not change your terminal preferences or synthesize desktop shortcuts. This remains a Ctrl+wheel limitation of retained mode.
|
|
142
|
+
|
|
66
143
|
Add to `settings.json` (Ctrl+Shift+, or Settings → Open JSON file) to forward the modified Enter keys Scramjet uses:
|
|
67
144
|
|
|
68
145
|
```json
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Terminal verification
|
|
2
|
+
|
|
3
|
+
Maintainer notes for the retained viewport. User operation, compatibility choices and terminal configuration belong in [Terminal Setup](terminal-setup.md). The records below preserve historical observations; moving them here does not establish acceptance of a newer revision.
|
|
4
|
+
|
|
5
|
+
## Evidence boundaries
|
|
6
|
+
|
|
7
|
+
| Check | What it establishes | What it does not establish |
|
|
8
|
+
|-------|---------------------|----------------------------|
|
|
9
|
+
| Headless TUI and production-composition tests | Parsed emitted text/cells, cursor position, intermediate frames, application input dispatch and flush ordering | Native input delivery, desktop clipboard contents or graphics pixels |
|
|
10
|
+
| Transport tests | Framing, timeout and keyboard/mouse mode sequences | Every terminal's delivery behavior |
|
|
11
|
+
| Native interaction journeys | Delivered desktop input, clipboard equality, configured terminal behavior and restoration | Every profile, physical trackpad, remote connection or custom component |
|
|
12
|
+
| Native graphics journeys | Fresh image pixels, confinement, suppression and restoration on the tested path | Arbitrary custom graphics envelopes |
|
|
13
|
+
| Driver self-tests | Required-check sets, failure detection, action receipts and cleanup predicates | A fresh native acceptance run |
|
|
14
|
+
|
|
15
|
+
The shared headless terminal feeds actual TUI writes into `@xterm/headless`; its `sendInput()` calls the application handler directly. A headless assertion and a desktop check with similar wording can therefore establish different things. Do not remove a native check without identifying the exact duplicate predicate, execution boundary and any calibration, sequencing or cleanup role.
|
|
16
|
+
|
|
17
|
+
The native journey fixture is `packages/scramjet/tests/fixtures/interactive-viewport.mjs`. Hosted interaction and graphics drivers live in `.github/scripts/terminal-probe.py` and `.github/scripts/terminal-safety.py`; `.github/scripts/test-terminal-fixtures.py` exercises their evidence predicates. `.github/workflows/terminal-feasibility.yml` owns the hosted matrix. Build the runtime before running fixtures, because they load compiled packages. Windows desktop/clipboard execution through `.github/scripts/windows-terminal-probe.sh` requires separate explicit permission.
|
|
18
|
+
|
|
19
|
+
## Exit retention and right-click latency — 2026-09-28
|
|
20
|
+
|
|
21
|
+
A user-authorized targeted Windows Terminal 1.24.11911.0 / Windows build 22631 / WSL Ubuntu journey exercised rebuilt packages on the dirty working tree based on `6c06a61812dcc9fc324b570eef35d6bbbd487a43`. An isolated production InteractiveMode used a nonempty model scope, 180 numbered styled history rows, above/below widgets, an editor draft and an underlined footer. It loaded no personal extensions and made no model calls. This was a targeted check, not full-matrix acceptance.
|
|
22
|
+
|
|
23
|
+
All 12 native predicates passed: calibrated desktop targeting, retention default off, first right-click paste, held-right-button motion paste, terminal-native paste, selector suppression, the actual `/settings` retention toggle, external-editor return, paste after that handoff, orderly exit and termios restoration. Five single-gesture motion pastes inserted exactly once in 13–25 ms from received right press to completed editor insertion; the first paste took about 36 ms. Native paste took about 0.36 ms from received payload to insertion. These are small-sample application-boundary timings, not physical-input-to-pixel latency guarantees. The fixture verified stable synthetic clipboard contents before each gesture; clipboard contention immediately after a change can still add latency.
|
|
24
|
+
|
|
25
|
+
Inspection of native screenshots confirmed styled history and the complete editor/widget/footer suffix in the restored shell. Parsing the captured application stdout with headless xterm additionally verified every numbered row and the model-scope header exactly once, their ANSI cell attributes, dock order, clean shell styles, no transcript replay between alternate-screen exit/reentry during the external-editor handoff, and zero submissions. Desktop window closure, pointer/focus restoration and absence of leftover matching Windows clipboard workers were independently verified. Deterministic tests cover default-off startup stdout isolation, both docking layouts, settings persistence/overrides/invalid values, graphics placeholders, failed snapshot restoration and clipboard lifecycle/stale-input boundaries.
|
|
26
|
+
|
|
27
|
+
The latency investigation first measured roughly 440–540 ms of per-request PowerShell startup/setup using clipboard-free output. Reusing PowerShell removed that cost, but the Forms-based reader still took roughly 150–180 ms in the native fixture. Direct Windows Unicode clipboard access with bounded lock acquisition reduced the measured delay without polling or caching clipboard contents. Preliminary runs also exposed a fixture import error, unstable synthetic clipboard seeding (including an empty terminal-native control paste), a focus-loss safety abort and an immediate native clipboard-lock rejection; those failed runs are not acceptance evidence. The final reader bounds acquisition retries before reading data and never repeats an ambiguously failed read.
|
|
28
|
+
|
|
29
|
+
Reproduction: start an isolated freshly built InteractiveMode with scoped startup and several screens of numbered styled text; calibrate desktop cells, seed and verify synthetic clipboard text, perform one stationary right-click and five press/move/release gestures while recording input and insertion timestamps, compare a native paste, exercise a real confirmation and `/settings` toggle, round-trip an external editor, then exit while scrolled into history. Check the emitted normal buffer and screenshots, termios, owned-window/pointer/focus cleanup and worker termination. Other operating systems, the full native matrix, native image pixels, both external-editor variants together, and job-control suspension were not rerun for this change; images deliberately become labelled placeholders in retained exit history.
|
|
30
|
+
|
|
31
|
+
## Paste, submission and underline diagnostics — 2026-09-28
|
|
32
|
+
|
|
33
|
+
A user-authorized Windows Terminal 1.24.11911.0 / Windows build 22631 / WSL Ubuntu journey exercised rebuilt packages on the dirty working tree based on `90b71a225075af2015338a8c31d35c79cc60a403`. An isolated production fixture used synthetic clipboard text, no personal extensions and no model calls. This was a targeted diagnostic, not full-matrix acceptance.
|
|
34
|
+
|
|
35
|
+
Before production edits, test-designer-guided regressions reproduced eight editor-submission failures, cancellation of pending paste by focus-in/cell-size notifications, ignored no-selection clicks outside the editor, a lost click during a pending repaint, and curly-underline padding/continuation errors. Actual editor spellcheck replacement and scrolling cleared underline attributes in headless tests before the changes; that control did not reproduce the reported persistent native marks.
|
|
36
|
+
|
|
37
|
+
The final native journey passed eight predicates: first-click paste over transcript, editor-click paste, selector paste suppression, Ctrl+wheel delivered without zoom, keyboard zoom, keyboard zoom restoration, bash submission following, and termios restoration. Clipboard insertion was exact and did not submit. Window closure, pointer restoration and foreground restoration were independently verified. Reproduction uses the real production composition: seed synthetic clipboard text, right-click transcript before other input, repeat over the editor, open a confirmation and verify right-click neither pastes nor answers, then browse away and submit `!printf NATIVE-SUBMIT-OUTPUT`. The pre-fix bash journey left output offscreen; the rebuilt journey followed it.
|
|
38
|
+
|
|
39
|
+
For zoom, hold Ctrl and wheel upward: stdin received `ESC[<80;11;3M` while terminal geometry remained unchanged. Ctrl+plus reduced the cell grid; Ctrl+minus restored it. This confirms terminal mouse routing precedes zoom on this path; swallowing that packet is not a zoom implementation. No terminal preferences were changed.
|
|
40
|
+
|
|
41
|
+
Native screenshots showed deterministic curly spellcheck underlines disappear after replacement with plain text and remain absent after scrolling. The separate fixed-coordinate persistent-underline report remains unresolved; the wrapping fix is not claimed as its root cause. The final focused run passed 479 TypeScript tests and 75 native-driver self-tests. Linux/macOS/tmux, physical trackpads, native graphics and the full desktop journey were not rerun. Two preliminary disposable-fixture attempts failed on fixture import/provider setup and supplied no acceptance evidence.
|
|
42
|
+
|
|
43
|
+
## Dock presentation check — 2026-09-25
|
|
44
|
+
|
|
45
|
+
A user-authorized, targeted Windows Terminal/WSL desktop journey passed **28/28 checks** against rebuilt packages on the uncommitted working tree based on `762822b4bb456bc32f79512a009ae97f6d98a4b0`. The recorded candidate digest was `2e6b47dac18baf1ef518f980dc2dd6b1c00f62e5c123fc9279a247164c13a022` (SHA-256 over sorted changed/untracked repository paths and their contents, each NUL-delimited, before this evidence-record edit). Environment: Windows Terminal 1.24.11911.0, Windows build 22631, WSL Ubuntu, Node 25.2.1; initial/restored geometry 120×30, fitting resize 102×25, plus a smaller unfit-selector case. The run deliberately recorded dirty-tree provenance; it was not the clean-checkout full-PR matrix.
|
|
46
|
+
|
|
47
|
+
The real production fixture hosted all four Scramjet selector variants, not lookalike components. Delivered desktop arrows/Enter verified framing, choices and model preview; mixed-height options exposed partial neighboring choices. Further checks covered resize/overlay restoration, genuine insufficient-space fallback, cancellation/draft restoration, explicit undocking/redocking, live continuation counts and tail blanking. Desktop clipboard reads independently matched displayed selector fragments and a transcript/editor seam selection without copying the Session decoration. Screenshots were inspected. Termios, owned-window closure, pointer position and foreground restoration all passed.
|
|
48
|
+
|
|
49
|
+
The targeted journey reused the Windows driver's owned-window/calibration/cleanup helpers and the fixture's command receipts. Reproduction uses a freshly built `interactive-viewport.mjs --production --journey`, its `selector-confirm`, `selector-select`, `selector-next`, `selector-model`, `overlay`, `close-overlay`, `update` and `copy-seam` actions, and real desktop navigation, resizing and copying. Opening receipts acknowledge mounting/flushing without awaiting an answer; `selector.result` records the later outcome. The permanent interaction drivers now require the product-selector/count checks and reject missing evidence.
|
|
50
|
+
|
|
51
|
+
Two earlier attempts exposed fixture-oracle limitations rather than production defects: uniform-height options aligned exactly with the viewport and did not exercise partial neighbors, then trailing spaces in synthetic descriptions disagreed with the trimmed screen-recording oracle. The final fixture uses mixed heights and trims its synthetic descriptions; assertions were retained. Those failed attempts are not acceptance evidence.
|
|
52
|
+
|
|
53
|
+
This verifies the declared Windows journey only. The hosted Linux/macOS/tmux interaction matrix, native Kitty/iTerm2 graphics pixels, physical trackpads and arbitrary extensions were not rerun. Deterministic tests cover image-covered/placeholder gaps, but are not native graphics acceptance. Existing broader PR verification obligations remain separate.
|
|
54
|
+
|
|
55
|
+
## Historical native journey evidence
|
|
56
|
+
|
|
57
|
+
Detailed successes, failed attempts and exact source commits are recorded in [issue #551](https://github.com/LeanAndMean/scramjet/issues/551) and [PR #560](https://github.com/LeanAndMean/scramjet/pull/560). The [recorded configuration table](terminal-setup.md#native-compatibility-evidence) describes earlier retained-renderer journeys, not blanket acceptance of subsequent changes. Match each claim to the source revision and check names in its report.
|
|
58
|
+
|
|
59
|
+
The hosted drivers require a zero fixture-exit receipt and terminal restoration; both macOS interaction and graphics drivers separately own and verify closure of their terminal process, windows and shell. The Windows driver independently attempts exit and window closure, then verifies window disappearance, pointer position and foreground restoration; a failed or unverified cleanup cannot pass. Graphics confinement compares fresh image pixels with independent single-row color bands in the safety fixture's above-editor widget and last footer row, preserving their heights and accounting for the leading dock spacer. Calibration uses screenshot pixels rather than default cell dimensions, so display scaling is measured rather than assumed. Graphics suppression checks require post-render action receipts and at least two fresh screenshots over a bounded stability interval. The representative VTE journey includes narrow wrapped editing, autocomplete acceptance and a separate committed-mode startup/promotion, approval, external-editor and suspend/resume journey. No-selection right-click checks outside the editor seed synthetic clipboard text, wait for acknowledged input, and require exact insertion without submission or changes to copy/error/terminal-paste counters over a stability interval; intercepted terminal paste remains payload-free. The current journeys also seed synthetic clipboard text and right-click the actual editor, checking exact insertion and no submission. Selection checks require an updated live footer while selection remains active, exact wrapped-editor copying, and exact copying across the transcript/editor seam in both directions. These predicates replace the older whole-frame freeze expectation; historical successes do not certify them. Linux interaction startup retains its 15-second visible-window requirement and records the owned process, pre-cleanup exit status, last window-search result and bounded failure diagnostics; terminal stdout/stderr and Openbox logs are retained with the artifact. Diagnostic observations cannot turn a failed startup into a pass. Windows desktop/clipboard execution requires separate explicit permission.
|
|
60
|
+
|
|
61
|
+
## Historical Windows selection comparison
|
|
62
|
+
|
|
63
|
+
A local comparison on 2026-09-23 used Windows Terminal 1.24.11911.0, Windows 11 build 22631 and WSL Ubuntu, with the existing terminal profile unchanged. A disposable fixture loaded the real compiled InteractiveMode with 1,500 numbered synthetic rows and isolated in-memory settings; no models or personal extensions were loaded. The installed baseline was `b2bb160d7e9f11670915c2f568bc91e3707e3ca7`; the candidate was `575f90bfd847d302eae7f52f901879920f0b8397`, rebuilt only in the development clone. Native desktop events dragged to an edge and then 16 or 100 pixels beyond the window, held there, and released outside. Screenshots, raw fixture stdin, synthetic clipboard reads and terminal-state receipts supplied the observations.
|
|
64
|
+
|
|
65
|
+
- The installed baseline and candidate `committed` mode accelerated with outside distance. For example, baseline upward screenshots advanced 112 rows over approximately 638 ms at 16 pixels, versus 584 rows over approximately 607 ms at 100 pixels. These are bounded observations, not a calibrated speed curve.
|
|
66
|
+
- Candidate `retained` mode stayed near its fixed 12.5 rows/second at both distances. Upward motion reached `ESC[<32;11;1M`, with no additional motion packet revealing outside distance; release arrived as `ESC[<0;11;1m`. Downward motion/release likewise used the bottom cell, row 21. This input path does not supply the distance needed to reproduce native acceleration merely by changing the scroll-rate formula.
|
|
67
|
+
- Retained scrolling stopped after outside release. Separate short-selection checks showed highlight persistence through a wheel-scroll round trip, right-click delivery of selected synthetic rows, and left-click clearing in all three modes. They do not establish byte-for-byte copy parity or the entire combined long-selection journey. All three successful runs restored their initial termios state.
|
|
68
|
+
|
|
69
|
+
An initial accessibility `TextPatternRange.GetText` call crashed the desktop driver; its owned window/button were cleaned up, and the replacement used screenshots rather than that API. A subsequent attempt coalesced mouse movement and did not establish selection; only the paced native-event run supports the observations above. Evidence stayed local. No terminal preferences or installed baseline files were changed. This comparison is not a complete acceptance suite, and retained/native selection parity remains unverified and demonstrably different in acceleration. `committed` remains the existing compatibility option, with its separate mutable-output browsing limitation.
|
|
70
|
+
|
|
71
|
+
The held-button wheel interaction was compared separately on the same Windows Terminal/WSL path. A press alone followed by stationary wheel input did not start native selection; after a small initial drag, the installed baseline and candidate `committed` mode extended selection on wheel input, while pre-fix retained mode kept only the initial range. The baseline checkout had advanced to `49c1f1209f8129a6dffc84c534da5b1c364ceb14` without changing its TUI implementation. The retained fix now preserves that initial-drag distinction and updates only active transcript selection endpoints. Deterministic checks cover both directions, reversal across the origin, events before repaint, release, boundaries and dock confinement. Three Windows driver cases use wheel-only desktop events and reject intervening drag-motion packets; three more mix horizontal/vertical motion with scrolling in both event orders, reverse direction, and perform a final column adjustment immediately followed by release. All six scroll again after release and independently compare exact Unicode clipboard text. The mixed-input deterministic checks also cover bursts before repaint, dragging after wheel input, final wide/combining-grapheme boundaries, and edge-timer ticks between pointer updates. They exposed and now prevent stale painted-coordinate endpoints and stale edge-timer pointer state, while retaining initial painted-row targeting and dock confinement. All six native cases passed against the rebuilt working tree in a targeted subset with explicit dirty-tree provenance, startup/calibration and restoration checks; the full native journey and other terminal paths were not rerun. These results do not resolve the deferred native-selection differences.
|
package/docs/themes.md
CHANGED
|
@@ -12,13 +12,14 @@ Themes are JSON files that define colors for the TUI.
|
|
|
12
12
|
- [Theme Format](#theme-format)
|
|
13
13
|
- [Color Tokens](#color-tokens)
|
|
14
14
|
- [Color Values](#color-values)
|
|
15
|
+
- [Terminal Background](#terminal-background)
|
|
15
16
|
- [Tips](#tips)
|
|
16
17
|
|
|
17
18
|
## Locations
|
|
18
19
|
|
|
19
20
|
Scramjet loads themes from:
|
|
20
21
|
|
|
21
|
-
- Built-in: `dark`, `light`
|
|
22
|
+
- Built-in: `pi-dark`, `pi-light`, and `scramjet-dark` (the bundled default for dark-mode terminals)
|
|
22
23
|
- Global: `~/.scramjet/agent/themes/*.json`
|
|
23
24
|
- Project: `.scramjet/themes/*.json`
|
|
24
25
|
- Packages: `themes/` directories or `pi.themes` entries in `package.json`
|
|
@@ -37,7 +38,25 @@ Select a theme via `/settings` or in `settings.json`:
|
|
|
37
38
|
}
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
An explicit `theme` setting is always authoritative — automatic detection never overrides it.
|
|
42
|
+
|
|
43
|
+
### Automatic Theme Detection
|
|
44
|
+
|
|
45
|
+
When no explicit theme is configured, scramjet classifies the terminal as light or dark on each interactive startup, then maps that classification to a theme. The classification uses the following precedence chain:
|
|
46
|
+
|
|
47
|
+
1. **`COLORFGBG` environment variable** — If set (e.g., `0;15` or `15;0;0`), the final semicolon-delimited field is interpreted as an xterm-256 color index. Its RGB approximation is classified by WCAG relative luminance (threshold 0.2) as light or dark.
|
|
48
|
+
2. **Apple Terminal heuristic** — If `TERM_PROGRAM=Apple_Terminal` and no valid `COLORFGBG` is present, classifies as `light`. Users with custom dark profiles can set `theme: "scramjet-dark"` explicitly.
|
|
49
|
+
3. **OSC 11 terminal query** — On terminals that support it (iTerm2, Kitty, WezTerm, Windows Terminal, Alacritty), scramjet queries the actual background color via the OSC 11 escape sequence. The query has a 100 ms timeout to avoid perceptible startup delay.
|
|
50
|
+
4. **Fallback** — If all of the above fail or time out, classifies as `dark`.
|
|
51
|
+
|
|
52
|
+
The classification then maps to a theme name:
|
|
53
|
+
|
|
54
|
+
- **dark** → `scramjet-dark`, scramjet's bundled default. When `scramjet-dark` is not registered — e.g. Pi used without scramjet — this falls back to `pi-dark`.
|
|
55
|
+
- **light** → `pi-light`.
|
|
56
|
+
|
|
57
|
+
Automatic detection runs on every interactive startup. The result is never persisted to settings — terminal profiles can change between launches, and detection will adapt.
|
|
58
|
+
|
|
59
|
+
> **Note:** macOS Terminal.app does not support OSC 11 queries (nor truecolor), but the Apple Terminal heuristic (step 2) correctly selects the light theme for its default configuration.
|
|
41
60
|
|
|
42
61
|
## Creating a Custom Theme
|
|
43
62
|
|
|
@@ -139,13 +158,13 @@ vim ~/.scramjet/agent/themes/my-theme.json
|
|
|
139
158
|
|
|
140
159
|
- `name` is required and must be unique.
|
|
141
160
|
- `vars` is optional. Define reusable colors here, then reference them in `colors`.
|
|
142
|
-
- `colors` must define all
|
|
161
|
+
- `colors` must define all 52 required tokens.
|
|
143
162
|
|
|
144
163
|
The `$schema` field enables editor auto-completion and validation.
|
|
145
164
|
|
|
146
165
|
## Color Tokens
|
|
147
166
|
|
|
148
|
-
Every theme must define all
|
|
167
|
+
Every theme must define all 52 color tokens. There are no optional colors.
|
|
149
168
|
|
|
150
169
|
### Core UI (11 colors)
|
|
151
170
|
|
|
@@ -235,9 +254,15 @@ Editor border colors indicating thinking level (visual hierarchy from subtle to
|
|
|
235
254
|
|-------|---------|
|
|
236
255
|
| `bashMode` | Editor border in bash mode (`!` prefix) |
|
|
237
256
|
|
|
257
|
+
### Spellcheck (1 color)
|
|
258
|
+
|
|
259
|
+
| Token | Purpose |
|
|
260
|
+
|-------|---------|
|
|
261
|
+
| `spellcheckError` | Misspelled-word foreground in the editor; the underline is applied independently |
|
|
262
|
+
|
|
238
263
|
### HTML Export (optional)
|
|
239
264
|
|
|
240
|
-
The `export` section controls colors for `/export` HTML output. If omitted, colors are derived from `userMessageBg`.
|
|
265
|
+
The `export` section controls colors for `/export` HTML output only. If omitted, colors are derived from `userMessageBg`.
|
|
241
266
|
|
|
242
267
|
```json
|
|
243
268
|
{
|
|
@@ -249,6 +274,8 @@ The `export` section controls colors for `/export` HTML output. If omitted, colo
|
|
|
249
274
|
}
|
|
250
275
|
```
|
|
251
276
|
|
|
277
|
+
> **Note:** `export.pageBg` controls the HTML export background, not the terminal background. Scramjet cannot set the terminal's background color — that is always owned by the terminal emulator. See [Terminal Background](#terminal-background) below.
|
|
278
|
+
|
|
252
279
|
## Color Values
|
|
253
280
|
|
|
254
281
|
Four formats are supported:
|
|
@@ -258,7 +285,16 @@ Four formats are supported:
|
|
|
258
285
|
| Hex | `"#ff0000"` | 6-digit hex RGB |
|
|
259
286
|
| 256-color | `39` | xterm 256-color palette index (0-255) |
|
|
260
287
|
| Variable | `"primary"` | Reference to a `vars` entry |
|
|
261
|
-
| Default | `""` | Terminal's default
|
|
288
|
+
| Default | `""` | Terminal's default foreground/background |
|
|
289
|
+
|
|
290
|
+
### Empty-String Terminal Defaults
|
|
291
|
+
|
|
292
|
+
When a color token is set to `""`, scramjet emits `\x1b[39m` (reset foreground) or `\x1b[49m` (reset background), deferring to the terminal's configured default colors. This means:
|
|
293
|
+
|
|
294
|
+
- The theme cannot guarantee contrast for empty-string tokens, because the terminal's actual foreground/background is unknown at theme time.
|
|
295
|
+
- Tokens like `text`, `userMessageText`, `customMessageText`, and `toolOutput` use `""` in the built-in themes to inherit the terminal's default foreground.
|
|
296
|
+
- The built-in `pi-light` theme also sets `spellcheckError` to `""`, preserving the terminal foreground for readable misspelled text while the editor applies the spell-check underline independently.
|
|
297
|
+
- If your terminal's default foreground creates poor contrast against themed element backgrounds, you can override these tokens with explicit colors in a custom theme.
|
|
262
298
|
|
|
263
299
|
### 256-Color Palette
|
|
264
300
|
|
|
@@ -276,20 +312,37 @@ Check truecolor support:
|
|
|
276
312
|
echo $COLORTERM # Should output "truecolor" or "24bit"
|
|
277
313
|
```
|
|
278
314
|
|
|
315
|
+
## Terminal Background
|
|
316
|
+
|
|
317
|
+
Scramjet styles individual UI elements (message boxes, tool boxes, selected items) with themed backgrounds, but it does **not** control the terminal's overall background color. The space between and around themed elements shows the terminal emulator's own background.
|
|
318
|
+
|
|
319
|
+
This is by design — setting the terminal background via escape sequences (OSC 11 mutation) is invasive, not universally supported, and affects scrollback behavior. Instead:
|
|
320
|
+
|
|
321
|
+
- The built-in `pi-light` theme is designed for white or near-white terminal backgrounds.
|
|
322
|
+
- The `scramjet-dark` theme (and Pi's `pi-dark`) is designed for dark terminal backgrounds.
|
|
323
|
+
- Automatic detection selects the appropriate theme based on your terminal's actual background.
|
|
324
|
+
- For best results, ensure your terminal profile's background color matches the theme family (light background → light theme, dark background → dark theme).
|
|
325
|
+
|
|
326
|
+
### Light Theme Contrast
|
|
327
|
+
|
|
328
|
+
The built-in light theme guarantees WCAG AA contrast (4.5:1) for explicit text-bearing foreground colors against their actual rendering surfaces (element backgrounds and white canvas). Terminal-default foregrounds, including `spellcheckError`, depend on the terminal profile and cannot carry a static theme contrast guarantee. Element backgrounds (`userMessageBg`, `toolPendingBg`, etc.) maintain at least 1.30:1 contrast against white for visual separation. These guarantees hold in both truecolor and 256-color rendering modes.
|
|
329
|
+
|
|
279
330
|
## Tips
|
|
280
331
|
|
|
281
332
|
**Dark terminals:** Use bright, saturated colors with higher contrast.
|
|
282
333
|
|
|
283
|
-
**Light terminals:** Use darker, muted colors
|
|
334
|
+
**Light terminals:** Use darker, muted colors. The built-in light theme targets 4.5:1+ contrast against white.
|
|
284
335
|
|
|
285
336
|
**Color harmony:** Start with a base palette (Nord, Gruvbox, Tokyo Night), define it in `vars`, and reference consistently.
|
|
286
337
|
|
|
287
|
-
**Testing:** Check your theme with different message types, tool states, markdown content, and long wrapped text.
|
|
338
|
+
**Testing:** Check your theme with different message types, tool states, markdown content, and long wrapped text. Test in both truecolor and 256-color mode if your users may have older terminals.
|
|
288
339
|
|
|
289
340
|
**VS Code:** Set `terminal.integrated.minimumContrastRatio` to `1` for accurate colors.
|
|
290
341
|
|
|
291
342
|
## Examples
|
|
292
343
|
|
|
293
344
|
See the built-in themes:
|
|
294
|
-
- [dark.json](../src/modes/interactive/theme/dark.json)
|
|
295
|
-
- [light.json](../src/modes/interactive/theme/light.json)
|
|
345
|
+
- [pi-dark.json](../src/modes/interactive/theme/pi-dark.json)
|
|
346
|
+
- [pi-light.json](../src/modes/interactive/theme/pi-light.json)
|
|
347
|
+
|
|
348
|
+
Scramjet's bundled default dark theme lives at [`packages/scramjet/themes/scramjet-dark.json`](../../scramjet/themes/scramjet-dark.json).
|