codebase-cli 2.0.0-pre.2 → 2.0.0-pre.20
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/dist/agent/agent.js +2 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/config.js +76 -9
- package/dist/agent/config.js.map +1 -1
- package/dist/app-server/protocol.js +7 -0
- package/dist/app-server/protocol.js.map +1 -0
- package/dist/app-server/server.js +241 -0
- package/dist/app-server/server.js.map +1 -0
- package/dist/auth/flow.js +174 -41
- package/dist/auth/flow.js.map +1 -1
- package/dist/cli.js +47 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/builtins.js +88 -5
- package/dist/commands/builtins.js.map +1 -1
- package/dist/commands/registry.js +46 -1
- package/dist/commands/registry.js.map +1 -1
- package/dist/headless/run.js +1 -1
- package/dist/headless/run.js.map +1 -1
- package/dist/permissions/store.js +4 -0
- package/dist/permissions/store.js.map +1 -1
- package/dist/projects/cli.js +92 -0
- package/dist/projects/cli.js.map +1 -0
- package/dist/projects/client.js +120 -0
- package/dist/projects/client.js.map +1 -0
- package/dist/projects/types.js +2 -0
- package/dist/projects/types.js.map +1 -0
- package/dist/skills/platform-loader.js +133 -38
- package/dist/skills/platform-loader.js.map +1 -1
- package/dist/ui/App.js +122 -12
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/FirstRunSetup.js +90 -15
- package/dist/ui/FirstRunSetup.js.map +1 -1
- package/dist/ui/Input.js +245 -13
- package/dist/ui/Input.js.map +1 -1
- package/dist/ui/Markdown.js +286 -0
- package/dist/ui/Markdown.js.map +1 -0
- package/dist/ui/Message.js +449 -25
- package/dist/ui/Message.js.map +1 -1
- package/dist/ui/MessageList.js +7 -2
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/Permission.js +43 -20
- package/dist/ui/Permission.js.map +1 -1
- package/dist/ui/PixelC.js +25 -0
- package/dist/ui/PixelC.js.map +1 -0
- package/dist/ui/Status.js +213 -7
- package/dist/ui/Status.js.map +1 -1
- package/dist/ui/Throbber.js +11 -7
- package/dist/ui/Throbber.js.map +1 -1
- package/dist/ui/Welcome.js +59 -0
- package/dist/ui/Welcome.js.map +1 -0
- package/dist/ui/attachments.js +68 -0
- package/dist/ui/attachments.js.map +1 -0
- package/dist/ui/debug-input.js +44 -0
- package/dist/ui/debug-input.js.map +1 -0
- package/dist/ui/highlight.js +324 -0
- package/dist/ui/highlight.js.map +1 -0
- package/dist/ui/history-store.js +60 -0
- package/dist/ui/history-store.js.map +1 -0
- package/dist/ui/path-complete.js +102 -0
- package/dist/ui/path-complete.js.map +1 -0
- package/dist/ui/terminal-restore.js +83 -0
- package/dist/ui/terminal-restore.js.map +1 -0
- package/package.json +3 -1
package/dist/ui/Permission.js
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
const RISK_COLOR = {
|
|
4
5
|
low: "yellow",
|
|
5
6
|
medium: "yellow",
|
|
6
7
|
high: "red",
|
|
7
8
|
};
|
|
8
|
-
const
|
|
9
|
-
low: "
|
|
10
|
-
medium: "
|
|
11
|
-
high: "
|
|
9
|
+
const RISK_LABEL = {
|
|
10
|
+
low: "LOW RISK",
|
|
11
|
+
medium: "REVIEW",
|
|
12
|
+
high: "HIGH RISK",
|
|
12
13
|
};
|
|
14
|
+
const CHOICES = [
|
|
15
|
+
{ label: "Allow", key: "allow-once", hint: "this one time", color: "green", shortcut: "y" },
|
|
16
|
+
{ label: "Trust tool", key: "trust-tool", hint: "for the rest of this session", color: "cyan", shortcut: "t" },
|
|
17
|
+
{ label: "Trust all", key: "trust-all", hint: "any tool, this session", color: "cyan", shortcut: "a" },
|
|
18
|
+
{ label: "Deny", key: "deny", hint: "block this call", color: "red", shortcut: "n" },
|
|
19
|
+
];
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
21
|
+
* Permission prompt — bordered box with risk badge, tool summary,
|
|
22
|
+
* collapsed detail, and four arrow-navigable choices. Single-key
|
|
23
|
+
* shortcuts still work (y/t/a/n) for muscle memory; Enter on the
|
|
24
|
+
* highlighted choice for newcomers; Esc maps to Deny.
|
|
18
25
|
*/
|
|
19
26
|
export function Permission({ request, onRespond }) {
|
|
27
|
+
const [cursor, setCursor] = useState(0);
|
|
20
28
|
useInput((input, key) => {
|
|
21
29
|
if (key.escape) {
|
|
22
30
|
onRespond("deny");
|
|
23
31
|
return;
|
|
24
32
|
}
|
|
33
|
+
if (key.return) {
|
|
34
|
+
onRespond(CHOICES[cursor].key);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (key.leftArrow || (key.shift && key.tab)) {
|
|
38
|
+
setCursor((c) => (c - 1 + CHOICES.length) % CHOICES.length);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (key.rightArrow || key.tab || key.downArrow || key.upArrow) {
|
|
42
|
+
setCursor((c) => (c + 1) % CHOICES.length);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
25
45
|
const ch = input.toLowerCase();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
onRespond("deny");
|
|
30
|
-
else if (ch === "t")
|
|
31
|
-
onRespond("trust-tool");
|
|
32
|
-
else if (ch === "a")
|
|
33
|
-
onRespond("trust-all");
|
|
46
|
+
const direct = CHOICES.find((c) => c.shortcut === ch);
|
|
47
|
+
if (direct)
|
|
48
|
+
onRespond(direct.key);
|
|
34
49
|
});
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [
|
|
50
|
+
const riskColor = RISK_COLOR[request.risk];
|
|
51
|
+
const riskLabel = RISK_LABEL[request.risk];
|
|
52
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: riskColor, paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: riskColor, bold: true, children: riskLabel }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u00B7 permission needed" })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: request.tool }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { children: request.summary })] }), request.detail ? (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { dimColor: true, children: truncate(request.detail, 600) }) })) : null, _jsx(Box, { marginTop: 1, flexDirection: "row", children: CHOICES.map((c, i) => {
|
|
53
|
+
const selected = i === cursor;
|
|
54
|
+
return (_jsxs(Box, { marginRight: 2, children: [_jsxs(Text, { color: selected ? c.color : "gray", bold: selected, children: [selected ? "▸ " : " ", c.label] }), _jsxs(Text, { dimColor: true, children: [" (", c.shortcut, ")"] })] }, c.key));
|
|
55
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [CHOICES[cursor].hint, " \u00B7 \u2190\u2192 Enter \u00B7 y/t/a/n shortcuts \u00B7 Esc to deny"] }) })] }));
|
|
56
|
+
}
|
|
57
|
+
function truncate(s, n) {
|
|
58
|
+
if (s.length <= n)
|
|
59
|
+
return s;
|
|
60
|
+
return `${s.slice(0, n - 1)}…`;
|
|
38
61
|
}
|
|
39
62
|
//# sourceMappingURL=Permission.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/ui/Permission.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"Permission.js","sourceRoot":"","sources":["../../src/ui/Permission.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
/**
|
|
4
|
+
* Static pixel-C brand mark. Mirrors `web/public/favicon.svg`:
|
|
5
|
+
* 5-row × 4-col grid, 9 filled pixels (3 top + 3 left + 3 bottom).
|
|
6
|
+
* Each SVG pixel renders as two block chars wide so the C reads
|
|
7
|
+
* proportionally in a 1:2 cell-ratio terminal.
|
|
8
|
+
*/
|
|
9
|
+
const FILL = "██";
|
|
10
|
+
const GAP = " ";
|
|
11
|
+
// Row id doubles as the React key. Names describe the C shape so the
|
|
12
|
+
// keys are stable regardless of order (biome's array-index-as-key rule
|
|
13
|
+
// is right in general — but here the rows aren't unique by content
|
|
14
|
+
// (the three FILL rows repeat), so we lean on positional ids).
|
|
15
|
+
const ROWS = [
|
|
16
|
+
{ id: "top", text: `${GAP}${FILL}${FILL}${FILL}` },
|
|
17
|
+
{ id: "mid-1", text: FILL },
|
|
18
|
+
{ id: "mid-2", text: FILL },
|
|
19
|
+
{ id: "mid-3", text: FILL },
|
|
20
|
+
{ id: "bot", text: `${GAP}${FILL}${FILL}${FILL}` },
|
|
21
|
+
];
|
|
22
|
+
export function PixelC({ color = "cyan" }) {
|
|
23
|
+
return (_jsx(Box, { flexDirection: "column", children: ROWS.map((row) => (_jsx(Text, { bold: true, color: color, children: row.text }, row.id))) }));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=PixelC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PixelC.js","sourceRoot":"","sources":["../../src/ui/PixelC.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC;;;;;GAKG;AAEH,MAAM,IAAI,GAAG,IAAI,CAAC;AAClB,MAAM,GAAG,GAAG,IAAI,CAAC;AAMjB,qEAAqE;AACrE,uEAAuE;AACvE,mEAAmE;AACnE,+DAA+D;AAC/D,MAAM,IAAI,GAA4C;IACrD,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;IAClD,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IAC3B,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;CAClD,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,EAAe;IACrD,OAAO,CACN,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACzB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAClB,KAAC,IAAI,IAAc,IAAI,QAAC,KAAK,EAAE,KAAK,YAClC,GAAG,CAAC,IAAI,IADC,GAAG,CAAC,EAAE,CAEV,CACP,CAAC,GACG,CACN,CAAC;AACH,CAAC"}
|
package/dist/ui/Status.js
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { basename } from "node:path";
|
|
2
3
|
import { Box, Text } from "ink";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
5
|
import { Throbber } from "./Throbber.js";
|
|
6
|
+
/**
|
|
7
|
+
* Playful verbs we cycle through while the agent is thinking — the
|
|
8
|
+
* Claude Code signature. They all read as "the model is working".
|
|
9
|
+
* Kept ASCII-clean so they line up in any terminal font.
|
|
10
|
+
*/
|
|
11
|
+
const THINKING_VERBS = [
|
|
12
|
+
"Thinking",
|
|
13
|
+
"Pondering",
|
|
14
|
+
"Synthesizing",
|
|
15
|
+
"Cogitating",
|
|
16
|
+
"Ruminating",
|
|
17
|
+
"Deliberating",
|
|
18
|
+
"Mulling",
|
|
19
|
+
"Marinating",
|
|
20
|
+
"Brewing",
|
|
21
|
+
"Contemplating",
|
|
22
|
+
"Reasoning",
|
|
23
|
+
"Considering",
|
|
24
|
+
];
|
|
4
25
|
const STATUS_LABEL = {
|
|
5
26
|
idle: "ready",
|
|
6
|
-
thinking: "
|
|
7
|
-
streaming: "
|
|
8
|
-
tool: "
|
|
27
|
+
thinking: "Thinking",
|
|
28
|
+
streaming: "Writing",
|
|
29
|
+
tool: "Working",
|
|
9
30
|
aborted: "aborted",
|
|
10
31
|
error: "error",
|
|
11
32
|
};
|
|
@@ -17,12 +38,197 @@ const STATUS_COLOR = {
|
|
|
17
38
|
aborted: "red",
|
|
18
39
|
error: "red",
|
|
19
40
|
};
|
|
20
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Bottom status line — matches Claude Code's pattern: spinner + state
|
|
43
|
+
* on the left, model + cwd + context % + cost on the right. Stays on
|
|
44
|
+
* one row in normal terminal widths; the cwd basename is the only
|
|
45
|
+
* dynamic-length piece so we always show what matters.
|
|
46
|
+
*/
|
|
47
|
+
export function Status({ state, cwd, contextWindow = 200_000 }) {
|
|
21
48
|
const busy = state.status === "thinking" || state.status === "streaming" || state.status === "tool";
|
|
22
|
-
const
|
|
49
|
+
const verb = useThinkingVerb(state.status === "thinking");
|
|
50
|
+
let label = state.status === "thinking" ? verb : STATUS_LABEL[state.status];
|
|
51
|
+
if (state.status === "tool") {
|
|
52
|
+
const running = findRunningTool(state);
|
|
53
|
+
if (running)
|
|
54
|
+
label = `${STATUS_LABEL.tool} · ${running}`;
|
|
55
|
+
}
|
|
23
56
|
const color = STATUS_COLOR[state.status];
|
|
57
|
+
const tokRate = useTokenRate(state);
|
|
58
|
+
const elapsedSec = useBusyElapsed(busy);
|
|
24
59
|
const u = state.usage;
|
|
25
|
-
|
|
60
|
+
const usedTokens = u.input + u.cacheRead;
|
|
61
|
+
const ctxPct = contextWindow > 0 ? Math.min(100, Math.round((usedTokens / contextWindow) * 100)) : 0;
|
|
62
|
+
const cwdLabel = cwd ? basename(cwd) || "/" : "";
|
|
63
|
+
const modelLabel = state.model.name || state.model.id;
|
|
64
|
+
return (_jsxs(Box, { flexDirection: "column", children: [state.error ? _jsx(ErrorCard, { message: state.error }) : null, ctxPct >= 85 ? _jsx(ContextWarning, { pct: ctxPct }) : null, _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [busy ? (_jsxs(_Fragment, { children: [_jsx(Throbber, { color: color }), _jsx(Text, { children: " " })] })) : null, _jsx(Text, { color: color, children: label }), elapsedSec !== undefined ? _jsxs(Text, { dimColor: true, children: [" (", elapsedSec, "s)"] }) : null] }), _jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [modelLabel, cwdLabel ? ` · ${cwdLabel}` : "", " \u00B7", " "] }), _jsxs(Text, { color: ctxColor(ctxPct), children: [ctxBar(ctxPct), " ", ctxPct, "%"] }), _jsxs(Text, { dimColor: true, children: [tokRate !== undefined ? ` · ${tokRate} tok/s` : "", " \u00B7 $", formatCost(u.cost.total)] })] })] })] }));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Track how long the agent has been busy. Returns undefined unless the
|
|
68
|
+
* elapsed time has crossed 3 seconds — short turns shouldn't carry an
|
|
69
|
+
* "(0s)" suffix on the status bar. Resets cleanly when the agent
|
|
70
|
+
* goes idle so consecutive turns each start their own timer.
|
|
71
|
+
*/
|
|
72
|
+
function useBusyElapsed(busy) {
|
|
73
|
+
const startRef = useRef(undefined);
|
|
74
|
+
const [tick, setTick] = useState(0);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!busy) {
|
|
77
|
+
startRef.current = undefined;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
startRef.current = Date.now();
|
|
81
|
+
const id = setInterval(() => setTick((t) => t + 1), 1000);
|
|
82
|
+
return () => clearInterval(id);
|
|
83
|
+
}, [busy]);
|
|
84
|
+
if (!busy || !startRef.current)
|
|
85
|
+
return undefined;
|
|
86
|
+
const elapsed = Math.floor((Date.now() - startRef.current) / 1000);
|
|
87
|
+
void tick;
|
|
88
|
+
return elapsed >= 3 ? elapsed : undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Pluck the most-recently-started in-flight tool so the status bar can
|
|
92
|
+
* say "Working · shell" instead of just "Working". Falls back to no
|
|
93
|
+
* tool name when the map is empty — keeps the bar terse.
|
|
94
|
+
*/
|
|
95
|
+
function findRunningTool(state) {
|
|
96
|
+
let best;
|
|
97
|
+
for (const tool of state.tools.values()) {
|
|
98
|
+
if (tool.status !== "running")
|
|
99
|
+
continue;
|
|
100
|
+
if (!best || tool.startedAt > best.startedAt) {
|
|
101
|
+
best = { name: tool.name, startedAt: tool.startedAt };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return best?.name;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Estimate the live token-output rate during streaming. Pi-ai only
|
|
108
|
+
* surfaces accurate `usage` at message_end, so for the live counter
|
|
109
|
+
* we approximate from the streaming message's character length using
|
|
110
|
+
* the common ~4-chars-per-token rule. Cheap, no extra deps, and
|
|
111
|
+
* accurate enough for a status-bar readout.
|
|
112
|
+
*
|
|
113
|
+
* Returns undefined when not streaming, or when too few chars have
|
|
114
|
+
* accumulated for the rate to be meaningful (so the bar doesn't
|
|
115
|
+
* flicker a noisy "9999 tok/s" in the first 100ms).
|
|
116
|
+
*/
|
|
117
|
+
function useTokenRate(state) {
|
|
118
|
+
const startRef = useRef(undefined);
|
|
119
|
+
const [tick, setTick] = useState(0);
|
|
120
|
+
const streaming = state.status === "streaming";
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (!streaming) {
|
|
123
|
+
startRef.current = undefined;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (startRef.current === undefined)
|
|
127
|
+
startRef.current = Date.now();
|
|
128
|
+
const id = setInterval(() => setTick((t) => t + 1), 500);
|
|
129
|
+
return () => clearInterval(id);
|
|
130
|
+
}, [streaming]);
|
|
131
|
+
if (!streaming || !startRef.current)
|
|
132
|
+
return undefined;
|
|
133
|
+
const elapsedSec = (Date.now() - startRef.current) / 1000;
|
|
134
|
+
if (elapsedSec < 0.5)
|
|
135
|
+
return undefined;
|
|
136
|
+
void tick; // force re-eval on each interval
|
|
137
|
+
const chars = streamingChars(state);
|
|
138
|
+
if (chars < 40)
|
|
139
|
+
return undefined;
|
|
140
|
+
const tokens = chars / 4;
|
|
141
|
+
const rate = tokens / elapsedSec;
|
|
142
|
+
return Math.round(rate);
|
|
143
|
+
}
|
|
144
|
+
/** Sum the visible text length of all text/thinking blocks in the live streaming message. */
|
|
145
|
+
function streamingChars(state) {
|
|
146
|
+
const m = state.streaming;
|
|
147
|
+
if (!m || m.role !== "assistant")
|
|
148
|
+
return 0;
|
|
149
|
+
let total = 0;
|
|
150
|
+
for (const block of m.content) {
|
|
151
|
+
if (block.type === "text")
|
|
152
|
+
total += block.text.length;
|
|
153
|
+
else if (block.type === "thinking")
|
|
154
|
+
total += block.thinking.length;
|
|
155
|
+
}
|
|
156
|
+
return total;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* While the agent is thinking, swap the verb every 3 seconds. We pick
|
|
160
|
+
* the next verb at random (excluding the current one) instead of
|
|
161
|
+
* cycling in order so the same word doesn't reappear at predictable
|
|
162
|
+
* beats. When the status leaves thinking we drop back to the first
|
|
163
|
+
* verb so re-entry starts fresh.
|
|
164
|
+
*/
|
|
165
|
+
function useThinkingVerb(active) {
|
|
166
|
+
const [verb, setVerb] = useState(THINKING_VERBS[0]);
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (!active) {
|
|
169
|
+
setVerb(THINKING_VERBS[0]);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const id = setInterval(() => {
|
|
173
|
+
setVerb((current) => {
|
|
174
|
+
let next = current;
|
|
175
|
+
while (next === current) {
|
|
176
|
+
next = THINKING_VERBS[Math.floor(Math.random() * THINKING_VERBS.length)];
|
|
177
|
+
}
|
|
178
|
+
return next;
|
|
179
|
+
});
|
|
180
|
+
}, 3000);
|
|
181
|
+
return () => clearInterval(id);
|
|
182
|
+
}, [active]);
|
|
183
|
+
return verb;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Banner shown when the context window is past 85%. Suggests /compact
|
|
187
|
+
* so the user can take action before auto-compaction kicks in, and
|
|
188
|
+
* shifts to red past 95% where the next turn might actually trip the
|
|
189
|
+
* model's hard limit.
|
|
190
|
+
*/
|
|
191
|
+
function ContextWarning({ pct }) {
|
|
192
|
+
const urgent = pct >= 95;
|
|
193
|
+
return (_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: urgent ? "red" : "yellow", bold: true, children: [urgent ? "⚠" : "•", " ", pct, "% of context used"] }), _jsx(Text, { dimColor: true, children: " \u2014 run /compact to free space" })] }));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Boxed error card. Headers the error with ERROR + a one-line summary,
|
|
197
|
+
* then shows the rest of the message body (if multi-line) in dim text.
|
|
198
|
+
* Matches Claude Code's pattern of giving fatal errors visual weight
|
|
199
|
+
* so the user doesn't miss them in a busy transcript.
|
|
200
|
+
*/
|
|
201
|
+
function ErrorCard({ message }) {
|
|
202
|
+
const lines = message.split("\n");
|
|
203
|
+
const head = lines[0] ?? message;
|
|
204
|
+
const body = lines.slice(1).filter((l) => l.trim().length > 0);
|
|
205
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: "red", bold: true, children: "ERROR" }), _jsx(Text, { children: " " }), _jsx(Text, { children: head })] }), body.length > 0 ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: body.map((line, i) => (_jsx(Text, { dimColor: true, children: line }, `err-${i}-${line.slice(0, 12)}`))) })) : null] }));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Render a tiny 6-cell bar for the context-window meter. Eighth-block
|
|
209
|
+
* glyphs give us 48 effective steps in 6 chars — enough resolution
|
|
210
|
+
* that 12% / 25% / 50% all look visibly different. Empty cells stay
|
|
211
|
+
* as a dim track so the bar always reads as a meter, not a slider.
|
|
212
|
+
*/
|
|
213
|
+
function ctxBar(pct) {
|
|
214
|
+
const cells = 6;
|
|
215
|
+
const totalEighths = Math.round((pct / 100) * cells * 8);
|
|
216
|
+
const full = Math.floor(totalEighths / 8);
|
|
217
|
+
const remainder = totalEighths - full * 8;
|
|
218
|
+
const partials = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉"];
|
|
219
|
+
let out = "█".repeat(Math.min(full, cells));
|
|
220
|
+
if (full < cells && remainder > 0)
|
|
221
|
+
out += partials[remainder] ?? "";
|
|
222
|
+
while (out.length < cells)
|
|
223
|
+
out += "░";
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
function ctxColor(pct) {
|
|
227
|
+
if (pct >= 90)
|
|
228
|
+
return "red";
|
|
229
|
+
if (pct >= 75)
|
|
230
|
+
return "yellow";
|
|
231
|
+
return "gray";
|
|
26
232
|
}
|
|
27
233
|
function formatCost(value) {
|
|
28
234
|
if (value === 0)
|
package/dist/ui/Status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Status.js","sourceRoot":"","sources":["../../src/ui/Status.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"Status.js","sourceRoot":"","sources":["../../src/ui/Status.tsx"],"names":[],"mappings":"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"}
|
package/dist/ui/Throbber.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Text } from "ink";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
const FRAMES = ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"];
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* 8-frame pulse cycle — the codebase pixel-C "scanning" through brightness
|
|
6
|
+
* levels. The compact (1-char) variant cycles a single block-glyph: ░▒▓█▓▒░.
|
|
7
|
+
* The full pixel-C variant scans a highlight row across the C shape.
|
|
8
|
+
*
|
|
9
|
+
* Self-throttling: owns its own interval so the parent's reducer state
|
|
10
|
+
* doesn't tick on every frame. A hot message_update stream + a 100ms
|
|
11
|
+
* spinner would otherwise compound into full re-renders 10× a second.
|
|
9
12
|
*/
|
|
10
|
-
|
|
13
|
+
const COMPACT_FRAMES = ["░", "▒", "▓", "█", "█", "▓", "▒", "░"];
|
|
14
|
+
export function Throbber({ color = "cyan", intervalMs = 90 }) {
|
|
11
15
|
const [frame, setFrame] = useState(0);
|
|
12
16
|
useEffect(() => {
|
|
13
17
|
const id = setInterval(() => {
|
|
14
|
-
setFrame((f) => (f + 1) %
|
|
18
|
+
setFrame((f) => (f + 1) % COMPACT_FRAMES.length);
|
|
15
19
|
}, intervalMs);
|
|
16
20
|
return () => clearInterval(id);
|
|
17
21
|
}, [intervalMs]);
|
|
18
|
-
return _jsx(Text, { color: color, children:
|
|
22
|
+
return _jsx(Text, { color: color, children: COMPACT_FRAMES[frame] });
|
|
19
23
|
}
|
|
20
24
|
//# sourceMappingURL=Throbber.js.map
|
package/dist/ui/Throbber.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Throbber.js","sourceRoot":"","sources":["../../src/ui/Throbber.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,
|
|
1
|
+
{"version":3,"file":"Throbber.js","sourceRoot":"","sources":["../../src/ui/Throbber.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C;;;;;;;;GAQG;AAEH,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAOhE,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,UAAU,GAAG,EAAE,EAAiB;IAC1E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEtC,SAAS,CAAC,GAAG,EAAE;QACd,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE;YAC3B,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,OAAO,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,YAAG,cAAc,CAAC,KAAK,CAAC,GAAQ,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { Box, Text } from "ink";
|
|
5
|
+
import { PixelC } from "./PixelC.js";
|
|
6
|
+
/**
|
|
7
|
+
* Best-effort git probe for the welcome banner. Returns null if the
|
|
8
|
+
* cwd isn't a git repo (or if `git` isn't on PATH), so non-git
|
|
9
|
+
* projects get a cleaner banner instead of an empty line. We swallow
|
|
10
|
+
* all errors — the banner is decorative; a slow / failing git
|
|
11
|
+
* shouldn't block startup.
|
|
12
|
+
*/
|
|
13
|
+
function readGitInfo(cwd) {
|
|
14
|
+
try {
|
|
15
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
16
|
+
cwd,
|
|
17
|
+
encoding: "utf8",
|
|
18
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
19
|
+
}).trim();
|
|
20
|
+
if (!branch)
|
|
21
|
+
return null;
|
|
22
|
+
const status = execSync("git status --porcelain", {
|
|
23
|
+
cwd,
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
26
|
+
});
|
|
27
|
+
const dirty = status.split("\n").filter((l) => l.trim().length > 0).length;
|
|
28
|
+
return { branch, dirty };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Humanize an absolute timestamp into "5m ago" / "3h ago" / "2d ago" — sub-minute reads as "just now". */
|
|
35
|
+
function formatAgo(ts) {
|
|
36
|
+
const sec = Math.max(0, Math.floor((Date.now() - ts) / 1000));
|
|
37
|
+
if (sec < 60)
|
|
38
|
+
return "just now";
|
|
39
|
+
if (sec < 3600)
|
|
40
|
+
return `${Math.floor(sec / 60)}m ago`;
|
|
41
|
+
if (sec < 86400)
|
|
42
|
+
return `${Math.floor(sec / 3600)}h ago`;
|
|
43
|
+
return `${Math.floor(sec / 86400)}d ago`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Empty-state banner shown above the input while the transcript is
|
|
47
|
+
* empty. Pixel-C logo on the left, contextual info + tips on the
|
|
48
|
+
* right. Renders once and gets pushed up by the first user message —
|
|
49
|
+
* not Static-rendered, but only a few rows so it's cheap.
|
|
50
|
+
*/
|
|
51
|
+
export function Welcome({ modelName, source, cwd, resumedFrom }) {
|
|
52
|
+
const cwdLabel = basename(cwd) || cwd;
|
|
53
|
+
const sourceLabel = source === "proxy" ? "signed in via codebase.design" : source === "byok" ? "BYOK" : `${source}`;
|
|
54
|
+
const gitInfo = readGitInfo(cwd);
|
|
55
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { marginRight: 2, children: _jsx(PixelC, {}) }), _jsxs(Box, { flexDirection: "column", justifyContent: "center", children: [_jsx(Text, { bold: true, color: "cyan", children: "codebase" }), _jsx(Text, { dimColor: true, children: modelName }), _jsxs(Text, { dimColor: true, children: [cwdLabel, " \u00B7 ", sourceLabel] }), gitInfo ? (_jsxs(Text, { dimColor: true, children: [gitInfo.branch, gitInfo.dirty > 0
|
|
56
|
+
? ` · ${gitInfo.dirty} uncommitted change${gitInfo.dirty === 1 ? "" : "s"}`
|
|
57
|
+
: " · clean"] })) : null] })] }), resumedFrom ? (_jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: "cyan", children: ["\u21BB Resumed from ", formatAgo(resumedFrom.updatedAt)] }), _jsxs(Text, { dimColor: true, children: [" ", "\u00B7 ", resumedFrom.messageCount, " message", resumedFrom.messageCount === 1 ? "" : "s"] })] })) : null, _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Ask me to read code, edit files, run commands, or anything in between." }), _jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: "cyan", children: "/" }), " commands \u00B7 ", _jsx(Text, { color: "cyan", children: "!" }), "shell \u00B7 ", _jsx(Text, { color: "cyan", children: "\u2191\u2193" }), " ", "history \u00B7 ", _jsx(Text, { color: "cyan", children: "Tab" }), " complete \u00B7 ", _jsx(Text, { color: "cyan", children: "\\" }), "+Enter for newline"] }), _jsx(Text, { dimColor: true, children: "Ctrl-C twice to exit." })] })] }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=Welcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Welcome.js","sourceRoot":"","sources":["../../src/ui/Welcome.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
|
+
export const MAX_ATTACHMENT_BYTES = 128 * 1024;
|
|
4
|
+
export const MAX_ATTACHMENTS = 8;
|
|
5
|
+
/**
|
|
6
|
+
* Scan the prompt for `@<path>` tokens and resolve each to a readable
|
|
7
|
+
* file under (or adjacent to) the cwd. Returns one entry per resolved
|
|
8
|
+
* file; unresolved `@` mentions don't appear here and stay as literal
|
|
9
|
+
* text — we never silently drop or rewrite user input.
|
|
10
|
+
*
|
|
11
|
+
* Skip rules:
|
|
12
|
+
* - tokens without a slash or dot (email-style @alice mentions)
|
|
13
|
+
* - paths over 256 chars (clearly not a real path)
|
|
14
|
+
* - non-files (directories, sockets, ...)
|
|
15
|
+
* - files larger than MAX_ATTACHMENT_BYTES
|
|
16
|
+
* - past MAX_ATTACHMENTS total attachments per prompt
|
|
17
|
+
*/
|
|
18
|
+
export function collectAttachments(text, cwd) {
|
|
19
|
+
const out = [];
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
const pattern = /@([A-Za-z0-9_./-]+)/g;
|
|
22
|
+
for (const match of text.matchAll(pattern)) {
|
|
23
|
+
if (out.length >= MAX_ATTACHMENTS)
|
|
24
|
+
break;
|
|
25
|
+
const rel = match[1];
|
|
26
|
+
if (!rel || rel.length > 256)
|
|
27
|
+
continue;
|
|
28
|
+
if (!rel.includes("/") && !rel.includes("."))
|
|
29
|
+
continue;
|
|
30
|
+
const abs = isAbsolute(rel) ? rel : join(cwd, rel);
|
|
31
|
+
if (seen.has(abs))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(abs);
|
|
34
|
+
try {
|
|
35
|
+
const stat = statSync(abs);
|
|
36
|
+
if (!stat.isFile())
|
|
37
|
+
continue;
|
|
38
|
+
if (stat.size > MAX_ATTACHMENT_BYTES)
|
|
39
|
+
continue;
|
|
40
|
+
const content = readFileSync(abs, "utf8");
|
|
41
|
+
out.push({ token: match[0], relPath: rel, absPath: abs, content });
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// File doesn't exist or isn't readable — leave the token in text.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the agent-bound prompt with attachments inlined as fenced code
|
|
51
|
+
* blocks above the user's actual ask. The original `@path` tokens stay
|
|
52
|
+
* in the text so the model can correlate the references with the
|
|
53
|
+
* attached content.
|
|
54
|
+
*/
|
|
55
|
+
export function buildAttachmentPrompt(text, attachments) {
|
|
56
|
+
const parts = ["Attached files (auto-inlined from @ mentions):", ""];
|
|
57
|
+
for (const a of attachments) {
|
|
58
|
+
parts.push(`### ${a.relPath}`);
|
|
59
|
+
parts.push("```");
|
|
60
|
+
parts.push(a.content);
|
|
61
|
+
parts.push("```");
|
|
62
|
+
parts.push("");
|
|
63
|
+
}
|
|
64
|
+
parts.push("---");
|
|
65
|
+
parts.push(text);
|
|
66
|
+
return parts.join("\n");
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/ui/attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAS7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,GAAW;IAC3D,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,OAAO,GAAG,sBAAsB,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5C,IAAI,GAAG,CAAC,MAAM,IAAI,eAAe;YAAE,MAAM;QACzC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG;YAAE,SAAS;QACvC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QACvD,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;gBAAE,SAAS;YAC7B,IAAI,IAAI,CAAC,IAAI,GAAG,oBAAoB;gBAAE,SAAS;YAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACR,kEAAkE;QACnE,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,WAAkC;IACrF,MAAM,KAAK,GAAa,CAAC,gDAAgD,EAAE,EAAE,CAAC,CAAC;IAC/E,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
let logPath = null;
|
|
5
|
+
let warned = false;
|
|
6
|
+
export function isDebugInputEnabled() {
|
|
7
|
+
return process.env.CODEBASE_DEBUG_INPUT === "1";
|
|
8
|
+
}
|
|
9
|
+
function resolveLogPath() {
|
|
10
|
+
if (logPath)
|
|
11
|
+
return logPath;
|
|
12
|
+
const dir = join(homedir(), ".codebase", "logs");
|
|
13
|
+
try {
|
|
14
|
+
mkdirSync(dir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// The fs error is non-fatal; the appendFileSync below will throw
|
|
18
|
+
// the same problem and we'll surface it once via the `warned`
|
|
19
|
+
// guard, not on every keystroke.
|
|
20
|
+
}
|
|
21
|
+
logPath = join(dir, "input.log");
|
|
22
|
+
return logPath;
|
|
23
|
+
}
|
|
24
|
+
export function logInputEvent(input, key) {
|
|
25
|
+
if (!isDebugInputEnabled())
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
const entry = {
|
|
29
|
+
t: new Date().toISOString(),
|
|
30
|
+
// Show the actual code points so a stray \x7f or \x1b is visible.
|
|
31
|
+
input,
|
|
32
|
+
codes: Array.from(input).map((ch) => ch.charCodeAt(0)),
|
|
33
|
+
key,
|
|
34
|
+
};
|
|
35
|
+
appendFileSync(resolveLogPath(), `${JSON.stringify(entry)}\n`);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (warned)
|
|
39
|
+
return;
|
|
40
|
+
warned = true;
|
|
41
|
+
process.stderr.write(`\n[debug-input] failed to write log: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=debug-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-input.js","sourceRoot":"","sources":["../../src/ui/debug-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA8BjC,IAAI,OAAO,GAAkB,IAAI,CAAC;AAClC,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB,MAAM,UAAU,mBAAmB;IAClC,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG,CAAC;AACjD,CAAC;AAED,SAAS,cAAc;IACtB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC;QACJ,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACR,iEAAiE;QACjE,8DAA8D;QAC9D,iCAAiC;IAClC,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACjC,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,GAAgB;IAC5D,IAAI,CAAC,mBAAmB,EAAE;QAAE,OAAO;IACnC,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG;YACb,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC3B,kEAAkE;YAClE,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACtD,GAAG;SACH,CAAC;QACF,cAAc,CAAC,cAAc,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,GAAG,IAAI,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,wCAAwC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAC5F,CAAC;IACH,CAAC;AACF,CAAC"}
|