@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 +26 -0
- package/dist/ui/app.js +9 -1
- package/dist/ui/footer.js +10 -7
- package/package.json +1 -1
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
|
|
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
|
-
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
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
|
}
|