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.
Files changed (63) hide show
  1. package/dist/agent/agent.js +2 -0
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/config.js +76 -9
  4. package/dist/agent/config.js.map +1 -1
  5. package/dist/app-server/protocol.js +7 -0
  6. package/dist/app-server/protocol.js.map +1 -0
  7. package/dist/app-server/server.js +241 -0
  8. package/dist/app-server/server.js.map +1 -0
  9. package/dist/auth/flow.js +174 -41
  10. package/dist/auth/flow.js.map +1 -1
  11. package/dist/cli.js +47 -6
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/builtins.js +88 -5
  14. package/dist/commands/builtins.js.map +1 -1
  15. package/dist/commands/registry.js +46 -1
  16. package/dist/commands/registry.js.map +1 -1
  17. package/dist/headless/run.js +1 -1
  18. package/dist/headless/run.js.map +1 -1
  19. package/dist/permissions/store.js +4 -0
  20. package/dist/permissions/store.js.map +1 -1
  21. package/dist/projects/cli.js +92 -0
  22. package/dist/projects/cli.js.map +1 -0
  23. package/dist/projects/client.js +120 -0
  24. package/dist/projects/client.js.map +1 -0
  25. package/dist/projects/types.js +2 -0
  26. package/dist/projects/types.js.map +1 -0
  27. package/dist/skills/platform-loader.js +133 -38
  28. package/dist/skills/platform-loader.js.map +1 -1
  29. package/dist/ui/App.js +122 -12
  30. package/dist/ui/App.js.map +1 -1
  31. package/dist/ui/FirstRunSetup.js +90 -15
  32. package/dist/ui/FirstRunSetup.js.map +1 -1
  33. package/dist/ui/Input.js +245 -13
  34. package/dist/ui/Input.js.map +1 -1
  35. package/dist/ui/Markdown.js +286 -0
  36. package/dist/ui/Markdown.js.map +1 -0
  37. package/dist/ui/Message.js +449 -25
  38. package/dist/ui/Message.js.map +1 -1
  39. package/dist/ui/MessageList.js +7 -2
  40. package/dist/ui/MessageList.js.map +1 -1
  41. package/dist/ui/Permission.js +43 -20
  42. package/dist/ui/Permission.js.map +1 -1
  43. package/dist/ui/PixelC.js +25 -0
  44. package/dist/ui/PixelC.js.map +1 -0
  45. package/dist/ui/Status.js +213 -7
  46. package/dist/ui/Status.js.map +1 -1
  47. package/dist/ui/Throbber.js +11 -7
  48. package/dist/ui/Throbber.js.map +1 -1
  49. package/dist/ui/Welcome.js +59 -0
  50. package/dist/ui/Welcome.js.map +1 -0
  51. package/dist/ui/attachments.js +68 -0
  52. package/dist/ui/attachments.js.map +1 -0
  53. package/dist/ui/debug-input.js +44 -0
  54. package/dist/ui/debug-input.js.map +1 -0
  55. package/dist/ui/highlight.js +324 -0
  56. package/dist/ui/highlight.js.map +1 -0
  57. package/dist/ui/history-store.js +60 -0
  58. package/dist/ui/history-store.js.map +1 -0
  59. package/dist/ui/path-complete.js +102 -0
  60. package/dist/ui/path-complete.js.map +1 -0
  61. package/dist/ui/terminal-restore.js +83 -0
  62. package/dist/ui/terminal-restore.js.map +1 -0
  63. package/package.json +3 -1
