@cspeach/cli 0.7.0 → 0.7.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/README.md CHANGED
@@ -60,6 +60,32 @@ machine.
60
60
  Ten Forge Rules are non-negotiable. Full philosophy in the `CSPeach Principles`
61
61
  preamble shown to the model every turn.
62
62
 
63
+ ## Cost transparency (since 0.7.0)
64
+
65
+ Every turn shows what it cost. After each model call you see:
66
+
67
+ ```
68
+ session abc123 │ S4H │ /abap-explain │ 2 turns │ 487 tok │ $0.07 │ session: $0.18 │ 3.6s
69
+ ```
70
+
71
+ - `tok` = tokens billed this turn (input + output + cache)
72
+ - `$` = this turn's cost
73
+ - `session:` = cumulative across the open session
74
+ - `/cost` shows a per-skill / per-tool breakdown
75
+ - `/compact` summarises older turns when context gets large (cuts subsequent-turn cost ~80-90%)
76
+ - `auto_compact` in `~/.cspeach/config.toml` fires `/compact` automatically over a configurable threshold
77
+
78
+ On `/exit` you get a session summary + the `cspeach --resume <id>` command in plain text so you can pick up where you left off.
79
+
80
+ ## UI modes
81
+
82
+ Two REPL modes, both supported. **Default is `classic`.** Switch with `/ui ink` or `/ui classic`; saves to `~/.cspeach/config.toml` under `[ui]`.
83
+
84
+ - **`classic`** — chalk + readline. The supported, demo-tested mode. Use this unless you have a reason not to. Native terminal scroll, mouse wheel, search — everything your terminal does naturally just works.
85
+ - **`ink`** — React-based TUI. **PREVIEW.** Some rendering quirks remain (occasional phantom cursor, picker fossil in scrollback). Functional for daily use but not yet polished enough to recommend as default. Promoted to default in a later release.
86
+
87
+ If `ink` is misbehaving for you, run `/ui classic` once — change applies after relaunch.
88
+
63
89
  ## Configuration
64
90
 
65
91
  `~/.cspeach/config.toml` is created on first `cspeach config add-sap`:
package/dist/ui/app.js CHANGED
@@ -103,7 +103,15 @@ export function App({ chunkEmitter, onSubmit, onSlashTrigger, onHistoryTrigger,
103
103
  } })), askQuestionPending && (_jsx(AskQuestionModal, { params: askQuestionPending, onResolve: (result) => {
104
104
  askQuestionPending.resolve(result);
105
105
  setAskQuestionPending(null);
106
- } })), _jsx(Footer, { onSubmit: onSubmit, onSlashTrigger: handleSlashTrigger, onHistoryTrigger: handleHistoryTrigger, paletteResult: paletteResult, onValueChange: setFooterValue }), showPalette && (_jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(CommandPalette, { onSelect: (s) => {
106
+ } })), _jsx(Footer, { onSubmit: onSubmit, onSlashTrigger: handleSlashTrigger, onHistoryTrigger: handleHistoryTrigger, paletteResult: paletteResult, onValueChange: setFooterValue,
107
+ // 2026-05-17 — disable Footer input while any modal owns
108
+ // input, so keystrokes don't double-fire (modal answer +
109
+ // spurious Footer submission with same text).
110
+ disabled: !!coachingPending
111
+ || !!askQuestionPending
112
+ || !!pendingApproval
113
+ || showPalette
114
+ || showTimeline }), showPalette && (_jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(CommandPalette, { onSelect: (s) => {
107
115
  setPaletteResult(s);
108
116
  setShowPalette(false);
109
117
  }, onCancel: () => setShowPalette(false) }) })), errorStack.map((e) => (_jsx(Box, { children: _jsx(RichErrorBox, { code: e.code, message: e.body, hint: e.hint, severity: "error" }) }, e.id))), showTimeline && (_jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(SessionTimeline, { entries: timelineEntries, onClose: () => setShowTimeline(false) }) })), pendingApproval && (_jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(ApprovalModal, { change: pendingApproval.change, effectiveRisk: pendingApproval.effectiveRisk, transport: pendingApproval.transport, onApply: () => {
package/dist/ui/footer.js CHANGED
@@ -24,7 +24,7 @@ function appendHistory(line, history) {
24
24
  history.push(line);
25
25
  }
26
26
  const PEACH = '#FFCBA4';
27
- export function Footer({ onSubmit, onSlashTrigger, onHistoryTrigger, onValueChange, paletteResult }) {
27
+ export function Footer({ onSubmit, onSlashTrigger, onHistoryTrigger, onValueChange, paletteResult, disabled }) {
28
28
  const [value, setValue] = useState('');
29
29
  const [history] = useState(loadHistory);
30
30
  const [histIdx, setHistIdx] = useState(-1);
@@ -99,10 +99,13 @@ export function Footer({ onSubmit, onSlashTrigger, onHistoryTrigger, onValueChan
99
99
  if (fuzzyMode) {
100
100
  return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "History search (Ctrl+R)" }), _jsx(TextInput, { value: fuzzyQuery, onChange: setFuzzyQuery, placeholder: "type to search..." }), fuzzyResults.map((r, i) => _jsx(Text, { dimColor: i > 0, children: r }, i)), _jsx(Text, { dimColor: true, children: "Esc: cancel Enter: select" })] }));
101
101
  }
102
- return (
103
- // Phase D6 (2026-05-17) — borderless input. The border had pushed
104
- // the terminal cursor one line below the input (the "booger"); now
105
- // both the terminal cursor AND TextInput's block character land at
106
- // the same position, appearing as a single blinking cursor.
107
- _jsxs(Box, { children: [_jsx(Text, { bold: true, color: PEACH, children: "CSPeach \u203A " }), _jsx(TextInput, { value: value, onChange: setValue, onSubmit: handleSubmit })] }));
102
+ // 2026-05-17 — when a modal owns input (coaching picker / ask_question),
103
+ // render a dim placeholder INSTEAD of the live TextInput. Otherwise
104
+ // keystrokes go to BOTH the modal AND this TextInput, and the queued
105
+ // Footer submission fires after the modal resolves → spurious picker
106
+ // for what the user thought was their modal answer.
107
+ if (disabled) {
108
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: PEACH, children: "CSPeach \u203A " }), _jsx(Text, { dimColor: true, children: "(answer above to continue)" })] }));
109
+ }
110
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: PEACH, children: "CSPeach \u203A " }), _jsx(TextInput, { value: value, onChange: setValue, onSubmit: handleSubmit })] }));
108
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cspeach/cli",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "CSPeach — AI-assisted ABAP development CLI. Direct SAP access, 34 skills, safety gates.",
5
5
  "keywords": [
6
6
  "abap",