@oh-my-pi/pi-tui 18.14.2 → 18.16.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 +64 -50
- package/README.md +1 -1
- package/dist/types/app-keybindings.d.ts +5 -0
- package/dist/types/apps/session-picker.d.ts +5 -3
- package/dist/types/chat/bash-execution.d.ts +4 -1
- package/dist/types/chat/eval-execution.d.ts +2 -0
- package/dist/types/chat/execution-shared.d.ts +14 -8
- package/dist/types/chat/transcript-render-helpers.d.ts +8 -0
- package/dist/types/chrome/dynamic-border.d.ts +6 -0
- package/dist/types/chrome/transcript-status.d.ts +2 -2
- package/dist/types/components/editor.d.ts +15 -6
- package/dist/types/components/loader.d.ts +2 -0
- package/dist/types/keybinding-matchers.d.ts +7 -0
- package/dist/types/overlays/btw-history-panel.d.ts +13 -0
- package/dist/types/overlays/btw-history.d.ts +24 -0
- package/dist/types/overlays/btw-inject.d.ts +22 -0
- package/dist/types/overlays/btw-panel.d.ts +7 -0
- package/dist/types/overlays/hook-editor.d.ts +2 -0
- package/dist/types/overlays/jobs-panel.d.ts +4 -0
- package/dist/types/overlays/session-selector.d.ts +24 -3
- package/dist/types/render/render-utils.d.ts +21 -1
- package/dist/types/status-line/startup.d.ts +0 -15
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/theme/symbols.d.ts +3 -1
- package/dist/types/theme/theme-class.d.ts +1 -0
- package/dist/types/tools/grouped-file-output.d.ts +76 -8
- package/dist/types/tools/hashline-format.d.ts +5 -0
- package/dist/types/tools/native-view.d.ts +2 -0
- package/dist/types/tools/output-meta.d.ts +27 -0
- package/dist/types/tools/read.d.ts +10 -0
- package/dist/types/tools/wait.d.ts +2 -0
- package/dist/types/utils.d.ts +4 -0
- package/package.json +9 -9
- package/src/app-keybindings.ts +5 -0
- package/src/apps/session-picker.ts +7 -3
- package/src/chat/bash-execution.ts +20 -9
- package/src/chat/chat-transcript-builder.ts +5 -2
- package/src/chat/eval-execution.ts +14 -7
- package/src/chat/execution-shared.ts +39 -18
- package/src/chat/read-tool-group.ts +56 -36
- package/src/chat/transcript-render-helpers.ts +52 -6
- package/src/chrome/dynamic-border.ts +28 -2
- package/src/chrome/transcript-status.ts +5 -3
- package/src/components/editor.ts +123 -24
- package/src/components/key-value-list.ts +9 -9
- package/src/components/loader.ts +7 -0
- package/src/components/section.ts +1 -9
- package/src/components/table.ts +1 -9
- package/src/keybinding-matchers.ts +13 -0
- package/src/overlays/btw-history-panel.ts +98 -14
- package/src/overlays/btw-history.ts +65 -0
- package/src/overlays/btw-inject.ts +91 -0
- package/src/overlays/btw-panel.ts +53 -12
- package/src/overlays/hook-editor.ts +6 -0
- package/src/overlays/jobs-panel.ts +45 -19
- package/src/overlays/model-browser.ts +65 -8
- package/src/overlays/session-selector.ts +131 -26
- package/src/overlays/usage-dashboard.ts +2 -2
- package/src/render/code-cell.ts +1 -1
- package/src/render/file-list.ts +4 -7
- package/src/render/render-utils.ts +36 -6
- package/src/setup/scenes/glyph.ts +1 -1
- package/src/status-line/startup.ts +36 -70
- package/src/terminal-capabilities.ts +5 -0
- package/src/theme/symbols.ts +16 -9
- package/src/theme/theme-class.ts +3 -0
- package/src/tmux.ts +1 -0
- package/src/tools/agent-tree.ts +13 -11
- package/src/tools/ask.ts +13 -6
- package/src/tools/ast-edit.ts +48 -93
- package/src/tools/ast-grep.ts +22 -66
- package/src/tools/bash.ts +7 -0
- package/src/tools/default-renderer.ts +1 -1
- package/src/tools/edit.ts +50 -43
- package/src/tools/fetch.ts +2 -1
- package/src/tools/find.ts +6 -3
- package/src/tools/github.ts +14 -46
- package/src/tools/glob.ts +2 -1
- package/src/tools/grep.ts +43 -287
- package/src/tools/grouped-file-output.ts +250 -64
- package/src/tools/hashline-format.ts +5 -0
- package/src/tools/json-tree.ts +1 -5
- package/src/tools/lsp.ts +53 -25
- package/src/tools/native-view.ts +11 -5
- package/src/tools/output-meta.ts +68 -4
- package/src/tools/proc-render.ts +3 -3
- package/src/tools/read.ts +14 -0
- package/src/tools/task.ts +34 -77
- package/src/tools/vibe.ts +4 -4
- package/src/tools/wait.ts +8 -5
- package/src/tools/write.ts +17 -27
- package/src/tools/yield.ts +1 -1
- package/src/utils.ts +10 -0
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Status line for the speculative first frame, before any session exists.
|
|
3
|
-
*
|
|
4
|
-
* InteractiveMode persists {@link StatusLineStartupData} (settings plus the
|
|
5
|
-
* resolved default role model and status inputs); the next launch renders it
|
|
6
|
-
* through the real {@link StatusLineComponent} as a fresh, unnamed session at
|
|
7
|
-
* the live terminal width. Path and git branch are read live; context usage is
|
|
8
|
-
* reported unknown so the gauge shows the window without a percent.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts ignore
|
|
12
|
-
* const statusLine = createStartupStatusLine(cached.statusLine);
|
|
13
|
-
* statusLine.attachToEditor(editor, getComposerStyle(shape));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
17
2
|
import type { Model } from "@oh-my-pi/pi-catalog/types";
|
|
18
3
|
import { StatusLineComponent } from "./component.js";
|
|
@@ -18,7 +18,7 @@ export declare enum NotifyProtocol {
|
|
|
18
18
|
Osc99 = "\u001B]99;;",
|
|
19
19
|
Osc9 = "\u001B]9;"
|
|
20
20
|
}
|
|
21
|
-
export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "warp" | "orca" | "otty" | "rio" | "tern" | "monstar" | "base" | "trueColor";
|
|
21
|
+
export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "warp" | "orca" | "otty" | "rio" | "tern" | "monstar" | "rex" | "base" | "trueColor";
|
|
22
22
|
/** Terminal capability details used for rendering and protocol selection. */
|
|
23
23
|
export declare class TerminalInfo {
|
|
24
24
|
readonly id: TerminalId;
|
|
@@ -2,7 +2,9 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
|
|
|
2
2
|
/**
|
|
3
3
|
* All available symbol keys organized by category.
|
|
4
4
|
*/
|
|
5
|
-
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted"
|
|
5
|
+
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted"
|
|
6
|
+
/** @deprecated Alias of `status.success`; kept so extensions and theme overrides that name it keep working. */
|
|
7
|
+
| "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "progress.filled" | "progress.empty" | "context.speculation" | "context.compaction" | "context.content" | "context.messages" | "context.buffer" | "context.free" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxDotted.horizontal" | "boxDotted.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.powerlineCapLeft" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.prewalk" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.pin" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.subscription" | "icon.advisor" | "icon.advisorClosed" | "icon.time" | "icon.omp" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.intelligence" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.vimNormal" | "icon.vimInsert" | "icon.vimVisual" | "icon.vimVisualLine" | "icon.vimReplace" | "cmd.action" | "cmd.prompt" | "cmd.extension" | "cmd.settings" | "cmd.gear" | "cmd.shield" | "cmd.wave" | "cmd.compass" | "cmd.inbox" | "cmd.swap" | "cmd.expand" | "cmd.computer" | "cmd.eye" | "cmd.todo" | "cmd.stats" | "cmd.news" | "cmd.keyboard" | "cmd.export" | "cmd.clipboard" | "cmd.share" | "cmd.broadcast" | "cmd.globe" | "cmd.copy" | "cmd.plus" | "cmd.restart" | "cmd.eraser" | "cmd.trash" | "cmd.compress" | "cmd.vibrate" | "cmd.handoff" | "cmd.history" | "cmd.question" | "cmd.rocket" | "cmd.stethoscope" | "cmd.redo" | "cmd.bug" | "cmd.memory" | "cmd.pencil" | "cmd.folderMove" | "cmd.folderPlus" | "cmd.folderMinus" | "cmd.hammer" | "cmd.power" | "cmd.cart" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.max" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "advisor.rail" | "skill.rail" | "btw.rail" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "chip.image" | "chip.video" | "chip.paste" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.launch" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move" | "key.ctrl" | "key.ctrlMac" | "key.shift" | "key.alt" | "key.altMac" | "key.super" | "key.superMac" | "key.joiner" | "key.enter" | "key.esc" | "key.tab" | "key.backspace" | "key.delete" | "key.space" | "key.up" | "key.down" | "key.left" | "key.right";
|
|
6
8
|
export type SymbolMap = Record<SymbolKey, string>;
|
|
7
9
|
/**
|
|
8
10
|
* Icon vocabulary for slash-command autocomplete type indicators. Each name
|
|
@@ -52,22 +52,35 @@ export interface FileMatchSection {
|
|
|
52
52
|
* Files with no model lines are omitted.
|
|
53
53
|
*/
|
|
54
54
|
export declare function formatFileMatches(files: string[], grouped: boolean, renderFile: (filePath: string) => FileMatchSection): GroupedFilesOutput;
|
|
55
|
-
/**
|
|
55
|
+
/** Whether a row of grouped output is a code-frame row the search matched (`*12│…`). */
|
|
56
|
+
export declare function isMatchRow(line: string): boolean;
|
|
57
|
+
/** Per-line classification of grouped output, used by renderers for file rows and hyperlinks. */
|
|
56
58
|
export interface GroupedLineContext {
|
|
57
59
|
/** Directory header, file header, or any non-header body/content line. */
|
|
58
60
|
kind: "dir" | "file" | "content";
|
|
59
61
|
/** Number of leading `#` for headers; 0 for content lines. */
|
|
60
62
|
depth: number;
|
|
61
|
-
/** Resolved absolute path of the
|
|
63
|
+
/** Resolved absolute path of the file a header points at (when resolvable). */
|
|
62
64
|
headerPath?: string;
|
|
65
|
+
/**
|
|
66
|
+
* For file headers, the full display path: the folded directory chain joined
|
|
67
|
+
* with the name, without `#` markers or the hashline tag.
|
|
68
|
+
*/
|
|
69
|
+
displayPath?: string;
|
|
70
|
+
/** For file headers, a trailing parenthesized note such as `(2 replacements)`. */
|
|
71
|
+
suffix?: string;
|
|
63
72
|
/** For content lines, the absolute path of the owning file (line hyperlinks). */
|
|
64
73
|
filePath?: string;
|
|
65
|
-
/**
|
|
66
|
-
|
|
74
|
+
/**
|
|
75
|
+
* The internal or url-like target, as written, that a header names or a
|
|
76
|
+
* content line belongs to; the caller resolves it (no filesystem path).
|
|
77
|
+
*/
|
|
78
|
+
url?: string;
|
|
67
79
|
}
|
|
68
80
|
/**
|
|
69
81
|
* Walk grouped output lines, tracking a directory stack keyed by header depth, so
|
|
70
|
-
* each header and body line can be linked back to its absolute filesystem path
|
|
82
|
+
* each header and body line can be linked back to its absolute filesystem path
|
|
83
|
+
* and each file header to its full display path.
|
|
71
84
|
* Reconstruction is stack-based (not per-blank-group) so nested directory headers
|
|
72
85
|
* resolve correctly across the whole output.
|
|
73
86
|
*
|
|
@@ -76,9 +89,64 @@ export interface GroupedLineContext {
|
|
|
76
89
|
* formatted relative to cwd regardless of the (sub)directory the search was
|
|
77
90
|
* scoped to. `fileScope` is the initial owning file for body lines that appear
|
|
78
91
|
* before any header (single-file scopes have no `#` headers); it defaults to
|
|
79
|
-
* `headerBase` and should be passed the scoped file's absolute path.
|
|
92
|
+
* `headerBase` and should be passed the scoped file's absolute path. A URL base
|
|
93
|
+
* or scope (`local://…`) is no filesystem path: headerless lines belong to the
|
|
94
|
+
* URL, and headers under a URL base get no path.
|
|
80
95
|
*/
|
|
81
96
|
export declare function classifyGroupedLines(lines: readonly string[], headerBase: string | undefined, fileScope?: string | undefined): GroupedLineContext[];
|
|
97
|
+
/**
|
|
98
|
+
* Line groups with one file per group, for terminal tree lists: `# dir/`
|
|
99
|
+
* header rows are dropped (each file row carries its full path), and every
|
|
100
|
+
* file row starts a group of its own, so each file gets its own branch instead
|
|
101
|
+
* of hanging under the previous file of its directory. Rows before a group's
|
|
102
|
+
* first file row (notes, a single-file scope's matches) keep their group.
|
|
103
|
+
*/
|
|
104
|
+
export declare function groupsByFile(groups: readonly (readonly number[])[], contexts: readonly GroupedLineContext[]): number[][];
|
|
105
|
+
/** Details fields a grouped-output tool records for links: the display base paths and resolved URL targets. */
|
|
106
|
+
export interface GroupedLinkDetails {
|
|
107
|
+
cwd?: string;
|
|
108
|
+
searchPath?: string;
|
|
109
|
+
displayTargets?: Record<string, string>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Link targets for the rows of sanitized grouped output: a file row's file, a
|
|
113
|
+
* body row's owning file, and for an internal URL the file `displayTargets`
|
|
114
|
+
* resolves it to. Destinations keep the original path bytes: while the raw
|
|
115
|
+
* `displayContent` still aligns line-for-line with the sanitized `displayText`,
|
|
116
|
+
* a row takes its raw classification's target. Raw text never links a row the
|
|
117
|
+
* sanitized output leaves unlinked.
|
|
118
|
+
*/
|
|
119
|
+
export declare function groupedLinkTargets(contexts: readonly GroupedLineContext[], displayText: string, details: GroupedLinkDetails | undefined, rawDetails: unknown): (index: number) => string | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* A memoized styler for the rows of grouped output, so a collapsed preview
|
|
122
|
+
* styles only the rows it shows. A file row is its full path (dim directory,
|
|
123
|
+
* name in {@link FILE_NAME_COLOR}; a URL whole in that color) and any dim
|
|
124
|
+
* suffix, linked to `linkTarget`'s file or, for an unresolved URL, the URL.
|
|
125
|
+
* Body rows come from `styleBody`, tabs expanded; with `linkLines` a numbered
|
|
126
|
+
* code-frame row links to its line in the owning file.
|
|
127
|
+
*/
|
|
128
|
+
export declare function createGroupedRowStyler(lines: readonly string[], contexts: readonly GroupedLineContext[], theme: Theme, options: {
|
|
129
|
+
linkTarget: (index: number) => string | undefined;
|
|
130
|
+
styleBody: (index: number) => string;
|
|
131
|
+
linkLines?: boolean;
|
|
132
|
+
}): (index: number) => string;
|
|
133
|
+
/**
|
|
134
|
+
* Fit line groups (one file each) into `budget` rows, the `… N more` summary row
|
|
135
|
+
* included: whole groups first, then the next group cut short when it can still
|
|
136
|
+
* show a row under its file row (the first group always shows its head). The
|
|
137
|
+
* summary counts the `unit`s left out, the rows `isUnit` accepts, out of `total`
|
|
138
|
+
* when the caller knows it; with none left out it counts hidden lines. `gap`
|
|
139
|
+
* rows separate shown groups (framed lists put a blank line between files).
|
|
140
|
+
*/
|
|
141
|
+
export declare function fitGroupsToBudget(groups: readonly (readonly number[])[], budget: number, options: {
|
|
142
|
+
isUnit: (index: number) => boolean;
|
|
143
|
+
unit: string;
|
|
144
|
+
total?: number;
|
|
145
|
+
gap?: number;
|
|
146
|
+
}): {
|
|
147
|
+
shown: (readonly number[])[];
|
|
148
|
+
summary: string;
|
|
149
|
+
};
|
|
82
150
|
/**
|
|
83
151
|
* Split line indices into blank-line-separated groups, mirroring
|
|
84
152
|
* `splitGroupsByBlankLine`: when any blank line is present, break on runs of
|
|
@@ -108,8 +176,8 @@ export type GroupedOutputEvent = {
|
|
|
108
176
|
};
|
|
109
177
|
/**
|
|
110
178
|
* Walk grouped output (`# dir/`, `## file.ts (suffix)`, body lines), resolving
|
|
111
|
-
* each file header to its full display path (
|
|
112
|
-
*
|
|
179
|
+
* each file header to its full display path (see {@link classifyGroupedLines})
|
|
180
|
+
* and dropping blank separator lines.
|
|
113
181
|
*/
|
|
114
182
|
export declare function walkGroupedOutput(lines: readonly string[]): Generator<GroupedOutputEvent>;
|
|
115
183
|
/** One source line for {@link numberedCode}: its number (`null` for an elision marker, which adds no line), text and match mark. */
|
|
@@ -6,6 +6,11 @@ export declare const HL_FILE_SUFFIX = "]";
|
|
|
6
6
|
export declare const HL_FILE_HASH_SEP = "#";
|
|
7
7
|
/** Number of characters in a displayed file hash. */
|
|
8
8
|
export declare const HL_FILE_HASH_LENGTH = 4;
|
|
9
|
+
/**
|
|
10
|
+
* A trailing `#TAG` file hash exactly as the hashline store writes it (uppercase
|
|
11
|
+
* hex), so a name's own `#dead` or `#v2` ending survives stripping.
|
|
12
|
+
*/
|
|
13
|
+
export declare const HASHLINE_TAG_SUFFIX_RE: RegExp;
|
|
9
14
|
/** Keyword identifying a file move operation. */
|
|
10
15
|
export declare const HL_MOVE_KEYWORD = "MV";
|
|
11
16
|
/** Keyword identifying a file removal operation. */
|
|
@@ -8,6 +8,8 @@ import type { NativeToolHead, NativeToolView, ToolRenderResult } from "./rendere
|
|
|
8
8
|
export declare function displayPath(filePath: string): string;
|
|
9
9
|
/** Configured file link for a head's path target, when the path is absolute. */
|
|
10
10
|
export declare function fileHref(filePath: string | undefined): string | undefined;
|
|
11
|
+
/** Link for a head target that is a web URL (`www.` gains `https://`), else undefined. */
|
|
12
|
+
export declare function urlHref(target: string | undefined): string | undefined;
|
|
11
13
|
/** Diff stats as one head `meta` string, `+8 −1` (U+2212 minus), or undefined when nothing changed. */
|
|
12
14
|
export declare function diffStatsMeta(added: number, removed: number): string | undefined;
|
|
13
15
|
/**
|
|
@@ -100,8 +100,21 @@ export interface LimitsMeta {
|
|
|
100
100
|
last: number;
|
|
101
101
|
};
|
|
102
102
|
selectorBase?: string;
|
|
103
|
+
/** The first cut line exceeds what one `:raw` read returns; that read stops at this many bytes. */
|
|
104
|
+
rawByteCap?: number;
|
|
103
105
|
};
|
|
104
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The bash minimizer rewrote this output: `filter` is the native filter label
|
|
109
|
+
* (`chain` for a minimized `&&`/`;` chain), the byte counts are the raw capture
|
|
110
|
+
* and the minimized text, and `artifactId` holds the raw capture.
|
|
111
|
+
*/
|
|
112
|
+
export interface MinimizedMeta {
|
|
113
|
+
filter: string;
|
|
114
|
+
inputBytes: number;
|
|
115
|
+
outputBytes: number;
|
|
116
|
+
artifactId: string;
|
|
117
|
+
}
|
|
105
118
|
/**
|
|
106
119
|
* Structured metadata for tool outputs.
|
|
107
120
|
*/
|
|
@@ -114,6 +127,7 @@ export interface OutputMeta {
|
|
|
114
127
|
pagedSource?: true;
|
|
115
128
|
diagnostics?: DiagnosticMeta;
|
|
116
129
|
limits?: LimitsMeta;
|
|
130
|
+
minimized?: MinimizedMeta;
|
|
117
131
|
}
|
|
118
132
|
/**
|
|
119
133
|
* Reformat pre-formatted diagnostic messages into a multi-level, prefix-folded
|
|
@@ -136,6 +150,13 @@ export declare function stripRawOutputArtifactNotice(text: string): {
|
|
|
136
150
|
text: string;
|
|
137
151
|
artifactId?: string;
|
|
138
152
|
};
|
|
153
|
+
/** Whether `line` is the column-cap notice {@link formatOutputNotice} emits (`[Some lines truncated to 768 chars…]`). */
|
|
154
|
+
export declare function isColumnTruncationNotice(line: string): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Whether the bracketed notice `line` carries the column-cap clause: alone, or after the parts
|
|
157
|
+
* {@link formatOutputNotice} joins before it (`[Showing lines 1-4 of 5. … Some lines truncated to 768 chars…]`).
|
|
158
|
+
*/
|
|
159
|
+
export declare function hasColumnTruncationClause(line: string): boolean;
|
|
139
160
|
/** Remove a trailing generated output notice when metadata is unavailable. */
|
|
140
161
|
export declare function stripGeneratedOutputNotice(text: string): string;
|
|
141
162
|
/** Format truncation ranges and recovery hints. */
|
|
@@ -147,6 +168,12 @@ export declare function formatTruncationMetaNotice(truncation: TruncationMeta, s
|
|
|
147
168
|
export declare function formatStyledArtifactReference(artifactId: string, theme: Theme): string;
|
|
148
169
|
/** Describe an incomplete artifact capture. */
|
|
149
170
|
export declare function formatArtifactErrorNotice(error: OutputArtifactError): string;
|
|
171
|
+
/**
|
|
172
|
+
* `git 120.4KB → 3.2KB`: the minimizer filter that ran and the bytes it took the output from and to.
|
|
173
|
+
* Native labels are short words; a rehydrated session or extension may carry any text, so the label
|
|
174
|
+
* is sanitized to one plain line, its home paths shortened to `~`, and width-bounded.
|
|
175
|
+
*/
|
|
176
|
+
export declare function formatMinimizedMeta(minimized: MinimizedMeta): string;
|
|
150
177
|
/**
|
|
151
178
|
* Format notices from OutputMeta for LLM consumption.
|
|
152
179
|
* Returns empty string if no notices needed.
|
|
@@ -111,6 +111,16 @@ export declare function splitInternalUrlSel(rawPath: string, specOf?: (scheme: s
|
|
|
111
111
|
path: string;
|
|
112
112
|
sel?: string;
|
|
113
113
|
};
|
|
114
|
+
/**
|
|
115
|
+
* Split a filesystem read target from its query (`data.json?q=.items` →
|
|
116
|
+
* `data.json`, `?q=.items`), the display twin of the read tool's JSON, SQLite
|
|
117
|
+
* and image-question parsers. URLs own their query string and stay whole, as
|
|
118
|
+
* does a `?` inside a file name.
|
|
119
|
+
*/
|
|
120
|
+
export declare function splitReadQuery(readPath: string): {
|
|
121
|
+
path: string;
|
|
122
|
+
query?: string;
|
|
123
|
+
};
|
|
114
124
|
/** Recognize HTTP URLs and www-prefixed external read targets. */
|
|
115
125
|
export declare function isReadableUrlPath(value: string): boolean;
|
|
116
126
|
/** Return the first selected line when every range has valid one-based bounds. */
|
|
@@ -17,6 +17,8 @@ export interface JobSnapshot {
|
|
|
17
17
|
type: "bash" | "task" | "eval";
|
|
18
18
|
status: "running" | "completed" | "failed" | "cancelled";
|
|
19
19
|
label: string;
|
|
20
|
+
/** What the job is for, in the model's words (a process job's `label` is its command line). */
|
|
21
|
+
intent?: string;
|
|
20
22
|
durationMs: number;
|
|
21
23
|
/** Process exit status when the job reports one. */
|
|
22
24
|
exitCode?: number;
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -36,6 +36,10 @@ export declare function truncateToWidth(text: string, maxWidth: number, ellipsis
|
|
|
36
36
|
export declare function wrapTextWithAnsi(text: string, width: number): string[];
|
|
37
37
|
export declare function extractSegments(line: string, beforeEnd: number, afterStart: number, afterLen: number, strictAfter: boolean): ExtractSegmentsResult;
|
|
38
38
|
export declare function replaceTabs(text: string): string;
|
|
39
|
+
/** Tabs expanded and line breaks collapsed to spaces, for text confined to one rendered row. */
|
|
40
|
+
export declare function singleLine(text: string): string;
|
|
41
|
+
/** A non-negative integer cell width; non-finite input becomes `fallback`. */
|
|
42
|
+
export declare function finiteWidth(width: number, fallback?: number): number;
|
|
39
43
|
/**
|
|
40
44
|
* Returns a string of n spaces. Uses a pre-allocated buffer for efficiency.
|
|
41
45
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-tui",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.16.0",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": {
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"gen:glyphs": "bun scripts/gen-glyphs.ts"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@oh-my-pi/omptype": "18.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "18.
|
|
46
|
-
"@oh-my-pi/pi-ai": "18.
|
|
47
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
48
|
-
"@oh-my-pi/pi-natives": "18.
|
|
49
|
-
"@oh-my-pi/pi-utils": "18.
|
|
50
|
-
"@oh-my-pi/pi-wire": "18.
|
|
51
|
-
"@oh-my-pi/snapcompact": "18.
|
|
44
|
+
"@oh-my-pi/omptype": "18.16.0",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "18.16.0",
|
|
46
|
+
"@oh-my-pi/pi-ai": "18.16.0",
|
|
47
|
+
"@oh-my-pi/pi-catalog": "18.16.0",
|
|
48
|
+
"@oh-my-pi/pi-natives": "18.16.0",
|
|
49
|
+
"@oh-my-pi/pi-utils": "18.16.0",
|
|
50
|
+
"@oh-my-pi/pi-wire": "18.16.0",
|
|
51
|
+
"@oh-my-pi/snapcompact": "18.16.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"kitty-vt-wasm": "^0.2.0"
|
package/src/app-keybindings.ts
CHANGED
|
@@ -44,6 +44,7 @@ interface AppKeybindings {
|
|
|
44
44
|
"app.message.followUp": true;
|
|
45
45
|
"app.retry": true;
|
|
46
46
|
"app.message.dequeue": true;
|
|
47
|
+
"app.btw.inject": true;
|
|
47
48
|
"app.clipboard.pasteImage": true;
|
|
48
49
|
"app.clipboard.pasteTextRaw": true;
|
|
49
50
|
"app.clipboard.copyLine": true;
|
|
@@ -182,6 +183,10 @@ export const KEYBINDINGS = {
|
|
|
182
183
|
defaultKeys: ["alt+up", "shift+up"],
|
|
183
184
|
description: "Dequeue message",
|
|
184
185
|
},
|
|
186
|
+
"app.btw.inject": {
|
|
187
|
+
defaultKeys: "alt+i",
|
|
188
|
+
description: "Send a /btw answer's steering prompt to the main session",
|
|
189
|
+
},
|
|
185
190
|
"app.clipboard.pasteImage": {
|
|
186
191
|
defaultKeys: getDefaultPasteImageKeys(),
|
|
187
192
|
description: "Paste image or text from clipboard",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import {
|
|
3
3
|
SessionSelectorComponent,
|
|
4
|
-
type SessionSelectorEntry,
|
|
5
4
|
type SessionHistoryMatcher,
|
|
5
|
+
type SessionListing,
|
|
6
|
+
type SessionSelectorEntry,
|
|
6
7
|
} from "../overlays/session-selector";
|
|
7
8
|
import { runStandaloneTui } from "./standalone-picker";
|
|
8
9
|
|
|
@@ -14,12 +15,14 @@ export interface SessionPickerHost<T extends SessionSelectorEntry = SessionSelec
|
|
|
14
15
|
/** Rename the session and resolve with the title as stored, or null when it did not happen. */
|
|
15
16
|
renameSession?(session: T, title: string): Promise<string | null>;
|
|
16
17
|
setSessionPinned?(session: T, pinned: boolean): Promise<void>;
|
|
17
|
-
loadAllSessions?(): Promise<T
|
|
18
|
+
loadAllSessions?(): Promise<SessionListing<T>>;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/** Presentation and capability controls for the standalone session picker. */
|
|
21
22
|
export interface SessionPickerOptions<T extends SessionSelectorEntry = SessionSelectorEntry> {
|
|
22
|
-
|
|
23
|
+
/** Complete listing replacing the preliminary `sessions` once it settles. */
|
|
24
|
+
completeSessions?: Promise<T[]>;
|
|
25
|
+
allSessions?: SessionListing<T>;
|
|
23
26
|
title?: string;
|
|
24
27
|
scopeLabel?: string | false;
|
|
25
28
|
showCwd?: boolean;
|
|
@@ -71,6 +74,7 @@ export async function selectSession<T extends SessionSelectorEntry>(
|
|
|
71
74
|
onRename: options.onRename ?? host.renameSession,
|
|
72
75
|
onSetPinned: host.setSessionPinned,
|
|
73
76
|
historyMatcher,
|
|
77
|
+
completeSessions: options.completeSessions,
|
|
74
78
|
loadAllSessions: options.allowGlobalScope === false ? undefined : host.loadAllSessions,
|
|
75
79
|
allSessions: options.allSessions,
|
|
76
80
|
getTerminalRows: () => ui.terminal.rows,
|
|
@@ -12,7 +12,7 @@ import { sanitizeText } from "@oh-my-pi/pi-utils";
|
|
|
12
12
|
import type { Terminal as XtermTerminalType } from "@oh-my-pi/pi-utils/vterm";
|
|
13
13
|
import { theme } from "../theme/theme";
|
|
14
14
|
import type { OutputArtifactError } from "../tools/streaming-output";
|
|
15
|
-
import type { TruncationMeta } from "../tools/output-meta";
|
|
15
|
+
import type { MinimizedMeta, TruncationMeta } from "../tools/output-meta";
|
|
16
16
|
import { resolveImageOptions } from "../render/render-utils";
|
|
17
17
|
import { OutputPane } from "../render/output-pane";
|
|
18
18
|
import { loadXtermTerminal, readTerminalRows, styleTerminalRow } from "../tools/terminal-output";
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
describeExecutionTool,
|
|
27
27
|
type ExecutionColorKey,
|
|
28
28
|
type ExecutionStatus,
|
|
29
|
+
executionStatusLabel,
|
|
29
30
|
PREVIEW_LINES,
|
|
30
31
|
resolveExecutionStatus,
|
|
31
32
|
} from "./execution-shared";
|
|
@@ -34,6 +35,7 @@ import { type DescribeContext, type NativeNode, type NativeUiEvent, rootToggleEx
|
|
|
34
35
|
import { NativeImageCache } from "../native/blobs";
|
|
35
36
|
import { imageLinksEnabled, linkedImageSpan, linkedImageText } from "../prompt/image-references";
|
|
36
37
|
import { Memo } from "../native/memo";
|
|
38
|
+
import type { DynamicBorder } from "../chrome/dynamic-border";
|
|
37
39
|
|
|
38
40
|
const STREAMING_LINE_CAP = PREVIEW_LINES * 5;
|
|
39
41
|
// Minimum interval between processing incoming chunks for display (ms).
|
|
@@ -59,8 +61,10 @@ export class BashExecutionComponent extends Container {
|
|
|
59
61
|
#status: ExecutionStatus = "running";
|
|
60
62
|
#exitCode: number | undefined = undefined;
|
|
61
63
|
#loader: Loader;
|
|
64
|
+
readonly #bottomBorder: DynamicBorder;
|
|
62
65
|
#truncation?: TruncationMeta;
|
|
63
66
|
#artifactError?: OutputArtifactError;
|
|
67
|
+
#minimized?: MinimizedMeta;
|
|
64
68
|
#expanded = false;
|
|
65
69
|
// Post-finalize mutation counter (FinalizableBlock.getTranscriptBlockVersion):
|
|
66
70
|
// a completed command's block still mutates on expansion toggles, and the
|
|
@@ -103,9 +107,10 @@ export class BashExecutionComponent extends Container {
|
|
|
103
107
|
// Use dim border for excluded-from-context commands (!! prefix)
|
|
104
108
|
const colorKey = excludeFromContext ? "dim" : "bashMode";
|
|
105
109
|
this.#colorKey = colorKey;
|
|
106
|
-
const { contentContainer, loader } = buildExecutionFrame(this, ui, colorKey);
|
|
110
|
+
const { contentContainer, loader, bottomBorder } = buildExecutionFrame(this, ui, colorKey);
|
|
107
111
|
this.#contentContainer = contentContainer;
|
|
108
112
|
this.#loader = loader;
|
|
113
|
+
this.#bottomBorder = bottomBorder;
|
|
109
114
|
this.#outputPane = new OutputPane(theme, {
|
|
110
115
|
expanded: false,
|
|
111
116
|
collapsedMaxLines: PREVIEW_LINES,
|
|
@@ -121,7 +126,6 @@ export class BashExecutionComponent extends Container {
|
|
|
121
126
|
// Command header
|
|
122
127
|
this.#headerText = new Text(theme.fg(colorKey, theme.bold(`$ ${command}`)), 1, 0);
|
|
123
128
|
this.#contentContainer.addChild(this.#headerText);
|
|
124
|
-
this.#contentContainer.addChild(this.#loader);
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|
|
@@ -200,6 +204,7 @@ export class BashExecutionComponent extends Container {
|
|
|
200
204
|
exitCode: this.#exitCode,
|
|
201
205
|
truncation: this.#truncation,
|
|
202
206
|
artifactError: this.#artifactError,
|
|
207
|
+
minimized: this.#minimized,
|
|
203
208
|
};
|
|
204
209
|
return dataFirst
|
|
205
210
|
? describeExecutionTool({
|
|
@@ -358,6 +363,7 @@ export class BashExecutionComponent extends Container {
|
|
|
358
363
|
output?: string;
|
|
359
364
|
truncation?: TruncationMeta;
|
|
360
365
|
artifactError?: OutputArtifactError;
|
|
366
|
+
minimized?: MinimizedMeta;
|
|
361
367
|
images?: readonly ImageContent[];
|
|
362
368
|
showImages?: boolean;
|
|
363
369
|
},
|
|
@@ -367,6 +373,7 @@ export class BashExecutionComponent extends Container {
|
|
|
367
373
|
this.#endedAt ??= performance.now();
|
|
368
374
|
this.#truncation = options?.truncation;
|
|
369
375
|
this.#artifactError = options?.artifactError;
|
|
376
|
+
this.#minimized = options?.minimized;
|
|
370
377
|
this.#images = options?.images ?? [];
|
|
371
378
|
this.#showImages = options?.showImages ?? true;
|
|
372
379
|
if (options?.output !== undefined && !this.#ptyMode) {
|
|
@@ -383,6 +390,12 @@ export class BashExecutionComponent extends Container {
|
|
|
383
390
|
this.#updateDisplay();
|
|
384
391
|
}
|
|
385
392
|
|
|
393
|
+
/** The loader animates the bottom border from outside the child tree, so teardown stops it here. */
|
|
394
|
+
override dispose(): void {
|
|
395
|
+
this.#loader.dispose();
|
|
396
|
+
super.dispose();
|
|
397
|
+
}
|
|
398
|
+
|
|
386
399
|
override render(width: number): readonly string[] {
|
|
387
400
|
if (this.#displayDirty) {
|
|
388
401
|
this.#displayDirty = false;
|
|
@@ -440,15 +453,13 @@ export class BashExecutionComponent extends Container {
|
|
|
440
453
|
}
|
|
441
454
|
}
|
|
442
455
|
|
|
443
|
-
//
|
|
444
|
-
if (this.#status
|
|
445
|
-
this.#
|
|
446
|
-
} else {
|
|
456
|
+
// The bottom border keeps the live loader until the run settles, then its exit/cancel marker.
|
|
457
|
+
if (this.#status !== "running") {
|
|
458
|
+
this.#bottomBorder.setLabel(executionStatusLabel(this.#status, this.#exitCode));
|
|
447
459
|
const footer = buildStatusFooter({
|
|
448
|
-
status: this.#status,
|
|
449
|
-
exitCode: this.#exitCode,
|
|
450
460
|
truncation: this.#truncation,
|
|
451
461
|
artifactError: this.#artifactError,
|
|
462
|
+
minimized: this.#minimized,
|
|
452
463
|
hiddenLineCount,
|
|
453
464
|
suppressHiddenCount: hasSixelOutput,
|
|
454
465
|
});
|
|
@@ -37,7 +37,7 @@ import { theme } from "../theme";
|
|
|
37
37
|
import {
|
|
38
38
|
assistantHasVisibleContent,
|
|
39
39
|
assistantUsageIsBilled,
|
|
40
|
-
|
|
40
|
+
buildAsyncResultComponent,
|
|
41
41
|
buildFileMentionBlock,
|
|
42
42
|
buildIrcMessageCard,
|
|
43
43
|
buildLaunchCompletionBlock,
|
|
@@ -356,6 +356,7 @@ export class ChatTranscriptBuilder {
|
|
|
356
356
|
component.setComplete(message.exitCode, message.cancelled, {
|
|
357
357
|
truncation: message.meta?.truncation,
|
|
358
358
|
artifactError: message.meta?.artifactError,
|
|
359
|
+
minimized: message.meta?.minimized,
|
|
359
360
|
images: message.images,
|
|
360
361
|
showImages: displayPreferences.showImages,
|
|
361
362
|
});
|
|
@@ -599,7 +600,9 @@ export class ChatTranscriptBuilder {
|
|
|
599
600
|
#appendCustomMessage(message: Extract<AgentMessage, { role: "custom" | "hookMessage" }>): void {
|
|
600
601
|
if (!message.display) return;
|
|
601
602
|
if (message.customType === "async-result") {
|
|
602
|
-
const
|
|
603
|
+
const renderer = this.#deps.getMessageRenderer?.(message.customType);
|
|
604
|
+
const component = buildAsyncResultComponent(message, renderer);
|
|
605
|
+
this.#trackExpandable(component);
|
|
603
606
|
this.container.addChild(component);
|
|
604
607
|
return;
|
|
605
608
|
}
|
|
@@ -19,12 +19,14 @@ import {
|
|
|
19
19
|
describeExecutionTool,
|
|
20
20
|
type ExecutionColorKey,
|
|
21
21
|
type ExecutionStatus,
|
|
22
|
+
executionStatusLabel,
|
|
22
23
|
PREVIEW_LINES,
|
|
23
24
|
resolveExecutionStatus,
|
|
24
25
|
} from "./execution-shared";
|
|
25
26
|
import { code, span } from "../native/describe";
|
|
26
27
|
import { type DescribeContext, type NativeNode, type NativeUiEvent, rootToggleExpanded } from "../native/node";
|
|
27
28
|
import { Memo } from "../native/memo";
|
|
29
|
+
import type { DynamicBorder } from "../chrome/dynamic-border";
|
|
28
30
|
|
|
29
31
|
export type EvalExecutionLanguage = "python" | "js";
|
|
30
32
|
|
|
@@ -32,6 +34,7 @@ export class EvalExecutionComponent extends Container {
|
|
|
32
34
|
#status: ExecutionStatus = "running";
|
|
33
35
|
#exitCode: number | undefined = undefined;
|
|
34
36
|
#loader: Loader;
|
|
37
|
+
readonly #bottomBorder: DynamicBorder;
|
|
35
38
|
#truncation?: TruncationMeta;
|
|
36
39
|
#artifactError?: OutputArtifactError;
|
|
37
40
|
#expanded = false;
|
|
@@ -72,9 +75,10 @@ export class EvalExecutionComponent extends Container {
|
|
|
72
75
|
this.#language = language;
|
|
73
76
|
|
|
74
77
|
const colorKey: ExecutionColorKey = this.#excludeFromContext ? "dim" : "pythonMode";
|
|
75
|
-
const { contentContainer, loader } = buildExecutionFrame(this, ui, colorKey);
|
|
78
|
+
const { contentContainer, loader, bottomBorder } = buildExecutionFrame(this, ui, colorKey);
|
|
76
79
|
this.#contentContainer = contentContainer;
|
|
77
80
|
this.#loader = loader;
|
|
81
|
+
this.#bottomBorder = bottomBorder;
|
|
78
82
|
this.#outputPane = new OutputPane(theme, {
|
|
79
83
|
expanded: false,
|
|
80
84
|
collapsedMaxLines: PREVIEW_LINES,
|
|
@@ -89,7 +93,6 @@ export class EvalExecutionComponent extends Container {
|
|
|
89
93
|
});
|
|
90
94
|
|
|
91
95
|
this.#contentContainer.addChild(this.#formatHeader(colorKey));
|
|
92
|
-
this.#contentContainer.addChild(this.#loader);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
/**
|
|
@@ -188,6 +191,12 @@ export class EvalExecutionComponent extends Container {
|
|
|
188
191
|
this.#updateDisplay();
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
/** The loader animates the bottom border from outside the child tree, so teardown stops it here. */
|
|
195
|
+
override dispose(): void {
|
|
196
|
+
this.#loader.dispose();
|
|
197
|
+
super.dispose();
|
|
198
|
+
}
|
|
199
|
+
|
|
191
200
|
#updateDisplay(): void {
|
|
192
201
|
// Only the collapsed preview hides lines; when expanded the footer must
|
|
193
202
|
// not keep advertising hidden lines / ctrl+o.
|
|
@@ -200,12 +209,10 @@ export class EvalExecutionComponent extends Container {
|
|
|
200
209
|
|
|
201
210
|
if (this.#outputPane.lineCount > 0) this.#contentContainer.addChild(this.#outputPane);
|
|
202
211
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
// The bottom border keeps the live loader until the run settles, then its exit/cancel marker.
|
|
213
|
+
if (this.#status !== "running") {
|
|
214
|
+
this.#bottomBorder.setLabel(executionStatusLabel(this.#status, this.#exitCode));
|
|
206
215
|
const footer = buildStatusFooter({
|
|
207
|
-
status: this.#status,
|
|
208
|
-
exitCode: this.#exitCode,
|
|
209
216
|
truncation: this.#truncation,
|
|
210
217
|
artifactError: this.#artifactError,
|
|
211
218
|
hiddenLineCount,
|