@@ -1,39 +1,62 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
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 RISK_GLYPH = {
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
- * Modal-ish prompt that takes over the input row while a tool call
15
- * awaits user approval. Single-keystroke responses keep it fast:
16
- * y allow once t trust this tool for the session
17
- * a trust everything n / Esc deny
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
- if (ch === "y")
27
- onRespond("allow-once");
28
- else if (ch === "n")
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 color = RISK_COLOR[request.risk];
36
- const glyph = RISK_GLYPH[request.risk];
37
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, marginY: 0, children: [_jsxs(Box, { children: [_jsxs(Text, { color: color, bold: true, children: [glyph, " permission"] }), _jsx(Text, { children: " " }), _jsx(Text, { children: request.summary })] }), request.detail ? (_jsx(Box, { marginLeft: 2, marginY: 0, children: _jsx(Text, { dimColor: true, children: request.detail }) })) : null, _jsx(Box, { marginTop: 0, children: _jsxs(Text, { dimColor: true, children: ["[", _jsx(Text, { color: "green", children: "y" }), "]es \u00B7 [", _jsx(Text, { color: "cyan", children: "t" }), "]rust this tool \u00B7 [", _jsx(Text, { color: "cyan", children: "a" }), "]ll \u00B7 [", _jsx(Text, { color: "red", children: "n" }), "]o"] }) })] }));
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;AAQ1C,MAAM,UAAU,GAA8C;IAC7D,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,KAAK;CACX,CAAC;AAEF,MAAM,UAAU,GAA8C;IAC7D,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;CACT,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAmB;IACjE,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,SAAS,CAAC,MAAM,CAAC,CAAC;YAClB,OAAO;QACR,CAAC;QACD,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC/B,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,YAAY,CAAC,CAAC;aACnC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,MAAM,CAAC,CAAC;aAClC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,YAAY,CAAC,CAAC;aACxC,IAAI,EAAE,KAAK,GAAG;YAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,aAClD,MAAC,GAAG,eACH,MAAC,IAAI,IAAC,KAAK,EAAE,KAAK,EAAE,IAAI,mBACtB,KAAK,mBACA,EACP,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,cAAE,OAAO,CAAC,OAAO,GAAQ,IACzB,EACL,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CACjB,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,YAC7B,KAAC,IAAI,IAAC,QAAQ,kBAAE,OAAO,CAAC,MAAM,GAAQ,GACjC,CACN,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YAChB,MAAC,IAAI,IAAC,QAAQ,wBACZ,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,kBAAS,kBAAO,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,8BAC/D,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,kBAAO,KAAC,IAAI,IAAC,KAAK,EAAC,KAAK,kBAAS,UACtD,GACF,IACD,CACN,CAAC;AACH,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 { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
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: "thinking",
7
- streaming: "responding",
8
- tool: "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
- export function Status({ state }) {
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 label = STATUS_LABEL[state.status];
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
- return (_jsxs(Box, { flexDirection: "column", children: [state.error ? (_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: "red", children: ["! ", state.error] }) })) : 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 })] }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: [state.model.provider, "/", state.model.id, " \u00B7 \u2193", u.input, " \u2191", u.output, " $", formatCost(u.cost.total)] }) })] })] }));
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)
@@ -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;AAEhC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMzC,MAAM,YAAY,GAAwC;IACzD,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,YAAY;IACvB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;CACd,CAAC;AAEF,MAAM,YAAY,GAAwC;IACzD,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,KAAK;CACZ,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,EAAe;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC;IACpG,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAEtB,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CACd,KAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,YACf,MAAC,IAAI,IAAC,KAAK,EAAC,KAAK,mBAAI,KAAK,CAAC,KAAK,IAAQ,GACnC,CACN,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,GAAG,IAAC,QAAQ,EAAE,CAAC,EAAE,cAAc,EAAC,eAAe,aAC/C,MAAC,GAAG,eACF,IAAI,CAAC,CAAC,CAAC,CACP,8BACC,KAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,GAAI,EAC1B,KAAC,IAAI,oBAAS,IACZ,CACH,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,YAAG,KAAK,GAAQ,IAC7B,EACN,KAAC,GAAG,cACH,MAAC,IAAI,IAAC,QAAQ,mBACZ,KAAK,CAAC,KAAK,CAAC,QAAQ,OAAG,KAAK,CAAC,KAAK,CAAC,EAAE,oBAAM,CAAC,CAAC,KAAK,aAAI,CAAC,CAAC,MAAM,QAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IACrF,GACF,IACD,IACD,CACN,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAChC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"Status.js","sourceRoot":"","sources":["../../src/ui/Status.tsx"],"names":[],"mappings":"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"}
@@ -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
- * Self-throttling spinner. Owns its own interval so the parent's reducer
7
- * state isn't ticked on every frame a hot stream of message_update events
8
- * + a 100ms spinner would otherwise compound into needless full re-renders.
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
- export function Throbber({ color = "cyan", intervalMs = 80 }) {
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) % FRAMES.length);
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: FRAMES[frame] });
22
+ return _jsx(Text, { color: color, children: COMPACT_FRAMES[frame] });
19
23
  }
20
24
  //# sourceMappingURL=Throbber.js.map
@@ -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,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAOxD;;;;GAIG;AACH,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,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,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,MAAM,CAAC,KAAK,CAAC,GAAQ,CAAC;AACnD,CAAC"}
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":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,GAAW;IAC/B,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,QAAQ,CAAC,iCAAiC,EAAE;YAC1D,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACnC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,EAAE;YACjD,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACnC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,2GAA2G;AAC3G,SAAS,SAAS,CAAC,EAAU;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,UAAU,CAAC;IAChC,IAAI,GAAG,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC;IACtD,IAAI,GAAG,GAAG,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;IACzD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;AAC1C,CAAC;AAUD;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAgB;IAC5E,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IACtC,MAAM,WAAW,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC;IACpH,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAEjC,OAAO,CACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,aACvD,MAAC,GAAG,IAAC,aAAa,EAAC,KAAK,aACvB,KAAC,GAAG,IAAC,WAAW,EAAE,CAAC,YAClB,KAAC,MAAM,KAAG,GACL,EACN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,cAAc,EAAC,QAAQ,aAClD,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,MAAM,yBAEhB,EACP,KAAC,IAAI,IAAC,QAAQ,kBAAE,SAAS,GAAQ,EACjC,MAAC,IAAI,IAAC,QAAQ,mBACZ,QAAQ,cAAK,WAAW,IACnB,EACN,OAAO,CAAC,CAAC,CAAC,CACV,MAAC,IAAI,IAAC,QAAQ,mBACZ,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,KAAK,GAAG,CAAC;wCACjB,CAAC,CAAC,MAAM,OAAO,CAAC,KAAK,sBAAsB,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;wCAC3E,CAAC,CAAC,UAAU,IACP,CACP,CAAC,CAAC,CAAC,IAAI,IACH,IACD,EACL,WAAW,CAAC,CAAC,CAAC,CACd,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,aAChB,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,qCAAiB,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,IAAQ,EAC3E,MAAC,IAAI,IAAC,QAAQ,mBACZ,GAAG,aACD,WAAW,CAAC,YAAY,cAAU,WAAW,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IACxE,IACF,CACN,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACxC,KAAC,IAAI,IAAC,QAAQ,6FAA8E,EAC5F,MAAC,IAAI,IAAC,QAAQ,mBACb,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,uBAAY,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,kBAAS,mBAAQ,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,6BAAU,EAAC,GAAG,qBAChG,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,oBAAW,uBAAY,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,mBAAS,0BACxE,EACP,KAAC,IAAI,IAAC,QAAQ,4CAA6B,IACtC,IACD,CACN,CAAC;AACH,CAAC"}
@@ -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"}