pi-open-tui 0.2.16 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensions/open-tui/icons.ts +207 -207
- package/extensions/open-tui/runtime.ts +174 -174
- package/extensions/open-tui/state.ts +26 -18
- package/package.json +4 -4
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
export type IconMode = "auto" | "nerd" | "ascii";
|
|
2
|
-
|
|
3
|
-
export interface IconGlyphs {
|
|
4
|
-
cwd: string;
|
|
5
|
-
session: string;
|
|
6
|
-
git: string;
|
|
7
|
-
working: string;
|
|
8
|
-
done: string;
|
|
9
|
-
context: string;
|
|
10
|
-
model: string;
|
|
11
|
-
thinking: string;
|
|
12
|
-
input: string;
|
|
13
|
-
output: string;
|
|
14
|
-
cacheHit: string;
|
|
15
|
-
cost: string;
|
|
16
|
-
speed: string;
|
|
17
|
-
latency: string;
|
|
18
|
-
stall: string;
|
|
19
|
-
extensions: string;
|
|
20
|
-
ahead: string;
|
|
21
|
-
behind: string;
|
|
22
|
-
diverged: string;
|
|
23
|
-
conflicted: string;
|
|
24
|
-
stashed: string;
|
|
25
|
-
modified: string;
|
|
26
|
-
staged: string;
|
|
27
|
-
untracked: string;
|
|
28
|
-
renamed: string;
|
|
29
|
-
deleted: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const NERD_GLYPHS: IconGlyphs = {
|
|
33
|
-
cwd: "",
|
|
34
|
-
session: "",
|
|
35
|
-
git: "",
|
|
36
|
-
working: "",
|
|
37
|
-
done: "",
|
|
38
|
-
context: "",
|
|
39
|
-
model: "",
|
|
40
|
-
thinking: "",
|
|
41
|
-
// client network view: input = upload to API, output = download from API
|
|
42
|
-
input: "",
|
|
43
|
-
output: "",
|
|
44
|
-
cacheHit: "",
|
|
45
|
-
cost: "",
|
|
46
|
-
speed: "",
|
|
47
|
-
latency: "",
|
|
48
|
-
stall: "",
|
|
49
|
-
extensions: "",
|
|
50
|
-
ahead: "↑",
|
|
51
|
-
behind: "↓",
|
|
52
|
-
diverged: "⇕",
|
|
53
|
-
conflicted: "=",
|
|
54
|
-
stashed: "$",
|
|
55
|
-
modified: "!",
|
|
56
|
-
staged: "+",
|
|
57
|
-
untracked: "?",
|
|
58
|
-
renamed: "»",
|
|
59
|
-
deleted: "✘",
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// ponytail: ASCII fallback uses compact symbols (not English words) to keep
|
|
63
|
-
// the footer's icon-like feel on non-Nerd-Font terminals. Symbols chosen to
|
|
64
|
-
// avoid collisions with the git-status set {= S ! A ? r x ^ v}.
|
|
65
|
-
const ASCII_GLYPHS: IconGlyphs = {
|
|
66
|
-
cwd: "@",
|
|
67
|
-
session: "s",
|
|
68
|
-
git: "*",
|
|
69
|
-
working: "o",
|
|
70
|
-
done: "+",
|
|
71
|
-
context: "#",
|
|
72
|
-
model: "M",
|
|
73
|
-
thinking: "~",
|
|
74
|
-
input: "↑",
|
|
75
|
-
output: "↓",
|
|
76
|
-
cacheHit: "c",
|
|
77
|
-
cost: "$",
|
|
78
|
-
speed: ">",
|
|
79
|
-
latency: "~",
|
|
80
|
-
stall: "!",
|
|
81
|
-
extensions: "&",
|
|
82
|
-
ahead: "^",
|
|
83
|
-
behind: "v",
|
|
84
|
-
diverged: "^v",
|
|
85
|
-
conflicted: "=",
|
|
86
|
-
stashed: "S",
|
|
87
|
-
modified: "!",
|
|
88
|
-
staged: "A",
|
|
89
|
-
untracked: "?",
|
|
90
|
-
renamed: "r",
|
|
91
|
-
deleted: "x",
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const NERD_FONT_TERMINALS = new Set([
|
|
95
|
-
"iTerm.app",
|
|
96
|
-
"Ghostty",
|
|
97
|
-
"WezTerm",
|
|
98
|
-
"kitty",
|
|
99
|
-
"rio",
|
|
100
|
-
"tabby",
|
|
101
|
-
"WindowsTerminal",
|
|
102
|
-
"vscode",
|
|
103
|
-
]);
|
|
104
|
-
|
|
105
|
-
export function detectNerdFont(): boolean {
|
|
106
|
-
const termProgram = process.env.TERM_PROGRAM;
|
|
107
|
-
if (termProgram && NERD_FONT_TERMINALS.has(termProgram)) return true;
|
|
108
|
-
|
|
109
|
-
const lcTerminal = process.env.LC_TERMINAL;
|
|
110
|
-
if (lcTerminal && NERD_FONT_TERMINALS.has(lcTerminal)) return true;
|
|
111
|
-
|
|
112
|
-
if (process.env.TERM === "xterm-kitty") return true;
|
|
113
|
-
|
|
114
|
-
// Windows Terminal sets WT_SESSION (not TERM_PROGRAM)
|
|
115
|
-
if (process.env.WT_SESSION) return true;
|
|
116
|
-
|
|
117
|
-
// VS Code integrated terminal
|
|
118
|
-
if (process.env.TERM_PROGRAM === "vscode") return true;
|
|
119
|
-
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function resolveIconMode(mode: IconMode): "nerd" | "ascii" {
|
|
124
|
-
if (mode === "nerd") return "nerd";
|
|
125
|
-
if (mode === "ascii") return "ascii";
|
|
126
|
-
return detectNerdFont() ? "nerd" : "ascii";
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function resolveGlyphs(mode: IconMode): IconGlyphs {
|
|
130
|
-
const resolved = resolveIconMode(mode);
|
|
131
|
-
return resolved === "nerd" ? NERD_GLYPHS : ASCII_GLYPHS;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const RUNTIME_SYMBOLS: Record<string, string> = {
|
|
135
|
-
nodejs: "\uE718",
|
|
136
|
-
rust: "\uE7A8",
|
|
137
|
-
go: "\uE626",
|
|
138
|
-
python: "\uE73C",
|
|
139
|
-
ruby: "\uE739",
|
|
140
|
-
java: "\uE256",
|
|
141
|
-
cpp: "\uE61D",
|
|
142
|
-
c: "\uE61E",
|
|
143
|
-
swift: "\uE755",
|
|
144
|
-
kotlin: "\uE634",
|
|
145
|
-
deno: "\uE7FB",
|
|
146
|
-
bun: "\
|
|
147
|
-
php: "\uE73D",
|
|
148
|
-
haskell: "\uE777",
|
|
149
|
-
julia: "\uE624",
|
|
150
|
-
lua: "\uE620",
|
|
151
|
-
elixir: "\uE62B",
|
|
152
|
-
erlang: "\uE7B1",
|
|
153
|
-
gleam: "\uE6B4",
|
|
154
|
-
crystal: "\uE62F",
|
|
155
|
-
dart: "\uE7C0",
|
|
156
|
-
nim: "\uE677",
|
|
157
|
-
zig: "\uE6A9",
|
|
158
|
-
ocaml: "\uE67A",
|
|
159
|
-
clojure: "\uE76A",
|
|
160
|
-
scala: "\uE747",
|
|
161
|
-
perl: "\uE769",
|
|
162
|
-
r: "\uE68A",
|
|
163
|
-
elm: "\uE62C",
|
|
164
|
-
haxe: "\uE7B7",
|
|
165
|
-
vagrant: "\uE21A",
|
|
166
|
-
terraform: "\uE1A5",
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const RUNTIME_ASCII_SYMBOLS: Record<string, string> = {
|
|
170
|
-
nodejs: "node",
|
|
171
|
-
rust: "rs",
|
|
172
|
-
go: "go",
|
|
173
|
-
python: "py",
|
|
174
|
-
ruby: "rb",
|
|
175
|
-
java: "java",
|
|
176
|
-
swift: "swift",
|
|
177
|
-
kotlin: "kt",
|
|
178
|
-
cpp: "c++",
|
|
179
|
-
c: "c",
|
|
180
|
-
deno: "deno",
|
|
181
|
-
bun: "bun",
|
|
182
|
-
php: "php",
|
|
183
|
-
haskell: "hs",
|
|
184
|
-
julia: "jl",
|
|
185
|
-
lua: "lua",
|
|
186
|
-
elixir: "ex",
|
|
187
|
-
erlang: "erl",
|
|
188
|
-
gleam: "gleam",
|
|
189
|
-
crystal: "cr",
|
|
190
|
-
dart: "dart",
|
|
191
|
-
nim: "nim",
|
|
192
|
-
zig: "zig",
|
|
193
|
-
ocaml: "ml",
|
|
194
|
-
clojure: "clj",
|
|
195
|
-
scala: "scala",
|
|
196
|
-
perl: "pl",
|
|
197
|
-
r: "R",
|
|
198
|
-
elm: "elm",
|
|
199
|
-
haxe: "hx",
|
|
200
|
-
vagrant: "vag",
|
|
201
|
-
terraform: "tf",
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
export function runtimeSymbol(name: string, mode: IconMode): string {
|
|
205
|
-
if (resolveIconMode(mode) === "ascii") return RUNTIME_ASCII_SYMBOLS[name] ?? name;
|
|
206
|
-
return RUNTIME_SYMBOLS[name] ?? "";
|
|
207
|
-
}
|
|
1
|
+
export type IconMode = "auto" | "nerd" | "ascii";
|
|
2
|
+
|
|
3
|
+
export interface IconGlyphs {
|
|
4
|
+
cwd: string;
|
|
5
|
+
session: string;
|
|
6
|
+
git: string;
|
|
7
|
+
working: string;
|
|
8
|
+
done: string;
|
|
9
|
+
context: string;
|
|
10
|
+
model: string;
|
|
11
|
+
thinking: string;
|
|
12
|
+
input: string;
|
|
13
|
+
output: string;
|
|
14
|
+
cacheHit: string;
|
|
15
|
+
cost: string;
|
|
16
|
+
speed: string;
|
|
17
|
+
latency: string;
|
|
18
|
+
stall: string;
|
|
19
|
+
extensions: string;
|
|
20
|
+
ahead: string;
|
|
21
|
+
behind: string;
|
|
22
|
+
diverged: string;
|
|
23
|
+
conflicted: string;
|
|
24
|
+
stashed: string;
|
|
25
|
+
modified: string;
|
|
26
|
+
staged: string;
|
|
27
|
+
untracked: string;
|
|
28
|
+
renamed: string;
|
|
29
|
+
deleted: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const NERD_GLYPHS: IconGlyphs = {
|
|
33
|
+
cwd: "",
|
|
34
|
+
session: "",
|
|
35
|
+
git: "",
|
|
36
|
+
working: "",
|
|
37
|
+
done: "",
|
|
38
|
+
context: "",
|
|
39
|
+
model: "",
|
|
40
|
+
thinking: "",
|
|
41
|
+
// client network view: input = upload to API, output = download from API
|
|
42
|
+
input: "",
|
|
43
|
+
output: "",
|
|
44
|
+
cacheHit: "",
|
|
45
|
+
cost: "",
|
|
46
|
+
speed: "",
|
|
47
|
+
latency: "",
|
|
48
|
+
stall: "",
|
|
49
|
+
extensions: "",
|
|
50
|
+
ahead: "↑",
|
|
51
|
+
behind: "↓",
|
|
52
|
+
diverged: "⇕",
|
|
53
|
+
conflicted: "=",
|
|
54
|
+
stashed: "$",
|
|
55
|
+
modified: "!",
|
|
56
|
+
staged: "+",
|
|
57
|
+
untracked: "?",
|
|
58
|
+
renamed: "»",
|
|
59
|
+
deleted: "✘",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ponytail: ASCII fallback uses compact symbols (not English words) to keep
|
|
63
|
+
// the footer's icon-like feel on non-Nerd-Font terminals. Symbols chosen to
|
|
64
|
+
// avoid collisions with the git-status set {= S ! A ? r x ^ v}.
|
|
65
|
+
const ASCII_GLYPHS: IconGlyphs = {
|
|
66
|
+
cwd: "@",
|
|
67
|
+
session: "s",
|
|
68
|
+
git: "*",
|
|
69
|
+
working: "o",
|
|
70
|
+
done: "+",
|
|
71
|
+
context: "#",
|
|
72
|
+
model: "M",
|
|
73
|
+
thinking: "~",
|
|
74
|
+
input: "↑",
|
|
75
|
+
output: "↓",
|
|
76
|
+
cacheHit: "c",
|
|
77
|
+
cost: "$",
|
|
78
|
+
speed: ">",
|
|
79
|
+
latency: "~",
|
|
80
|
+
stall: "!",
|
|
81
|
+
extensions: "&",
|
|
82
|
+
ahead: "^",
|
|
83
|
+
behind: "v",
|
|
84
|
+
diverged: "^v",
|
|
85
|
+
conflicted: "=",
|
|
86
|
+
stashed: "S",
|
|
87
|
+
modified: "!",
|
|
88
|
+
staged: "A",
|
|
89
|
+
untracked: "?",
|
|
90
|
+
renamed: "r",
|
|
91
|
+
deleted: "x",
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const NERD_FONT_TERMINALS = new Set([
|
|
95
|
+
"iTerm.app",
|
|
96
|
+
"Ghostty",
|
|
97
|
+
"WezTerm",
|
|
98
|
+
"kitty",
|
|
99
|
+
"rio",
|
|
100
|
+
"tabby",
|
|
101
|
+
"WindowsTerminal",
|
|
102
|
+
"vscode",
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
export function detectNerdFont(): boolean {
|
|
106
|
+
const termProgram = process.env.TERM_PROGRAM;
|
|
107
|
+
if (termProgram && NERD_FONT_TERMINALS.has(termProgram)) return true;
|
|
108
|
+
|
|
109
|
+
const lcTerminal = process.env.LC_TERMINAL;
|
|
110
|
+
if (lcTerminal && NERD_FONT_TERMINALS.has(lcTerminal)) return true;
|
|
111
|
+
|
|
112
|
+
if (process.env.TERM === "xterm-kitty") return true;
|
|
113
|
+
|
|
114
|
+
// Windows Terminal sets WT_SESSION (not TERM_PROGRAM)
|
|
115
|
+
if (process.env.WT_SESSION) return true;
|
|
116
|
+
|
|
117
|
+
// VS Code integrated terminal
|
|
118
|
+
if (process.env.TERM_PROGRAM === "vscode") return true;
|
|
119
|
+
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function resolveIconMode(mode: IconMode): "nerd" | "ascii" {
|
|
124
|
+
if (mode === "nerd") return "nerd";
|
|
125
|
+
if (mode === "ascii") return "ascii";
|
|
126
|
+
return detectNerdFont() ? "nerd" : "ascii";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function resolveGlyphs(mode: IconMode): IconGlyphs {
|
|
130
|
+
const resolved = resolveIconMode(mode);
|
|
131
|
+
return resolved === "nerd" ? NERD_GLYPHS : ASCII_GLYPHS;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const RUNTIME_SYMBOLS: Record<string, string> = {
|
|
135
|
+
nodejs: "\uE718",
|
|
136
|
+
rust: "\uE7A8",
|
|
137
|
+
go: "\uE626",
|
|
138
|
+
python: "\uE73C",
|
|
139
|
+
ruby: "\uE739",
|
|
140
|
+
java: "\uE256",
|
|
141
|
+
cpp: "\uE61D",
|
|
142
|
+
c: "\uE61E",
|
|
143
|
+
swift: "\uE755",
|
|
144
|
+
kotlin: "\uE634",
|
|
145
|
+
deno: "\uE7FB",
|
|
146
|
+
bun: "\uE76F",
|
|
147
|
+
php: "\uE73D",
|
|
148
|
+
haskell: "\uE777",
|
|
149
|
+
julia: "\uE624",
|
|
150
|
+
lua: "\uE620",
|
|
151
|
+
elixir: "\uE62B",
|
|
152
|
+
erlang: "\uE7B1",
|
|
153
|
+
gleam: "\uE6B4",
|
|
154
|
+
crystal: "\uE62F",
|
|
155
|
+
dart: "\uE7C0",
|
|
156
|
+
nim: "\uE677",
|
|
157
|
+
zig: "\uE6A9",
|
|
158
|
+
ocaml: "\uE67A",
|
|
159
|
+
clojure: "\uE76A",
|
|
160
|
+
scala: "\uE747",
|
|
161
|
+
perl: "\uE769",
|
|
162
|
+
r: "\uE68A",
|
|
163
|
+
elm: "\uE62C",
|
|
164
|
+
haxe: "\uE7B7",
|
|
165
|
+
vagrant: "\uE21A",
|
|
166
|
+
terraform: "\uE1A5",
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const RUNTIME_ASCII_SYMBOLS: Record<string, string> = {
|
|
170
|
+
nodejs: "node",
|
|
171
|
+
rust: "rs",
|
|
172
|
+
go: "go",
|
|
173
|
+
python: "py",
|
|
174
|
+
ruby: "rb",
|
|
175
|
+
java: "java",
|
|
176
|
+
swift: "swift",
|
|
177
|
+
kotlin: "kt",
|
|
178
|
+
cpp: "c++",
|
|
179
|
+
c: "c",
|
|
180
|
+
deno: "deno",
|
|
181
|
+
bun: "bun",
|
|
182
|
+
php: "php",
|
|
183
|
+
haskell: "hs",
|
|
184
|
+
julia: "jl",
|
|
185
|
+
lua: "lua",
|
|
186
|
+
elixir: "ex",
|
|
187
|
+
erlang: "erl",
|
|
188
|
+
gleam: "gleam",
|
|
189
|
+
crystal: "cr",
|
|
190
|
+
dart: "dart",
|
|
191
|
+
nim: "nim",
|
|
192
|
+
zig: "zig",
|
|
193
|
+
ocaml: "ml",
|
|
194
|
+
clojure: "clj",
|
|
195
|
+
scala: "scala",
|
|
196
|
+
perl: "pl",
|
|
197
|
+
r: "R",
|
|
198
|
+
elm: "elm",
|
|
199
|
+
haxe: "hx",
|
|
200
|
+
vagrant: "vag",
|
|
201
|
+
terraform: "tf",
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export function runtimeSymbol(name: string, mode: IconMode): string {
|
|
205
|
+
if (resolveIconMode(mode) === "ascii") return RUNTIME_ASCII_SYMBOLS[name] ?? name;
|
|
206
|
+
return RUNTIME_SYMBOLS[name] ?? "";
|
|
207
|
+
}
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { promisify } from "node:util";
|
|
5
|
-
|
|
6
|
-
const execFileAsync = promisify(execFile);
|
|
7
|
-
const VERSION_TIMEOUT_MS = 2500;
|
|
8
|
-
|
|
9
|
-
export interface RuntimeInfo {
|
|
10
|
-
name: string;
|
|
11
|
-
version?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface RuntimeDef {
|
|
15
|
-
name: string;
|
|
16
|
-
files: readonly string[];
|
|
17
|
-
folders?: readonly string[];
|
|
18
|
-
extensions?: readonly string[];
|
|
19
|
-
env?: string;
|
|
20
|
-
versionCommand?: { cmd: string; args?: string[]; pattern?: RegExp };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const RUNTIMES: readonly RuntimeDef[] = [
|
|
24
|
-
{ name: "
|
|
25
|
-
{ name: "
|
|
26
|
-
{ name: "
|
|
27
|
-
{ name: "
|
|
28
|
-
{ name: "
|
|
29
|
-
{ name: "
|
|
30
|
-
{ name: "
|
|
31
|
-
{ name: "
|
|
32
|
-
{ name: "
|
|
33
|
-
{ name: "
|
|
34
|
-
{ name: "
|
|
35
|
-
{ name: "
|
|
36
|
-
{ name: "php", files: ["composer.json"], versionCommand: { cmd: "php", args: ["--version"], pattern: /PHP\s+(\d+\.\d+\.\d+)/ } },
|
|
37
|
-
{ name: "haskell", files: ["stack.yaml", "cabal.project", ".cabal"], versionCommand: { cmd: "ghc", args: ["--version"], pattern: /(\d+\.\d+\.\d+)/ } },
|
|
38
|
-
{ name: "julia", files: ["Project.toml", "Manifest.toml"], versionCommand: { cmd: "julia", args: ["--version"], pattern: /julia\s+(\d+\.\d+\.\d+)/ } },
|
|
39
|
-
{ name: "lua", files: ["stylua.toml", ".luarc.json"], versionCommand: { cmd: "lua", args: ["-v"], pattern: /Lua\s+(\d+\.\d+)/ } },
|
|
40
|
-
{ name: "elixir", files: ["mix.exs"], versionCommand: { cmd: "elixir", args: ["--version"], pattern: /Elixir\s+(\d+\.\d+\.\d+)/ } },
|
|
41
|
-
{ name: "erlang", files: ["rebar.config", "erlang.mk"] },
|
|
42
|
-
{ name: "gleam", files: ["gleam.toml"], versionCommand: { cmd: "gleam", args: ["--version"], pattern: /gleam\s+(\d+\.\d+\.\d+)/ } },
|
|
43
|
-
{ name: "crystal", files: ["shard.yml"], versionCommand: { cmd: "crystal", args: ["--version"], pattern: /Crystal\s+(\d+\.\d+\.\d+)/ } },
|
|
44
|
-
{ name: "dart", files: ["pubspec.yaml"], versionCommand: { cmd: "dart", args: ["--version"], pattern: /Dart\s+SDK\s+version:\s+(\d+\.\d+\.\d+)/ } },
|
|
45
|
-
{ name: "nim", files: ["nim.cfg", ".nimble"] },
|
|
46
|
-
{ name: "zig", files: ["build.zig"], versionCommand: { cmd: "zig", args: ["version"], pattern: /(\d+\.\d+\.\d+)/ } },
|
|
47
|
-
{ name: "ocaml", files: [".opam", "dune", "dune-project"] },
|
|
48
|
-
{ name: "clojure", files: ["project.clj", "deps.edn"] },
|
|
49
|
-
{ name: "scala", files: ["build.sbt", ".scala", ".metals"] },
|
|
50
|
-
{ name: "perl", files: ["Makefile.PL", "cpanfile"] },
|
|
51
|
-
{ name: "r", files: [".Rproj", "DESCRIPTION"] },
|
|
52
|
-
{ name: "elm", files: ["elm.json"] },
|
|
53
|
-
{ name: "haxe", files: ["haxelib.json", ".haxerc"] },
|
|
54
|
-
{ name: "vagrant", files: ["Vagrantfile"] },
|
|
55
|
-
{ name: "terraform", files: ["main.tf", "variables.tf"], folders: [".terraform"] },
|
|
56
|
-
{ name: "helm", files: ["Chart.yaml", "helmfile.yaml"] },
|
|
57
|
-
{ name: "solidity", files: [], extensions: [".sol"] },
|
|
58
|
-
{ name: "fortran", files: ["fpm.toml"], extensions: [".f", ".f90", ".f95"] },
|
|
59
|
-
{ name: "mojo", files: [], extensions: [".mojo"] },
|
|
60
|
-
{ name: "red", files: [], extensions: [".red", ".reds"] },
|
|
61
|
-
{ name: "raku", files: ["META6.json"], extensions: [".raku", ".rakumod"] },
|
|
62
|
-
{ name: "purescript", files: ["spago.dhall", "spago.yaml"] },
|
|
63
|
-
{ name: "fennel", files: [], extensions: [".fnl"] },
|
|
64
|
-
{ name: "odin", files: [], extensions: [".odin"] },
|
|
65
|
-
{ name: "v", files: ["v.mod", "vpkg.json"], extensions: [".v"] },
|
|
66
|
-
{ name: "xmake", files: ["xmake.lua"] },
|
|
67
|
-
{ name: "gradle", files: ["build.gradle", "build.gradle.kts"], folders: ["gradle"] },
|
|
68
|
-
{ name: "maven", files: ["pom.xml"] },
|
|
69
|
-
{ name: "cmake", files: ["CMakeLists.txt", "CMakeCache.txt"] },
|
|
70
|
-
{ name: "meson", files: ["meson.build"], env: "MESON_DEVENV" },
|
|
71
|
-
{ name: "nix", files: ["flake.nix", "shell.nix"], env: "IN_NIX_SHELL" },
|
|
72
|
-
{ name: "guix", files: [], env: "GUIX_ENVIRONMENT" },
|
|
73
|
-
{ name: "conda", files: [], env: "CONDA_DEFAULT_ENV" },
|
|
74
|
-
{ name: "pixi", files: ["pixi.toml", "pixi.lock"], env: "PIXI_ENVIRONMENT_NAME" },
|
|
75
|
-
{ name: "spack", files: [], env: "SPACK_ENV" },
|
|
76
|
-
{ name: "pulumi", files: ["Pulumi.yaml", "Pulumi.yml"] },
|
|
77
|
-
{ name: "typst", files: ["template.typ"], extensions: [".typ"] },
|
|
78
|
-
{ name: "buf", files: ["buf.yaml", "buf.gen.yaml", "buf.work.yaml"] },
|
|
79
|
-
{ name: "dotnet", files: [".csproj", ".fsproj", "global.json", "Directory.Build.props"] },
|
|
80
|
-
{ name: "cobol", files: [], extensions: [".cbl", ".cob"] },
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
interface CacheEntry {
|
|
84
|
-
fingerprint: string;
|
|
85
|
-
runtime: RuntimeInfo | null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const cache = new Map<string, CacheEntry>();
|
|
89
|
-
const CACHE_MAX = 32;
|
|
90
|
-
|
|
91
|
-
function fingerprint(cwd: string, def: RuntimeDef): string {
|
|
92
|
-
const parts: string[] = [];
|
|
93
|
-
for (const f of def.files) {
|
|
94
|
-
try {
|
|
95
|
-
const stat = statSync(join(cwd, f));
|
|
96
|
-
parts.push(`${f}:${stat.mtimeMs}`);
|
|
97
|
-
} catch { /* ignore */ }
|
|
98
|
-
}
|
|
99
|
-
if (def.extensions || def.folders) {
|
|
100
|
-
try {
|
|
101
|
-
const entries = readdirSync(cwd);
|
|
102
|
-
parts.push(...entries.slice().sort());
|
|
103
|
-
} catch { /* ignore */ }
|
|
104
|
-
}
|
|
105
|
-
if (def.env && process.env[def.env]) {
|
|
106
|
-
parts.push(`${def.env}=${process.env[def.env]}`);
|
|
107
|
-
}
|
|
108
|
-
return parts.join("\0");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function matchesDef(cwd: string, def: RuntimeDef): boolean {
|
|
112
|
-
if (def.env && process.env[def.env]) return true;
|
|
113
|
-
if (def.files.some((f) => existsSync(join(cwd, f)))) return true;
|
|
114
|
-
if (def.folders?.some((f) => existsSync(join(cwd, f)))) return true;
|
|
115
|
-
if (def.extensions) {
|
|
116
|
-
try {
|
|
117
|
-
const entries = readdirSync(cwd);
|
|
118
|
-
if (entries.some((e) => def.extensions!.some((ext) => e.endsWith(ext)))) return true;
|
|
119
|
-
} catch { /* ignore */ }
|
|
120
|
-
}
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async function fetchVersion(def: RuntimeDef, cwd: string): Promise<string | undefined> {
|
|
125
|
-
if (!def.versionCommand) return undefined;
|
|
126
|
-
try {
|
|
127
|
-
const { stdout } = await execFileAsync(def.versionCommand.cmd, def.versionCommand.args ?? [], {
|
|
128
|
-
cwd,
|
|
129
|
-
timeout: VERSION_TIMEOUT_MS,
|
|
130
|
-
maxBuffer: 64 * 1024,
|
|
131
|
-
});
|
|
132
|
-
if (def.versionCommand.pattern) {
|
|
133
|
-
const match = stdout.match(def.versionCommand.pattern);
|
|
134
|
-
return match?.[1];
|
|
135
|
-
}
|
|
136
|
-
return stdout.trim() || undefined;
|
|
137
|
-
} catch {
|
|
138
|
-
return undefined;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export async function readRuntimeInfo(cwd: string): Promise<RuntimeInfo | null> {
|
|
143
|
-
for (const def of RUNTIMES) {
|
|
144
|
-
if (!matchesDef(cwd, def)) continue;
|
|
145
|
-
const fp = fingerprint(cwd, def);
|
|
146
|
-
const cacheKey = `${cwd}\0${def.name}`;
|
|
147
|
-
const cached = cache.get(cacheKey);
|
|
148
|
-
if (cached && cached.fingerprint === fp) {
|
|
149
|
-
return cached.runtime;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
for (const key of cache.keys()) {
|
|
153
|
-
if (key === cacheKey || key.startsWith(`${cwd}\0`)) cache.delete(key);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const version = await fetchVersion(def, cwd);
|
|
157
|
-
const info: RuntimeInfo = {
|
|
158
|
-
name: def.name,
|
|
159
|
-
version,
|
|
160
|
-
};
|
|
161
|
-
cache.set(cacheKey, { fingerprint: fp, runtime: info });
|
|
162
|
-
while (cache.size > CACHE_MAX) {
|
|
163
|
-
const oldest = cache.keys().next().value;
|
|
164
|
-
if (oldest === undefined) break;
|
|
165
|
-
cache.delete(oldest);
|
|
166
|
-
}
|
|
167
|
-
return info;
|
|
168
|
-
}
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export function clearRuntimeCache(): void {
|
|
173
|
-
cache.clear();
|
|
174
|
-
}
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const VERSION_TIMEOUT_MS = 2500;
|
|
8
|
+
|
|
9
|
+
export interface RuntimeInfo {
|
|
10
|
+
name: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface RuntimeDef {
|
|
15
|
+
name: string;
|
|
16
|
+
files: readonly string[];
|
|
17
|
+
folders?: readonly string[];
|
|
18
|
+
extensions?: readonly string[];
|
|
19
|
+
env?: string;
|
|
20
|
+
versionCommand?: { cmd: string; args?: string[]; pattern?: RegExp };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const RUNTIMES: readonly RuntimeDef[] = [
|
|
24
|
+
{ name: "bun", files: ["bun.lock", "bun.lockb"], versionCommand: { cmd: "bun", args: ["--version"], pattern: /(\d+\.\d+\.\d+)/ } },
|
|
25
|
+
{ name: "nodejs", files: ["package.json", ".nvmrc", ".node-version"], versionCommand: { cmd: "node", args: ["--version"], pattern: /v(\d+\.\d+\.\d+)/ } },
|
|
26
|
+
{ name: "rust", files: ["Cargo.toml"], versionCommand: { cmd: "rustc", args: ["--version"], pattern: /rustc\s+(\d+\.\d+\.\d+)/ } },
|
|
27
|
+
{ name: "go", files: ["go.mod"], versionCommand: { cmd: "go", args: ["version"], pattern: /go(\d+\.\d+\.\d+)/ } },
|
|
28
|
+
{ name: "python", files: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile", ".python-version"], versionCommand: { cmd: "python3", args: ["--version"], pattern: /Python\s+(\d+\.\d+\.\d+)/ } },
|
|
29
|
+
{ name: "ruby", files: ["Gemfile", ".ruby-version"], versionCommand: { cmd: "ruby", args: ["--version"], pattern: /ruby\s+(\d+\.\d+\.\d+)/ } },
|
|
30
|
+
{ name: "java", files: ["pom.xml", "build.gradle", "build.gradle.kts", ".java-version"], versionCommand: { cmd: "java", args: ["-version"], pattern: /version\s+"(\d+\.\d+[\.\d]*)"/ } },
|
|
31
|
+
{ name: "swift", files: ["Package.swift"], versionCommand: { cmd: "swift", args: ["--version"], pattern: /Swift\s+(\d+\.\d+)/ } },
|
|
32
|
+
{ name: "kotlin", files: ["build.gradle.kts", "settings.gradle.kts"] },
|
|
33
|
+
{ name: "cpp", files: ["CMakeLists.txt", "Makefile"] },
|
|
34
|
+
{ name: "c", files: ["Makefile", "CMakeLists.txt"] },
|
|
35
|
+
{ name: "deno", files: ["deno.json", "deno.jsonc", "deno.lock"], versionCommand: { cmd: "deno", args: ["--version"], pattern: /deno\s+(\d+\.\d+\.\d+)/ } },
|
|
36
|
+
{ name: "php", files: ["composer.json"], versionCommand: { cmd: "php", args: ["--version"], pattern: /PHP\s+(\d+\.\d+\.\d+)/ } },
|
|
37
|
+
{ name: "haskell", files: ["stack.yaml", "cabal.project", ".cabal"], versionCommand: { cmd: "ghc", args: ["--version"], pattern: /(\d+\.\d+\.\d+)/ } },
|
|
38
|
+
{ name: "julia", files: ["Project.toml", "Manifest.toml"], versionCommand: { cmd: "julia", args: ["--version"], pattern: /julia\s+(\d+\.\d+\.\d+)/ } },
|
|
39
|
+
{ name: "lua", files: ["stylua.toml", ".luarc.json"], versionCommand: { cmd: "lua", args: ["-v"], pattern: /Lua\s+(\d+\.\d+)/ } },
|
|
40
|
+
{ name: "elixir", files: ["mix.exs"], versionCommand: { cmd: "elixir", args: ["--version"], pattern: /Elixir\s+(\d+\.\d+\.\d+)/ } },
|
|
41
|
+
{ name: "erlang", files: ["rebar.config", "erlang.mk"] },
|
|
42
|
+
{ name: "gleam", files: ["gleam.toml"], versionCommand: { cmd: "gleam", args: ["--version"], pattern: /gleam\s+(\d+\.\d+\.\d+)/ } },
|
|
43
|
+
{ name: "crystal", files: ["shard.yml"], versionCommand: { cmd: "crystal", args: ["--version"], pattern: /Crystal\s+(\d+\.\d+\.\d+)/ } },
|
|
44
|
+
{ name: "dart", files: ["pubspec.yaml"], versionCommand: { cmd: "dart", args: ["--version"], pattern: /Dart\s+SDK\s+version:\s+(\d+\.\d+\.\d+)/ } },
|
|
45
|
+
{ name: "nim", files: ["nim.cfg", ".nimble"] },
|
|
46
|
+
{ name: "zig", files: ["build.zig"], versionCommand: { cmd: "zig", args: ["version"], pattern: /(\d+\.\d+\.\d+)/ } },
|
|
47
|
+
{ name: "ocaml", files: [".opam", "dune", "dune-project"] },
|
|
48
|
+
{ name: "clojure", files: ["project.clj", "deps.edn"] },
|
|
49
|
+
{ name: "scala", files: ["build.sbt", ".scala", ".metals"] },
|
|
50
|
+
{ name: "perl", files: ["Makefile.PL", "cpanfile"] },
|
|
51
|
+
{ name: "r", files: [".Rproj", "DESCRIPTION"] },
|
|
52
|
+
{ name: "elm", files: ["elm.json"] },
|
|
53
|
+
{ name: "haxe", files: ["haxelib.json", ".haxerc"] },
|
|
54
|
+
{ name: "vagrant", files: ["Vagrantfile"] },
|
|
55
|
+
{ name: "terraform", files: ["main.tf", "variables.tf"], folders: [".terraform"] },
|
|
56
|
+
{ name: "helm", files: ["Chart.yaml", "helmfile.yaml"] },
|
|
57
|
+
{ name: "solidity", files: [], extensions: [".sol"] },
|
|
58
|
+
{ name: "fortran", files: ["fpm.toml"], extensions: [".f", ".f90", ".f95"] },
|
|
59
|
+
{ name: "mojo", files: [], extensions: [".mojo"] },
|
|
60
|
+
{ name: "red", files: [], extensions: [".red", ".reds"] },
|
|
61
|
+
{ name: "raku", files: ["META6.json"], extensions: [".raku", ".rakumod"] },
|
|
62
|
+
{ name: "purescript", files: ["spago.dhall", "spago.yaml"] },
|
|
63
|
+
{ name: "fennel", files: [], extensions: [".fnl"] },
|
|
64
|
+
{ name: "odin", files: [], extensions: [".odin"] },
|
|
65
|
+
{ name: "v", files: ["v.mod", "vpkg.json"], extensions: [".v"] },
|
|
66
|
+
{ name: "xmake", files: ["xmake.lua"] },
|
|
67
|
+
{ name: "gradle", files: ["build.gradle", "build.gradle.kts"], folders: ["gradle"] },
|
|
68
|
+
{ name: "maven", files: ["pom.xml"] },
|
|
69
|
+
{ name: "cmake", files: ["CMakeLists.txt", "CMakeCache.txt"] },
|
|
70
|
+
{ name: "meson", files: ["meson.build"], env: "MESON_DEVENV" },
|
|
71
|
+
{ name: "nix", files: ["flake.nix", "shell.nix"], env: "IN_NIX_SHELL" },
|
|
72
|
+
{ name: "guix", files: [], env: "GUIX_ENVIRONMENT" },
|
|
73
|
+
{ name: "conda", files: [], env: "CONDA_DEFAULT_ENV" },
|
|
74
|
+
{ name: "pixi", files: ["pixi.toml", "pixi.lock"], env: "PIXI_ENVIRONMENT_NAME" },
|
|
75
|
+
{ name: "spack", files: [], env: "SPACK_ENV" },
|
|
76
|
+
{ name: "pulumi", files: ["Pulumi.yaml", "Pulumi.yml"] },
|
|
77
|
+
{ name: "typst", files: ["template.typ"], extensions: [".typ"] },
|
|
78
|
+
{ name: "buf", files: ["buf.yaml", "buf.gen.yaml", "buf.work.yaml"] },
|
|
79
|
+
{ name: "dotnet", files: [".csproj", ".fsproj", "global.json", "Directory.Build.props"] },
|
|
80
|
+
{ name: "cobol", files: [], extensions: [".cbl", ".cob"] },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
interface CacheEntry {
|
|
84
|
+
fingerprint: string;
|
|
85
|
+
runtime: RuntimeInfo | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const cache = new Map<string, CacheEntry>();
|
|
89
|
+
const CACHE_MAX = 32;
|
|
90
|
+
|
|
91
|
+
function fingerprint(cwd: string, def: RuntimeDef): string {
|
|
92
|
+
const parts: string[] = [];
|
|
93
|
+
for (const f of def.files) {
|
|
94
|
+
try {
|
|
95
|
+
const stat = statSync(join(cwd, f));
|
|
96
|
+
parts.push(`${f}:${stat.mtimeMs}`);
|
|
97
|
+
} catch { /* ignore */ }
|
|
98
|
+
}
|
|
99
|
+
if (def.extensions || def.folders) {
|
|
100
|
+
try {
|
|
101
|
+
const entries = readdirSync(cwd);
|
|
102
|
+
parts.push(...entries.slice().sort());
|
|
103
|
+
} catch { /* ignore */ }
|
|
104
|
+
}
|
|
105
|
+
if (def.env && process.env[def.env]) {
|
|
106
|
+
parts.push(`${def.env}=${process.env[def.env]}`);
|
|
107
|
+
}
|
|
108
|
+
return parts.join("\0");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function matchesDef(cwd: string, def: RuntimeDef): boolean {
|
|
112
|
+
if (def.env && process.env[def.env]) return true;
|
|
113
|
+
if (def.files.some((f) => existsSync(join(cwd, f)))) return true;
|
|
114
|
+
if (def.folders?.some((f) => existsSync(join(cwd, f)))) return true;
|
|
115
|
+
if (def.extensions) {
|
|
116
|
+
try {
|
|
117
|
+
const entries = readdirSync(cwd);
|
|
118
|
+
if (entries.some((e) => def.extensions!.some((ext) => e.endsWith(ext)))) return true;
|
|
119
|
+
} catch { /* ignore */ }
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function fetchVersion(def: RuntimeDef, cwd: string): Promise<string | undefined> {
|
|
125
|
+
if (!def.versionCommand) return undefined;
|
|
126
|
+
try {
|
|
127
|
+
const { stdout } = await execFileAsync(def.versionCommand.cmd, def.versionCommand.args ?? [], {
|
|
128
|
+
cwd,
|
|
129
|
+
timeout: VERSION_TIMEOUT_MS,
|
|
130
|
+
maxBuffer: 64 * 1024,
|
|
131
|
+
});
|
|
132
|
+
if (def.versionCommand.pattern) {
|
|
133
|
+
const match = stdout.match(def.versionCommand.pattern);
|
|
134
|
+
return match?.[1];
|
|
135
|
+
}
|
|
136
|
+
return stdout.trim() || undefined;
|
|
137
|
+
} catch {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function readRuntimeInfo(cwd: string): Promise<RuntimeInfo | null> {
|
|
143
|
+
for (const def of RUNTIMES) {
|
|
144
|
+
if (!matchesDef(cwd, def)) continue;
|
|
145
|
+
const fp = fingerprint(cwd, def);
|
|
146
|
+
const cacheKey = `${cwd}\0${def.name}`;
|
|
147
|
+
const cached = cache.get(cacheKey);
|
|
148
|
+
if (cached && cached.fingerprint === fp) {
|
|
149
|
+
return cached.runtime;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const key of cache.keys()) {
|
|
153
|
+
if (key === cacheKey || key.startsWith(`${cwd}\0`)) cache.delete(key);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const version = await fetchVersion(def, cwd);
|
|
157
|
+
const info: RuntimeInfo = {
|
|
158
|
+
name: def.name,
|
|
159
|
+
version,
|
|
160
|
+
};
|
|
161
|
+
cache.set(cacheKey, { fingerprint: fp, runtime: info });
|
|
162
|
+
while (cache.size > CACHE_MAX) {
|
|
163
|
+
const oldest = cache.keys().next().value;
|
|
164
|
+
if (oldest === undefined) break;
|
|
165
|
+
cache.delete(oldest);
|
|
166
|
+
}
|
|
167
|
+
return info;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function clearRuntimeCache(): void {
|
|
173
|
+
cache.clear();
|
|
174
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
1
2
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
3
3
|
import type { GitStatus } from "./git.ts";
|
|
4
4
|
import { emptyGitStatus } from "./git.ts";
|
|
5
5
|
import type { RuntimeInfo } from "./runtime.ts";
|
|
@@ -39,24 +39,32 @@ export function getUsageTotals(ctx: ExtensionContext): UsageTotals {
|
|
|
39
39
|
latestCacheHitRate: undefined,
|
|
40
40
|
};
|
|
41
41
|
for (const entry of ctx.sessionManager.getEntries()) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
let u: Usage | undefined;
|
|
43
|
+
let updateCacheHitRate = false;
|
|
44
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
45
|
+
u = entry.message.usage;
|
|
46
|
+
updateCacheHitRate = true;
|
|
47
|
+
} else if (entry.type === "message" && entry.message.role === "toolResult") {
|
|
48
|
+
u = entry.message.usage;
|
|
49
|
+
} else if (entry.type === "branch_summary" || entry.type === "compaction") {
|
|
50
|
+
u = entry.usage;
|
|
51
|
+
}
|
|
52
|
+
if (!u) continue;
|
|
53
|
+
const input = finiteOrZero(u.input);
|
|
54
|
+
const cacheRead = finiteOrZero(u.cacheRead);
|
|
55
|
+
const cacheWrite = finiteOrZero(u.cacheWrite);
|
|
56
|
+
// input matches /session's "uncached" total: cacheWrite is billed near full
|
|
57
|
+
// price (fresh content), only cacheRead is discounted repeat content.
|
|
58
|
+
totals.input += input + cacheWrite;
|
|
59
|
+
totals.output += finiteOrZero(u.output);
|
|
60
|
+
totals.cacheRead += cacheRead;
|
|
61
|
+
totals.cacheWrite += cacheWrite;
|
|
62
|
+
totals.cost += finiteOrZero(u.cost?.total);
|
|
63
|
+
if (updateCacheHitRate) {
|
|
56
64
|
const promptTokens = input + cacheRead + cacheWrite;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
totals.latestCacheHitRate = promptTokens > 0
|
|
66
|
+
? (cacheRead / promptTokens) * 100
|
|
67
|
+
: undefined;
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
usageCache = { key, totals };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-open-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A polished TUI for Pi coding agent: animated logo header, Starship-style footer, rounded editor with model metadata, and prompt-box user messages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"image": "https://raw.githubusercontent.com/OldSuns/pi-open-tui/main/assets/preview_dashboard_1.png"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
39
|
-
"@earendil-works/pi-ai": ">=0.
|
|
40
|
-
"@earendil-works/pi-tui": ">=0.
|
|
38
|
+
"@earendil-works/pi-coding-agent": ">=0.81",
|
|
39
|
+
"@earendil-works/pi-ai": ">=0.81",
|
|
40
|
+
"@earendil-works/pi-tui": ">=0.81"
|
|
41
41
|
},
|
|
42
42
|
"peerDependenciesMeta": {
|
|
43
43
|
"@earendil-works/pi-coding-agent": {
|