@este.systems/dsc 0.2.1 → 0.3.0
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/api.js +63 -0
- package/dist/api.js.map +1 -1
- package/dist/approval.js +58 -27
- package/dist/approval.js.map +1 -1
- package/dist/clipboard.js +59 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/history.js +106 -0
- package/dist/history.js.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/slash.js +5 -0
- package/dist/slash.js.map +1 -1
- package/dist/store.js.map +1 -1
- package/dist/tools.js +119 -35
- package/dist/tools.js.map +1 -1
- package/dist/tui/ApprovalDialog.js +41 -1
- package/dist/tui/ApprovalDialog.js.map +1 -1
- package/dist/tui/History.js +14 -1
- package/dist/tui/History.js.map +1 -1
- package/dist/tui/Input.js +17 -0
- package/dist/tui/Input.js.map +1 -1
- package/dist/tui.js +275 -26
- package/dist/tui.js.map +1 -1
- package/dist/ui.js +7 -1
- package/dist/ui.js.map +1 -1
- package/dist/update.js +163 -0
- package/dist/update.js.map +1 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { useStore } from "./useStore.js";
|
|
4
4
|
import { setState } from "../store.js";
|
|
5
|
+
const MAX_BODY_LINES = 24;
|
|
5
6
|
export function ApprovalDialog() {
|
|
6
7
|
const approval = useStore((s) => s.approval);
|
|
7
8
|
useInput((input, key) => {
|
|
@@ -12,6 +13,14 @@ export function ApprovalDialog() {
|
|
|
12
13
|
approval.resolve("y");
|
|
13
14
|
setState({ approval: null });
|
|
14
15
|
}
|
|
16
|
+
else if (ch === "a") {
|
|
17
|
+
// Approve this call AND auto-approve future calls of the same tool
|
|
18
|
+
// for the rest of this session. The asker translates "a" to the
|
|
19
|
+
// "always" ApprovalAnswer, and the tool's gateApproval adds the
|
|
20
|
+
// tool name to ctx.sessionApprovals.
|
|
21
|
+
approval.resolve("a");
|
|
22
|
+
setState({ approval: null });
|
|
23
|
+
}
|
|
15
24
|
else if (ch === "n" || key.escape) {
|
|
16
25
|
approval.resolve("n");
|
|
17
26
|
setState({ approval: null });
|
|
@@ -19,6 +28,37 @@ export function ApprovalDialog() {
|
|
|
19
28
|
}, { isActive: approval !== null });
|
|
20
29
|
if (!approval)
|
|
21
30
|
return null;
|
|
22
|
-
|
|
31
|
+
const lines = approval.body ? approval.body.split("\n") : [];
|
|
32
|
+
const overflow = Math.max(0, lines.length - MAX_BODY_LINES);
|
|
33
|
+
const shown = overflow > 0 ? lines.slice(0, MAX_BODY_LINES) : lines;
|
|
34
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: approval.title }), shown.length > 0 ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [shown.map((line, i) => (_jsx(BodyLine, { line: line, kind: approval.kind }, i))), overflow > 0 ? (_jsxs(Text, { dimColor: true, children: ["\u2026 (", overflow, " more line", overflow === 1 ? "" : "s", ")"] })) : null] })) : null, _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [approval.question, "[y]es / [n]o (Esc rejects)"] }) })] }));
|
|
35
|
+
}
|
|
36
|
+
// Structural coloring per line. Diff lines get green/red/cyan based on prefix;
|
|
37
|
+
// command bodies highlight the `$` prefix; everything else stays default.
|
|
38
|
+
// Keeps the body plain text in the store — color is a render concern.
|
|
39
|
+
function BodyLine({ line, kind, }) {
|
|
40
|
+
if (kind === "diff") {
|
|
41
|
+
if (line.startsWith("+++") || line.startsWith("---")) {
|
|
42
|
+
return _jsx(Text, { bold: true, children: line });
|
|
43
|
+
}
|
|
44
|
+
if (line.startsWith("@@"))
|
|
45
|
+
return _jsx(Text, { color: "cyan", children: line });
|
|
46
|
+
if (line.startsWith("+"))
|
|
47
|
+
return _jsx(Text, { color: "green", children: line });
|
|
48
|
+
if (line.startsWith("-"))
|
|
49
|
+
return _jsx(Text, { color: "red", children: line });
|
|
50
|
+
return _jsx(Text, { children: line });
|
|
51
|
+
}
|
|
52
|
+
if (kind === "command") {
|
|
53
|
+
if (line.startsWith("$ ")) {
|
|
54
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "$" }), " ", line.slice(2)] }));
|
|
55
|
+
}
|
|
56
|
+
return _jsx(Text, { dimColor: true, children: line });
|
|
57
|
+
}
|
|
58
|
+
if (kind === "url")
|
|
59
|
+
return _jsx(Text, { color: "cyan", children: line });
|
|
60
|
+
if (line.startsWith("--- "))
|
|
61
|
+
return _jsx(Text, { dimColor: true, children: line });
|
|
62
|
+
return _jsx(Text, { children: line });
|
|
23
63
|
}
|
|
24
64
|
//# sourceMappingURL=ApprovalDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApprovalDialog.js","sourceRoot":"","sources":["../../src/tui/ApprovalDialog.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,UAAU,cAAc;IAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE7C,QAAQ,CACN,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAC/B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACtB,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACpC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACtB,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,EACD,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAAE,CAChC,CAAC;IAEF,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,CACL,MAAC,GAAG,IACF,WAAW,EAAC,OAAO,EACnB,WAAW,EAAC,QAAQ,EACpB,QAAQ,EAAE,CAAC,EACX,aAAa,EAAC,QAAQ,EACtB,YAAY,EAAE,CAAC,aAEf,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,QAAQ,YACtB,QAAQ,CAAC,KAAK,GACV,EACN,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"ApprovalDialog.js","sourceRoot":"","sources":["../../src/tui/ApprovalDialog.tsx"],"names":[],"mappings":"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"}
|
package/dist/tui/History.js
CHANGED
|
@@ -12,7 +12,7 @@ export function History() {
|
|
|
12
12
|
export function MessageRow({ message: m, streaming = false }) {
|
|
13
13
|
if (m.role === "tool") {
|
|
14
14
|
const label = m.tool_name ? `← ${m.tool_name}` : "← tool";
|
|
15
|
-
return (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [label, ": ",
|
|
15
|
+
return (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [label, ": ", toolTruncate(m.content, 600)] }) }));
|
|
16
16
|
}
|
|
17
17
|
if (m.role === "system") {
|
|
18
18
|
// Errors get red; everything else (slash-command output, notices) goes
|
|
@@ -40,4 +40,17 @@ export function MessageRow({ message: m, streaming = false }) {
|
|
|
40
40
|
function truncate(s, n) {
|
|
41
41
|
return s.length <= n ? s : s.slice(0, n) + "…";
|
|
42
42
|
}
|
|
43
|
+
// Tool-result truncation. The model sees the full output (it's stored as
|
|
44
|
+
// the tool message's content); the TUI just renders a head slice so the
|
|
45
|
+
// dynamic frame stays readable. The marker explains the gap so the user
|
|
46
|
+
// knows nothing was silently lost from the model's perspective.
|
|
47
|
+
function toolTruncate(s, maxChars) {
|
|
48
|
+
if (s.length <= maxChars)
|
|
49
|
+
return s;
|
|
50
|
+
const head = s.slice(0, maxChars);
|
|
51
|
+
const omittedChars = s.length - maxChars;
|
|
52
|
+
const omittedLines = s.slice(maxChars).split("\n").length - 1;
|
|
53
|
+
const lineHint = omittedLines > 0 ? ` / ${omittedLines} more lines` : "";
|
|
54
|
+
return `${head}\n… (${omittedChars} more chars${lineHint}, full output sent to model)`;
|
|
55
|
+
}
|
|
43
56
|
//# sourceMappingURL=History.js.map
|
package/dist/tui/History.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../src/tui/History.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,yEAAyE;AACzE,0EAA0E;AAC1E,0CAA0C;AAC1C,MAAM,UAAU,OAAO;IACrB,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,CACL,KAAC,MAAM,IAAC,KAAK,EAAE,OAAO,YACnB,CAAC,CAAC,EAAE,EAAE,CAAC,KAAC,UAAU,IAAY,OAAO,EAAE,CAAC,IAAhB,CAAC,CAAC,EAAE,CAAgB,GACtC,CACV,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,GAAG,KAAK,EAAmB;IAC3E,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1D,OAAO,CACL,KAAC,GAAG,IAAC,YAAY,EAAE,CAAC,YAClB,MAAC,IAAI,IAAC,QAAQ,mBACX,KAAK,QAAI,
|
|
1
|
+
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../src/tui/History.tsx"],"names":[],"mappings":"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"}
|
package/dist/tui/Input.js
CHANGED
|
@@ -157,6 +157,23 @@ export function Input({ value, onChange, onSubmit, focus, history = [] }) {
|
|
|
157
157
|
const pre = value.slice(0, cursor);
|
|
158
158
|
const at = value[cursor] ?? " ";
|
|
159
159
|
const post = value.slice(cursor + 1);
|
|
160
|
+
// Ghost-text suggestion: when typing a /slash command and the cursor is
|
|
161
|
+
// at the end, show what TAB would complete to as dim suffix. Skip when
|
|
162
|
+
// a multiline buffer continuation is active or when the cursor isn't at
|
|
163
|
+
// the end (mid-edit completions would look like the cursor jumped).
|
|
164
|
+
const isAtEnd = cursor === value.length;
|
|
165
|
+
let ghost = "";
|
|
166
|
+
if (isAtEnd && value.startsWith("/")) {
|
|
167
|
+
const { completion } = completeSlash(value);
|
|
168
|
+
if (completion && completion.startsWith(value) && completion.length > value.length) {
|
|
169
|
+
ghost = completion.slice(value.length);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (ghost) {
|
|
173
|
+
// Render the cursor on top of the first ghost char so the user sees
|
|
174
|
+
// "press TAB to take this" with the caret positioned to commit.
|
|
175
|
+
return (_jsxs(Text, { children: [pre, focus ? (_jsx(Text, { inverse: true, dimColor: true, children: ghost[0] })) : (_jsx(Text, { dimColor: true, children: ghost[0] })), _jsx(Text, { dimColor: true, children: ghost.slice(1) })] }));
|
|
176
|
+
}
|
|
160
177
|
return (_jsxs(Text, { children: [pre, focus ? _jsx(Text, { inverse: true, children: at }) : _jsx(Text, { children: at }), post] }));
|
|
161
178
|
}
|
|
162
179
|
//# sourceMappingURL=Input.js.map
|
package/dist/tui/Input.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.js","sourceRoot":"","sources":["../../src/tui/Input.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Input.js","sourceRoot":"","sources":["../../src/tui/Input.tsx"],"names":[],"mappings":"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"}
|
package/dist/tui.js
CHANGED
|
@@ -2,16 +2,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { render } from "ink";
|
|
3
3
|
import { App } from "./tui/App.js";
|
|
4
4
|
import { getState, setState } from "./store.js";
|
|
5
|
-
import { AVAILABLE_MODELS, DEFAULT_MODEL, DeepSeekError, computeCostUsd, configPath, hasApiKey, recordUsage, } from "./api.js";
|
|
5
|
+
import { AVAILABLE_MODELS, DEFAULT_MODEL, DeepSeekError, apiKeySource, computeCostUsd, configPath, hasApiKey, recordUsage, saveApiKey, } from "./api.js";
|
|
6
6
|
import { runAgent, formatCost, estimateContextTokens, } from "./agent.js";
|
|
7
7
|
import * as history from "./history.js";
|
|
8
8
|
import * as approval from "./approval.js";
|
|
9
9
|
import * as audit from "./audit.js";
|
|
10
10
|
import * as replHistory from "./repl_history.js";
|
|
11
11
|
import { promises as fsp } from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import * as path from "node:path";
|
|
12
14
|
import { compactSession } from "./compact.js";
|
|
13
15
|
import { formatVersionInfo } from "./version.js";
|
|
14
16
|
import { openEditor } from "./editor.js";
|
|
17
|
+
import { checkForUpdate, runUpdate } from "./update.js";
|
|
18
|
+
import { copyToClipboard } from "./clipboard.js";
|
|
15
19
|
const AUTO_COMPACT_AT_TOKENS = Number(process.env.DSC_AUTO_COMPACT ?? "0") || 0;
|
|
16
20
|
const AUTO_COMPACT_KEEP = Number(process.env.DSC_AUTO_COMPACT_KEEP ?? "4") || 4;
|
|
17
21
|
// Minimal arg parsing for the TUI entry. Mirrors the subset of flags the
|
|
@@ -66,12 +70,11 @@ async function main() {
|
|
|
66
70
|
].join("\n") + "\n");
|
|
67
71
|
process.exit(0);
|
|
68
72
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
73
|
+
// First-launch UX: don't hard-exit when there's no API key. Boot the
|
|
74
|
+
// TUI and surface a one-line system message telling the user how to
|
|
75
|
+
// set one via /api-key. Submitting a turn before setting the key will
|
|
76
|
+
// surface DeepSeekError, which we already render as a system error.
|
|
77
|
+
const startupKeyMissing = !hasApiKey();
|
|
75
78
|
const cwd = process.cwd();
|
|
76
79
|
await history.migrateLegacyIfPresent(cwd, DEFAULT_MODEL);
|
|
77
80
|
// Auto-resume most recent for cwd unless --no-resume; otherwise fresh.
|
|
@@ -95,6 +98,7 @@ async function main() {
|
|
|
95
98
|
yolo: !!cli.yolo,
|
|
96
99
|
filesTouched: stats.files_touched,
|
|
97
100
|
sessionId: session.id,
|
|
101
|
+
sessionApprovals: new Set(),
|
|
98
102
|
};
|
|
99
103
|
// Coalescing save (same shape as REPL).
|
|
100
104
|
let savePromise = null;
|
|
@@ -166,15 +170,16 @@ async function main() {
|
|
|
166
170
|
// 1-second tick so the session timer in StatusBar advances even when idle.
|
|
167
171
|
const timerId = setInterval(syncStatus, 1000);
|
|
168
172
|
// Install the approval asker — routes confirm* calls through the
|
|
169
|
-
// ApprovalDialog component. The
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
approval.setAsker((
|
|
173
|
+
// ApprovalDialog component. The structured payload (title + body + kind)
|
|
174
|
+
// goes into the dialog so the diff/preview renders inline in the yellow
|
|
175
|
+
// bordered box rather than bleeding above ink's dynamic frame.
|
|
176
|
+
approval.setAsker((req) => new Promise((resolve) => {
|
|
173
177
|
setState({
|
|
174
178
|
approval: {
|
|
175
|
-
title:
|
|
176
|
-
body: "",
|
|
177
|
-
|
|
179
|
+
title: req.title,
|
|
180
|
+
body: req.body ?? "",
|
|
181
|
+
kind: req.kind,
|
|
182
|
+
question: req.question,
|
|
178
183
|
resolve,
|
|
179
184
|
},
|
|
180
185
|
});
|
|
@@ -204,20 +209,44 @@ async function main() {
|
|
|
204
209
|
},
|
|
205
210
|
onAssistantFinal: (turnId, msg) => {
|
|
206
211
|
// Move from `current` into the static history (so it's selectable).
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
212
|
+
// Only skip when the turn was purely a tool dispatch (content empty,
|
|
213
|
+
// reasoning empty, and tool_calls present) — those produce visible
|
|
214
|
+
// tool messages of their own, so an "(no content)" placeholder would
|
|
215
|
+
// be noise. A truly empty turn (no content, no reasoning, no tool
|
|
216
|
+
// calls) still gets a marker so the user knows the agent stopped
|
|
217
|
+
// rather than wondering whether something silently broke.
|
|
218
|
+
const hasText = (msg.content && msg.content.length) ||
|
|
219
|
+
(msg.reasoning && msg.reasoning.length);
|
|
220
|
+
const isToolOnly = !hasText && msg.tool_calls && msg.tool_calls.length;
|
|
210
221
|
setState((s) => {
|
|
211
222
|
const next = { current: null };
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
if (isToolOnly) {
|
|
224
|
+
// pure tool dispatch — tool messages will appear separately
|
|
225
|
+
}
|
|
226
|
+
else if (hasText) {
|
|
227
|
+
next.history = [
|
|
228
|
+
...s.history,
|
|
229
|
+
{
|
|
230
|
+
id: turnId,
|
|
231
|
+
role: "assistant",
|
|
232
|
+
content: msg.content,
|
|
233
|
+
reasoning: msg.reasoning,
|
|
234
|
+
tool_calls: msg.tool_calls,
|
|
235
|
+
},
|
|
236
|
+
];
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// Genuinely empty turn — show a marker rather than appearing to
|
|
240
|
+
// hang. Most often hit when an upstream filter or network drop
|
|
241
|
+
// cuts the stream before any tokens arrive.
|
|
242
|
+
next.history = [
|
|
243
|
+
...s.history,
|
|
244
|
+
{
|
|
245
|
+
id: turnId,
|
|
246
|
+
role: "system",
|
|
247
|
+
content: "(model returned no content)",
|
|
248
|
+
},
|
|
249
|
+
];
|
|
221
250
|
}
|
|
222
251
|
return next;
|
|
223
252
|
});
|
|
@@ -415,11 +444,16 @@ async function main() {
|
|
|
415
444
|
"/lang [name|off] force the model to reply in a language",
|
|
416
445
|
"/auto-continue [N|off] auto-grant N extra MAX_TOOL_DEPTH budgets",
|
|
417
446
|
"/cost show token usage and cost",
|
|
447
|
+
"/copy copy last assistant response to clipboard",
|
|
418
448
|
"/version show version info",
|
|
419
449
|
"/compact [keep] summarize old turns (default keep=4)",
|
|
420
450
|
"/transcript dump full message log",
|
|
421
451
|
"/audit [path|show N] audit log info",
|
|
422
452
|
"/queue [clear] list or clear queued prompts",
|
|
453
|
+
"/export [path] write current session JSON for transfer",
|
|
454
|
+
"/import <path> load session JSON; rebinds cwd here (--keep-cwd to skip)",
|
|
455
|
+
"/api-key [key] show source / save api key to config file",
|
|
456
|
+
"/update check npm for a newer dsc and install it",
|
|
423
457
|
"/exit exit",
|
|
424
458
|
].join("\n"));
|
|
425
459
|
return true;
|
|
@@ -429,6 +463,10 @@ async function main() {
|
|
|
429
463
|
stats = session.stats;
|
|
430
464
|
toolCtx.filesTouched = stats.files_touched;
|
|
431
465
|
toolCtx.sessionId = session.id;
|
|
466
|
+
// Per-tool always-approval is conversation-scoped, so /clear has
|
|
467
|
+
// to drop it — otherwise the user would carry over implicit trust
|
|
468
|
+
// they made to a session they just walked away from.
|
|
469
|
+
toolCtx.sessionApprovals = new Set();
|
|
432
470
|
setState({ history: [], current: null });
|
|
433
471
|
info(`new session started (${session.id})`);
|
|
434
472
|
syncStatus();
|
|
@@ -536,6 +574,32 @@ async function main() {
|
|
|
536
574
|
case "cost":
|
|
537
575
|
info(formatCost(stats, model));
|
|
538
576
|
return true;
|
|
577
|
+
case "copy": {
|
|
578
|
+
// Copy the most recent assistant response to the OS clipboard. The
|
|
579
|
+
// last *assistant* (not tool, not system, not user) is what the
|
|
580
|
+
// user usually wants — the actual answer text.
|
|
581
|
+
const state = getState();
|
|
582
|
+
let target;
|
|
583
|
+
for (let i = state.history.length - 1; i >= 0; i--) {
|
|
584
|
+
const m = state.history[i];
|
|
585
|
+
if (m.role === "assistant" && m.content) {
|
|
586
|
+
target = m;
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (!target) {
|
|
591
|
+
info("no assistant message to copy");
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
await copyToClipboard(target.content);
|
|
596
|
+
info(`copied ${target.content.length} chars to clipboard`);
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
info(`error: ${e.message}`);
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
539
603
|
case "model":
|
|
540
604
|
if (!arg) {
|
|
541
605
|
info(`current model: ${model}`);
|
|
@@ -624,6 +688,60 @@ async function main() {
|
|
|
624
688
|
case "version":
|
|
625
689
|
info(formatVersionInfo());
|
|
626
690
|
return true;
|
|
691
|
+
case "update": {
|
|
692
|
+
// Two-phase: check first so we don't run npm install when we're
|
|
693
|
+
// already current. Force-fetches the latest (ignores the 24h cache
|
|
694
|
+
// because the user explicitly asked) and reports either way.
|
|
695
|
+
info("checking npm for a newer version…");
|
|
696
|
+
try {
|
|
697
|
+
const check = await checkForUpdate({ force: true });
|
|
698
|
+
if (!check.newerAvailable) {
|
|
699
|
+
info(`up to date (${check.current})`);
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
info(`installing ${check.latest} (currently ${check.current}) via npm…`);
|
|
703
|
+
const r = await runUpdate();
|
|
704
|
+
if (!r.ok) {
|
|
705
|
+
// npm prints reasonably descriptive errors itself; pass them
|
|
706
|
+
// through so EACCES / permission issues are obvious.
|
|
707
|
+
info(`error: update failed\n${r.output.slice(-1500)}\n\n` +
|
|
708
|
+
`If this is a permission error, try: sudo npm install -g @este.systems/dsc@latest`);
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
info(`installed ${check.latest}. Exit and re-run \`dsc\` to pick up the new version.`);
|
|
712
|
+
}
|
|
713
|
+
catch (e) {
|
|
714
|
+
info(`error: update failed: ${e.message}`);
|
|
715
|
+
}
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
case "api-key": {
|
|
719
|
+
const text = arg.trim();
|
|
720
|
+
if (!text) {
|
|
721
|
+
// No arg: show where the key is configured (don't print the key
|
|
722
|
+
// itself; that's not the kind of thing /command output should
|
|
723
|
+
// splash to scrollback).
|
|
724
|
+
const src = apiKeySource();
|
|
725
|
+
if (src === "env") {
|
|
726
|
+
info("api key: set via $DEEPSEEK_API_KEY (env)");
|
|
727
|
+
}
|
|
728
|
+
else if (src === "file") {
|
|
729
|
+
info(`api key: stored in ${configPath()}`);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
info(`api key: not set\nrun /api-key <key> to save one, or export DEEPSEEK_API_KEY`);
|
|
733
|
+
}
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
try {
|
|
737
|
+
const written = await saveApiKey(text);
|
|
738
|
+
info(`api key saved to ${written}`);
|
|
739
|
+
}
|
|
740
|
+
catch (e) {
|
|
741
|
+
info(`error: ${e.message}`);
|
|
742
|
+
}
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
627
745
|
case "audit": {
|
|
628
746
|
const sub = arg.trim();
|
|
629
747
|
if (!sub || sub === "path") {
|
|
@@ -688,6 +806,70 @@ async function main() {
|
|
|
688
806
|
await runCompaction(keep, false);
|
|
689
807
|
return true;
|
|
690
808
|
}
|
|
809
|
+
case "export": {
|
|
810
|
+
// `/export [path]` — writes the current session JSON. Relative
|
|
811
|
+
// paths resolve against the launch cwd, not against the session's
|
|
812
|
+
// recorded cwd, so it lands where the user actually is. Default
|
|
813
|
+
// target is the cwd, named after /save'd name (or id) and date.
|
|
814
|
+
const dest = arg.trim() || ".";
|
|
815
|
+
const absDest = path.isAbsolute(dest) ? dest : path.resolve(cwd, dest);
|
|
816
|
+
try {
|
|
817
|
+
await persist(); // flush any pending writes before export
|
|
818
|
+
const written = await history.exportSession(session.id, absDest);
|
|
819
|
+
info(`exported to ${written}`);
|
|
820
|
+
}
|
|
821
|
+
catch (e) {
|
|
822
|
+
info(`error: export failed: ${e.message}`);
|
|
823
|
+
}
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
case "import": {
|
|
827
|
+
// `/import <path>` — reads a session JSON, rebinds its cwd to the
|
|
828
|
+
// current directory so auto-resume picks it up here, and loads it
|
|
829
|
+
// as the active session immediately. Keep the previous cwd with
|
|
830
|
+
// `--keep-cwd` (positional flag, kept simple).
|
|
831
|
+
const tokens = arg.trim().split(/\s+/).filter(Boolean);
|
|
832
|
+
const keepCwd = tokens.includes("--keep-cwd");
|
|
833
|
+
const file = tokens.find((t) => !t.startsWith("--"));
|
|
834
|
+
if (!file) {
|
|
835
|
+
info("error: usage: /import <path> [--keep-cwd]");
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
const absFile = path.isAbsolute(file) ? file : path.resolve(cwd, file);
|
|
839
|
+
try {
|
|
840
|
+
const loaded = await history.importSession(absFile, {
|
|
841
|
+
rebindCwd: keepCwd ? undefined : cwd,
|
|
842
|
+
});
|
|
843
|
+
session = loaded;
|
|
844
|
+
messages = session.messages;
|
|
845
|
+
stats = session.stats;
|
|
846
|
+
model = session.model;
|
|
847
|
+
toolCtx.filesTouched = stats.files_touched;
|
|
848
|
+
toolCtx.sessionId = session.id;
|
|
849
|
+
// Refill the visible history so the user sees the imported
|
|
850
|
+
// conversation right away (same logic the /resume path uses).
|
|
851
|
+
const restored = [];
|
|
852
|
+
for (const m of messages) {
|
|
853
|
+
if (m.role === "system")
|
|
854
|
+
continue;
|
|
855
|
+
restored.push({
|
|
856
|
+
id: `r-${restored.length}`,
|
|
857
|
+
role: m.role,
|
|
858
|
+
content: typeof m.content === "string" ? m.content : "",
|
|
859
|
+
tool_call_id: m.tool_call_id,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
setState({ history: restored, current: null, model });
|
|
863
|
+
syncStatus();
|
|
864
|
+
const userTurns = messages.filter((m) => m.role === "user").length;
|
|
865
|
+
const cwdNote = keepCwd ? "" : " (cwd rebound to here)";
|
|
866
|
+
info(`imported ${session.id} (${userTurns} turns, model ${model})${cwdNote}`);
|
|
867
|
+
}
|
|
868
|
+
catch (e) {
|
|
869
|
+
info(`error: import failed: ${e.message}`);
|
|
870
|
+
}
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
691
873
|
case "edit": {
|
|
692
874
|
// ink owns the terminal — unmount before spawning $EDITOR so the
|
|
693
875
|
// editor gets a clean screen. We also clear `history` before the
|
|
@@ -723,6 +905,16 @@ async function main() {
|
|
|
723
905
|
void replHistory.append(text);
|
|
724
906
|
}
|
|
725
907
|
if (text.startsWith("/")) {
|
|
908
|
+
// Echo the command into history so the user can see which command
|
|
909
|
+
// produced the system-line output that follows. Without this the
|
|
910
|
+
// info() lines look orphaned — especially when you scroll back
|
|
911
|
+
// and there's no record of what triggered each one.
|
|
912
|
+
setState((s) => ({
|
|
913
|
+
history: [
|
|
914
|
+
...s.history,
|
|
915
|
+
{ id: `u-${s.history.length}`, role: "user", content: text },
|
|
916
|
+
],
|
|
917
|
+
}));
|
|
726
918
|
void handleSlash(text);
|
|
727
919
|
return;
|
|
728
920
|
}
|
|
@@ -769,6 +961,63 @@ async function main() {
|
|
|
769
961
|
process.exit(0);
|
|
770
962
|
}
|
|
771
963
|
mountApp();
|
|
964
|
+
// One-time welcome panel. Stamped via a touch-file under XDG_STATE_HOME
|
|
965
|
+
// so we never nag the same user twice. New users get an orientation
|
|
966
|
+
// (key, /help, hotkeys); the more-targeted "no API key" reminder fires
|
|
967
|
+
// on every subsequent launch when no key is configured.
|
|
968
|
+
const stateDir = (() => {
|
|
969
|
+
const xdg = process.env.XDG_STATE_HOME;
|
|
970
|
+
const base = xdg && xdg.length ? xdg : path.join(homedir(), ".local", "state");
|
|
971
|
+
return path.join(base, "dsc");
|
|
972
|
+
})();
|
|
973
|
+
const welcomeStamp = path.join(stateDir, "welcomed");
|
|
974
|
+
let alreadyWelcomed = false;
|
|
975
|
+
try {
|
|
976
|
+
await fsp.access(welcomeStamp);
|
|
977
|
+
alreadyWelcomed = true;
|
|
978
|
+
}
|
|
979
|
+
catch {
|
|
980
|
+
// first launch
|
|
981
|
+
}
|
|
982
|
+
if (!alreadyWelcomed) {
|
|
983
|
+
const lines = [
|
|
984
|
+
"Welcome to dsc — a CLI coding agent for DeepSeek.",
|
|
985
|
+
"",
|
|
986
|
+
" /help full command list (TAB completes any /command)",
|
|
987
|
+
" Up / Down recall past prompts",
|
|
988
|
+
" ESC abort a running turn",
|
|
989
|
+
" Ctrl+D exit",
|
|
990
|
+
];
|
|
991
|
+
if (startupKeyMissing) {
|
|
992
|
+
lines.push("", `To get started, save your API key: /api-key sk-...`, ` (or export DEEPSEEK_API_KEY in your shell)`);
|
|
993
|
+
}
|
|
994
|
+
info(lines.join("\n"));
|
|
995
|
+
try {
|
|
996
|
+
await fsp.mkdir(stateDir, { recursive: true });
|
|
997
|
+
await fsp.writeFile(welcomeStamp, new Date().toISOString(), "utf8");
|
|
998
|
+
}
|
|
999
|
+
catch {
|
|
1000
|
+
// best-effort; missing perms just means the welcome shows again
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
else if (startupKeyMissing) {
|
|
1004
|
+
// Targeted nudge for returning users who somehow lost their key.
|
|
1005
|
+
info(`No DeepSeek API key found. Run "/api-key <key>" to save one to ${configPath()}, or export DEEPSEEK_API_KEY in your shell.`);
|
|
1006
|
+
}
|
|
1007
|
+
// Fire-and-forget update probe. Uses the 24h cache by default so a
|
|
1008
|
+
// chatty notice doesn't appear on every launch — only once per day,
|
|
1009
|
+
// when the registry says we're behind. Failures are swallowed silently.
|
|
1010
|
+
void (async () => {
|
|
1011
|
+
try {
|
|
1012
|
+
const check = await checkForUpdate();
|
|
1013
|
+
if (check.newerAvailable) {
|
|
1014
|
+
info(`update available: ${check.latest} (you have ${check.current}). Run /update to install.`);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
catch {
|
|
1018
|
+
// best-effort; offline launches shouldn't yell at the user
|
|
1019
|
+
}
|
|
1020
|
+
})();
|
|
772
1021
|
}
|
|
773
1022
|
function truncate(s, n) {
|
|
774
1023
|
return s.length <= n ? s : s.slice(0, n) + "…";
|