@sns-myagent/cli 0.3.5 → 0.3.7
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 +51 -1
- package/package.json +1 -1
- package/scripts/smoke-tui.ts +18 -22
- package/src/tui/chat-blocks.ts +85 -179
- package/src/tui/code-cell.ts +9 -9
- package/src/tui/command-palette.ts +56 -174
- package/src/tui/index.ts +1 -1
- package/src/tui/splash.ts +67 -111
- package/src/ui/error-display.ts +60 -103
- package/src/ui/gradient.ts +15 -95
- package/src/ui/index.ts +3 -9
- package/src/ui/memory-toast.ts +38 -77
- package/src/ui/status-bar.ts +24 -47
package/src/ui/gradient.ts
CHANGED
|
@@ -1,104 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Minimal palette for SNS-MyAgent TUI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* One accent color (cyan) for prompts and brand. Everything else uses
|
|
5
|
+
* terminal defaults + chalk.dim for secondary text. No gradients, no
|
|
6
|
+
* 4-role color explosion — the terminal is for reading, not for show.
|
|
6
7
|
*/
|
|
7
|
-
import gradient from "gradient-string";
|
|
8
|
-
import chalk from "chalk";
|
|
9
|
-
import { BRAND_GRADIENT, ACCENT_GRADIENT, SUBTLE_GRADIENT, ROLE_HEX } from "./colors.js";
|
|
10
|
-
|
|
11
|
-
// ── Pre-built gradient instances ──
|
|
12
|
-
|
|
13
|
-
/** Full brand gradient: cyan → purple → pink. */
|
|
14
|
-
export const brandGradient: (text: string) => string = gradient(BRAND_GRADIENT);
|
|
15
|
-
|
|
16
|
-
/** Accent gradient: purple → cyan. */
|
|
17
|
-
export const accentGradient: (text: string) => string = gradient(ACCENT_GRADIENT);
|
|
18
|
-
|
|
19
|
-
/** Subtle gradient: cyan → purple. */
|
|
20
|
-
export const subtleGradient: (text: string) => string = gradient(SUBTLE_GRADIENT);
|
|
21
|
-
|
|
22
|
-
// ── Gradient functions ──
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Apply brand gradient to text.
|
|
26
|
-
*/
|
|
27
|
-
export function brand(text: string): string {
|
|
28
|
-
return brandGradient(text);
|
|
29
|
-
}
|
|
30
8
|
|
|
31
|
-
|
|
32
|
-
* Apply accent gradient to text.
|
|
33
|
-
*/
|
|
34
|
-
export function accentGrad(text: string): string {
|
|
35
|
-
return accentGradient(text);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Apply subtle gradient to text.
|
|
40
|
-
*/
|
|
41
|
-
export function subtle(text: string): string {
|
|
42
|
-
return subtleGradient(text);
|
|
43
|
-
}
|
|
9
|
+
import chalk from "chalk";
|
|
44
10
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
export function gradientLine(width: number): string {
|
|
49
|
-
const line = "─".repeat(width);
|
|
50
|
-
return brandGradient(line);
|
|
51
|
-
}
|
|
11
|
+
/** Single accent for prompts, brand, and focus. */
|
|
12
|
+
export const accent = chalk.cyan;
|
|
52
13
|
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
export function labeledGradientLine(label: string, width: number): string {
|
|
57
|
-
const labelLen = label.length + 4; // 2 spaces each side
|
|
58
|
-
const sideLen = Math.max(0, Math.floor((width - labelLen) / 2));
|
|
59
|
-
const left = "─".repeat(sideLen);
|
|
60
|
-
const right = "─".repeat(width - sideLen - labelLen);
|
|
61
|
-
return brandGradient(left) + chalk.dim(` ${label} `) + brandGradient(right);
|
|
62
|
-
}
|
|
14
|
+
/** Brand text — bold cyan, no rainbow. */
|
|
15
|
+
export const brand = (s: string) => chalk.cyan.bold(s);
|
|
63
16
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
*/
|
|
67
|
-
export function roleGradient(role: keyof typeof ROLE_HEX): (text: string) => string {
|
|
68
|
-
const hex = ROLE_HEX[role];
|
|
69
|
-
const darker = adjustBrightness(hex, -30);
|
|
70
|
-
return gradient([hex, darker]);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Create a gradient for status indicators.
|
|
75
|
-
*/
|
|
76
|
-
export function statusGradient(status: "success" | "warning" | "error" | "info"): (text: string) => string {
|
|
77
|
-
switch (status) {
|
|
78
|
-
case "success":
|
|
79
|
-
return gradient(["#00d2ff", "#00ff88"]);
|
|
80
|
-
case "warning":
|
|
81
|
-
return gradient(["#ffd700", "#ff8c00"]);
|
|
82
|
-
case "error":
|
|
83
|
-
return gradient(["#ff4444", "#cc0000"]);
|
|
84
|
-
case "info":
|
|
85
|
-
return gradient(["#00d2ff", "#7b2ff7"]);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Render text with a gradient background (using chalk bg).
|
|
91
|
-
*/
|
|
92
|
-
export function gradientBg(text: string, hex: string): string {
|
|
93
|
-
return chalk.bgHex(hex)(text);
|
|
94
|
-
}
|
|
17
|
+
/** Subtle / dimmed text. */
|
|
18
|
+
export const subtle = chalk.dim;
|
|
95
19
|
|
|
96
|
-
|
|
20
|
+
/** Stronger emphasis than subtle. */
|
|
21
|
+
export const muted = chalk.gray;
|
|
97
22
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const r = Math.min(255, Math.max(0, ((num >> 16) & 0xff) + amount));
|
|
101
|
-
const g = Math.min(255, Math.max(0, ((num >> 8) & 0xff) + amount));
|
|
102
|
-
const b = Math.min(255, Math.max(0, (num & 0xff) + amount));
|
|
103
|
-
return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, "0")}`;
|
|
104
|
-
}
|
|
23
|
+
/** Accent for inline emphasis (e.g. file paths, ids). */
|
|
24
|
+
export const inline = chalk.cyan;
|
package/src/ui/index.ts
CHANGED
|
@@ -5,17 +5,11 @@ export { createSpinner } from "./spinner.js";
|
|
|
5
5
|
export { createPrompt } from "./chat-prompt.js";
|
|
6
6
|
export { renderStatusBar, clearStatusBar, type StatusBarState } from "./status-bar.js";
|
|
7
7
|
export {
|
|
8
|
-
|
|
9
|
-
accentGradient,
|
|
10
|
-
subtleGradient,
|
|
8
|
+
accent,
|
|
11
9
|
brand,
|
|
12
|
-
accentGrad,
|
|
13
10
|
subtle,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
roleGradient,
|
|
17
|
-
statusGradient,
|
|
18
|
-
gradientBg,
|
|
11
|
+
muted,
|
|
12
|
+
inline,
|
|
19
13
|
} from "./gradient.js";
|
|
20
14
|
export { renderErrorDisplay, renderQuickError, renderWarning, type ErrorDisplayOptions, type ErrorSeverity } from "./error-display.js";
|
|
21
15
|
export { renderMemoryToast, renderMemoryRecall, renderMemorySave, clearToastLine, type MemoryToastOptions, type ToastType } from "./memory-toast.js";
|
package/src/ui/memory-toast.ts
CHANGED
|
@@ -1,102 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Memory toast
|
|
3
|
-
*
|
|
4
|
-
* Shows brief, non-intrusive memory recall/save notifications.
|
|
5
|
-
* Auto-dismiss style — fades after a short duration.
|
|
2
|
+
* Memory toast — flat `●` prefix, single accent.
|
|
6
3
|
*/
|
|
4
|
+
|
|
7
5
|
import chalk from "chalk";
|
|
8
|
-
import gradient from "gradient-string";
|
|
9
|
-
import { BRAND_GRADIENT } from "./colors.js";
|
|
10
6
|
|
|
11
7
|
export type ToastType = "recall" | "save" | "forget" | "info";
|
|
12
8
|
|
|
13
9
|
export interface MemoryToastOptions {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
memoryId?: string;
|
|
20
|
-
/** Relevance score (0-1). */
|
|
21
|
-
relevance?: number;
|
|
22
|
-
/** Duration in ms before auto-dismiss (default: 3000). */
|
|
23
|
-
duration?: number;
|
|
10
|
+
type: ToastType;
|
|
11
|
+
message: string;
|
|
12
|
+
memoryId?: string;
|
|
13
|
+
relevance?: number;
|
|
14
|
+
duration?: number;
|
|
24
15
|
}
|
|
25
16
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
17
|
+
const TOAST_LABEL: Record<ToastType, string> = {
|
|
18
|
+
recall: "memory",
|
|
19
|
+
save: "saved",
|
|
20
|
+
forget: "forgot",
|
|
21
|
+
info: "info",
|
|
22
|
+
};
|
|
32
23
|
|
|
33
|
-
/**
|
|
34
|
-
* Render a memory toast notification line.
|
|
35
|
-
* Returns a string that can be printed and then cleared.
|
|
36
|
-
*/
|
|
37
24
|
export function renderMemoryToast(opts: MemoryToastOptions): string {
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
const label = chalk.cyan(`● ${TOAST_LABEL[opts.type]}`);
|
|
26
|
+
const msg = opts.message;
|
|
27
|
+
const parts = [label, msg];
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
if (opts.memoryId) {
|
|
30
|
+
parts.push(chalk.dim(`#${opts.memoryId.slice(0, 8)}`));
|
|
31
|
+
}
|
|
44
32
|
|
|
45
|
-
|
|
33
|
+
if (opts.relevance !== undefined) {
|
|
34
|
+
const score = Math.round(opts.relevance * 100);
|
|
35
|
+
parts.push(chalk.cyan(`(${score}%)`));
|
|
36
|
+
}
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
parts.push(chalk.dim(`#${opts.memoryId.slice(0, 8)}`));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (opts.relevance !== undefined) {
|
|
52
|
-
const score = Math.round(opts.relevance * 100);
|
|
53
|
-
const scoreColor = score > 80 ? chalk.green : score > 50 ? chalk.yellow : chalk.dim;
|
|
54
|
-
parts.push(scoreColor(`(${score}%)`));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return ` ${parts.join(" ")}`;
|
|
38
|
+
return ` ${parts.join(" ")}`;
|
|
58
39
|
}
|
|
59
40
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
snippet: string,
|
|
66
|
-
relevance?: number,
|
|
67
|
-
): string {
|
|
68
|
-
const grad = gradient(BRAND_GRADIENT);
|
|
69
|
-
const icon = "🧠";
|
|
70
|
-
const label = grad(chalk.bold(" MEMORY "));
|
|
71
|
-
const queryStr = chalk.cyan(`"${query}"`);
|
|
72
|
-
const scoreStr = relevance !== undefined
|
|
73
|
-
? chalk.dim(` ${Math.round(relevance * 100)}% match`)
|
|
74
|
-
: "";
|
|
41
|
+
export function renderMemoryRecall(query: string, snippet: string, relevance?: number): string {
|
|
42
|
+
const queryStr = chalk.cyan(`"${query}"`);
|
|
43
|
+
const scoreStr = relevance !== undefined
|
|
44
|
+
? chalk.dim(` ${Math.round(relevance * 100)}% match`)
|
|
45
|
+
: "";
|
|
75
46
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
47
|
+
const lines = [
|
|
48
|
+
` ${chalk.cyan("● memory")} ${queryStr}${scoreStr}`,
|
|
49
|
+
` ${chalk.dim(snippet.slice(0, 120))}${snippet.length > 120 ? chalk.dim("...") : ""}`,
|
|
50
|
+
];
|
|
80
51
|
|
|
81
|
-
|
|
52
|
+
return lines.join("\n");
|
|
82
53
|
}
|
|
83
54
|
|
|
84
|
-
/**
|
|
85
|
-
* Render a memory save confirmation toast.
|
|
86
|
-
*/
|
|
87
55
|
export function renderMemorySave(content: string, scope?: string): string {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const scopeStr = scope ? chalk.dim(` [${scope}]`) : "";
|
|
92
|
-
const preview = content.length > 80 ? content.slice(0, 80) + "..." : content;
|
|
93
|
-
|
|
94
|
-
return `${icon} ${label}${scopeStr} ${chalk.dim(preview)}`;
|
|
56
|
+
const scopeStr = scope ? chalk.dim(` [${scope}]`) : "";
|
|
57
|
+
const preview = content.length > 80 ? content.slice(0, 80) + "..." : content;
|
|
58
|
+
return ` ${chalk.cyan("● saved")}${scopeStr} ${chalk.dim(preview)}`;
|
|
95
59
|
}
|
|
96
60
|
|
|
97
|
-
/**
|
|
98
|
-
* Clear toast line (ANSI escape to clear current line).
|
|
99
|
-
*/
|
|
100
61
|
export function clearToastLine(): void {
|
|
101
|
-
|
|
62
|
+
process.stdout.write("\x1b[2K\r");
|
|
102
63
|
}
|
package/src/ui/status-bar.ts
CHANGED
|
@@ -1,63 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Status bar
|
|
3
|
-
*
|
|
4
|
-
* Renders a bottom-line summary: model, tokens, session time, memory hits.
|
|
5
|
-
* Uses ANSI to draw a single-line bar at the bottom of the terminal.
|
|
6
|
-
* Premium gradient styling.
|
|
2
|
+
* Status bar — flat single line, dim key + cyan value.
|
|
7
3
|
*/
|
|
8
4
|
|
|
9
5
|
import chalk from "chalk";
|
|
10
|
-
import gradient from "gradient-string";
|
|
11
|
-
import { BRAND_GRADIENT } from "./colors.js";
|
|
12
6
|
|
|
13
|
-
/** Runtime state passed into the status bar renderer. */
|
|
14
7
|
export interface StatusBarState {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/** Session start timestamp (ms since epoch). */
|
|
20
|
-
sessionStarted: number;
|
|
21
|
-
/** Number of memory/context hits. */
|
|
22
|
-
memoryHits: number;
|
|
8
|
+
model: string;
|
|
9
|
+
tokensUsed: number;
|
|
10
|
+
sessionStarted: number;
|
|
11
|
+
memoryHits: number;
|
|
23
12
|
}
|
|
24
13
|
|
|
25
14
|
function formatDuration(ms: number): string {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
const secs = Math.floor(ms / 1000);
|
|
16
|
+
const mins = Math.floor(secs / 60);
|
|
17
|
+
const hrs = Math.floor(mins / 60);
|
|
18
|
+
if (hrs > 0) return `${hrs}h${mins % 60}m`;
|
|
19
|
+
if (mins > 0) return `${mins}m${secs % 60}s`;
|
|
20
|
+
return `${secs}s`;
|
|
32
21
|
}
|
|
33
22
|
|
|
34
|
-
/**
|
|
35
|
-
* Render the status bar to stdout.
|
|
36
|
-
* Overwrites the current line using `\r` + ANSI clear-line.
|
|
37
|
-
*/
|
|
38
23
|
export function renderStatusBar(state: StatusBarState): void {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const sep = chalk.dim(" │ ");
|
|
54
|
-
const bar = ` ${segments.join(sep)} `;
|
|
55
|
-
process.stdout.write(`\x1b[2K\r${bar}`);
|
|
24
|
+
const elapsed = formatDuration(Date.now() - state.sessionStarted);
|
|
25
|
+
const tokens = state.tokensUsed > 1000
|
|
26
|
+
? `${(state.tokensUsed / 1000).toFixed(1)}k`
|
|
27
|
+
: String(state.tokensUsed);
|
|
28
|
+
|
|
29
|
+
const k = (s: string) => chalk.dim(s);
|
|
30
|
+
const v = (s: string) => chalk.cyan(s);
|
|
31
|
+
const sep = chalk.dim(" · ");
|
|
32
|
+
|
|
33
|
+
const bar = ` ${k("model")} ${v(state.model)}${sep}${k("tokens")} ${v(tokens)}${sep}${k("time")} ${v(elapsed)}${sep}${k("mem")} ${v(String(state.memoryHits))}`;
|
|
34
|
+
|
|
35
|
+
process.stdout.write(`\x1b[2K\r${bar}`);
|
|
56
36
|
}
|
|
57
37
|
|
|
58
|
-
/**
|
|
59
|
-
* Clear the status bar line (call before printing normal output).
|
|
60
|
-
*/
|
|
61
38
|
export function clearStatusBar(): void {
|
|
62
|
-
|
|
39
|
+
process.stdout.write("\x1b[2K\r");
|
|
63
40
|
}
|