@sns-myagent/cli 0.3.8 → 0.3.9
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/CHANGELOG.md +8 -0
- package/README.md +63 -24
- package/bin/snsagent.js +3 -41
- package/dist/cli.js +3267 -3288
- package/package.json +4 -4
- package/src/adapters/telegram/handler.ts +1 -1
- package/src/agents/ensemble.ts +3 -4
- package/src/agents/executor.ts +129 -0
- package/src/async/notifier.ts +1 -1
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/entry.ts +2 -2
- package/src/cli/gallery-fixtures/misc.ts +3 -3
- package/src/cli/grep-cli.ts +3 -3
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/index.ts +186 -31
- package/src/cli/plugin-cli.ts +5 -5
- package/src/cli/setup-cli.ts +3 -3
- package/src/cli/setup-model-picker.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/tiny-models-cli.ts +3 -3
- package/src/cli/ttsr-cli.ts +2 -2
- package/src/cli/update-cli.ts +3 -3
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli-commands.ts +6 -6
- package/src/commands/acp.ts +2 -2
- package/src/commands/chat.ts +3 -3
- package/src/commands/install.ts +5 -5
- package/src/commands/say.ts +4 -4
- package/src/commands/setup.ts +1 -1
- package/src/config/settings-schema.ts +1 -1
- package/src/config/sns-config.ts +1 -1
- package/src/extensibility/plugins/loader.ts +1 -1
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/hindsight/client.ts +1 -1
- package/src/internal-urls/docs-index.generated.txt +2 -0
- package/src/main.ts +4 -0
- package/src/modes/acp/acp-agent.ts +3 -3
- package/src/modes/components/plugin-settings.ts +2 -2
- package/src/modes/components/welcome.ts +66 -160
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/print-mode.ts +2 -2
- package/src/modes/setup-version.ts +1 -1
- package/src/modes/setup-wizard/scenes/byok-setup.ts +354 -0
- package/src/modes/setup-wizard/scenes/outro.ts +2 -2
- package/src/modes/setup-wizard/scenes/providers.ts +6 -5
- package/src/modes/setup-wizard/scenes/splash.ts +3 -3
- package/src/modes/setup-wizard/wizard-overlay.ts +2 -2
- package/src/modes/theme/dark.json +21 -20
- package/src/modes/theme/light.json +30 -29
- package/src/modes/utils/ui-helpers.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +6 -3
- package/src/stt/recorder.ts +3 -3
- package/src/tts/player.ts +1 -1
- package/src/tts/tts-client.ts +1 -1
- package/src/tui/chat-blocks.ts +4 -4
- package/src/tui/chat-ui.ts +3 -3
- package/src/tui/code-cell.ts +3 -3
- package/src/tui/command-palette.ts +3 -3
- package/src/tui/splash.ts +12 -18
- package/src/ui/banner.ts +51 -66
- package/src/ui/chat-prompt.ts +2 -2
- package/src/ui/colors.ts +17 -14
- package/src/ui/error-display.ts +2 -2
- package/src/ui/gradient.ts +3 -3
- package/src/ui/memory-toast.ts +5 -5
- package/src/ui/status-bar.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
|
-
import { gradientLogo,
|
|
2
|
+
import { gradientLogo, SNS_LOGO } from "../../components/welcome";
|
|
3
3
|
import { theme } from "../../theme/theme";
|
|
4
4
|
import { renderStarfield, SETUP_TICK_MS } from "./splash";
|
|
5
5
|
|
|
@@ -21,7 +21,7 @@ export function renderSetupOutro(width: number, height: number, elapsedMs: numbe
|
|
|
21
21
|
const frame = Math.floor(elapsedMs / SETUP_TICK_MS);
|
|
22
22
|
const lines = renderStarfield(width, height, frame + 1000);
|
|
23
23
|
const progress = Math.max(0, Math.min(1, elapsedMs / SETUP_OUTRO_MS));
|
|
24
|
-
const logo = gradientLogo(
|
|
24
|
+
const logo = gradientLogo(SNS_LOGO, progress * 1.2, { pos: (progress * 2) % 1, strength: 1 - progress });
|
|
25
25
|
const title = theme.bold(theme.fg("success", `${theme.status.success} Setup saved`));
|
|
26
26
|
const subtitle = theme.fg("muted", "Handing off to the normal CLI…");
|
|
27
27
|
const sweepWidth = Math.max(1, Math.min(width - 8, Math.floor((width - 8) * progress)));
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { type SgrMouseEvent, TabBar } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { getTabBarTheme } from "../../shared";
|
|
3
|
+
import { ByokSetupTab } from "./byok-setup";
|
|
3
4
|
import { SignInTab } from "./sign-in";
|
|
4
5
|
import type { SetupScene, SetupSceneController, SetupSceneHost, SetupTab } from "./types";
|
|
5
6
|
import { WebSearchTab } from "./web-search";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Tabbed "Set up your providers" scene. Composes independent panels (model
|
|
9
|
-
* sign-in, web search) behind a {@link TabBar}; the active
|
|
10
|
-
* rendering and input, while modal panels (e.g. an in-flight OAuth
|
|
11
|
-
* temporarily suppress tab switching.
|
|
10
|
+
* sign-in, BYOK quick-connect, web search) behind a {@link TabBar}; the active
|
|
11
|
+
* panel owns rendering and input, while modal panels (e.g. an in-flight OAuth
|
|
12
|
+
* login) temporarily suppress tab switching.
|
|
12
13
|
*/
|
|
13
14
|
class ProvidersSceneController implements SetupSceneController {
|
|
14
15
|
title = "Set up your providers";
|
|
15
|
-
subtitle = "Sign in
|
|
16
|
+
subtitle = "Sign in, bring your own key, or pick a web search provider. Press Esc when you're done.";
|
|
16
17
|
|
|
17
18
|
#tabs: SetupTab[];
|
|
18
19
|
#tabBar: TabBar;
|
|
@@ -20,7 +21,7 @@ class ProvidersSceneController implements SetupSceneController {
|
|
|
20
21
|
#tabRowCount = 1;
|
|
21
22
|
|
|
22
23
|
constructor(host: SetupSceneHost) {
|
|
23
|
-
this.#tabs = [new SignInTab(host), new WebSearchTab(host)];
|
|
24
|
+
this.#tabs = [new ByokSetupTab(host), new SignInTab(host), new WebSearchTab(host)];
|
|
24
25
|
this.#tabBar = new TabBar(
|
|
25
26
|
"Providers",
|
|
26
27
|
this.#tabs.map(tab => ({ id: tab.id, label: tab.label })),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
|
-
import { gradientEscape, gradientLogo,
|
|
2
|
+
import { gradientEscape, gradientLogo, SNS_LOGO, type ShineConfig } from "../../components/welcome";
|
|
3
3
|
import { theme } from "../../theme/theme";
|
|
4
4
|
|
|
5
5
|
export const SETUP_SPLASH_MS = 2600;
|
|
6
6
|
export const SETUP_TICK_MS = 33;
|
|
7
7
|
|
|
8
8
|
/** Brand mark at 2x: every glyph doubled horizontally, every row doubled vertically. */
|
|
9
|
-
const LARGE_LOGO =
|
|
9
|
+
const LARGE_LOGO = SNS_LOGO.flatMap(line => {
|
|
10
10
|
let wide = "";
|
|
11
11
|
for (const char of line) {
|
|
12
12
|
wide += char === " " ? " " : `${char}${char}`;
|
|
@@ -188,7 +188,7 @@ export function renderSetupSplash(width: number, height: number, elapsedMs: numb
|
|
|
188
188
|
|
|
189
189
|
/** Centered fallback for windows too small to hold the full scene. */
|
|
190
190
|
function renderCompactSplash(width: number, height: number, phase: number, shine: ShineConfig): string[] {
|
|
191
|
-
const art = height >= 14 ? LARGE_LOGO :
|
|
191
|
+
const art = height >= 14 ? LARGE_LOGO : SNS_LOGO;
|
|
192
192
|
const content = [...gradientLogo(art, phase, shine), "", theme.bold("S n s A g e n t")];
|
|
193
193
|
const start = Math.max(0, Math.floor((height - content.length) / 2));
|
|
194
194
|
const lines: string[] = [];
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
visibleWidth,
|
|
9
9
|
} from "@oh-my-pi/pi-tui";
|
|
10
10
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
11
|
-
import { gradientLogo,
|
|
11
|
+
import { gradientLogo, SNS_LOGO } from "../components/welcome";
|
|
12
12
|
import { theme } from "../theme/theme";
|
|
13
13
|
import type { InteractiveModeContext } from "../types";
|
|
14
14
|
import { renderSetupOutro, SETUP_OUTRO_MS } from "./scenes/outro";
|
|
@@ -200,7 +200,7 @@ export class SetupWizardComponent implements Component, OverlayFocusOwner {
|
|
|
200
200
|
const title = this.#activeScene?.title ?? scene?.title ?? "Setup";
|
|
201
201
|
const subtitle = this.#activeScene?.subtitle;
|
|
202
202
|
const contentWidth = Math.max(MIN_CONTENT_WIDTH, width - SCENE_MARGIN_X * 2);
|
|
203
|
-
const logo = gradientLogo(
|
|
203
|
+
const logo = gradientLogo(SNS_LOGO, 0);
|
|
204
204
|
const header = [
|
|
205
205
|
"",
|
|
206
206
|
...logo.map(line => centerLine(line, width)),
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "sns-agent",
|
|
3
3
|
"name": "dark",
|
|
4
4
|
"vars": {
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"orange": "#F97316",
|
|
6
|
+
"orangeDeep": "#EA580C",
|
|
7
|
+
"orangeLight": "#FB923C",
|
|
7
8
|
"green": "#89d281",
|
|
8
9
|
"red": "#fc3a4b",
|
|
9
10
|
"yellow": "#e4c00f",
|
|
10
11
|
"gray": "#777d88",
|
|
11
12
|
"dimGray": "#5f6673",
|
|
12
13
|
"darkGray": "#3d424a",
|
|
13
|
-
"accent": "#
|
|
14
|
-
"selectedBg": "#
|
|
15
|
-
"userMsgBg": "#
|
|
14
|
+
"accent": "#F97316",
|
|
15
|
+
"selectedBg": "#2a1d12",
|
|
16
|
+
"userMsgBg": "#1a1410",
|
|
16
17
|
"toolPendingBg": "#1d2129",
|
|
17
18
|
"toolSuccessBg": "#161a1f",
|
|
18
19
|
"toolErrorBg": "#291d1d",
|
|
@@ -20,8 +21,8 @@
|
|
|
20
21
|
},
|
|
21
22
|
"colors": {
|
|
22
23
|
"accent": "accent",
|
|
23
|
-
"border": "
|
|
24
|
-
"borderAccent": "
|
|
24
|
+
"border": "orange",
|
|
25
|
+
"borderAccent": "orange",
|
|
25
26
|
"borderMuted": "darkGray",
|
|
26
27
|
"success": "green",
|
|
27
28
|
"error": "red",
|
|
@@ -35,14 +36,14 @@
|
|
|
35
36
|
"userMessageText": "",
|
|
36
37
|
"customMessageBg": "customMsgBg",
|
|
37
38
|
"customMessageText": "",
|
|
38
|
-
"customMessageLabel": "#
|
|
39
|
+
"customMessageLabel": "#FB923C",
|
|
39
40
|
"toolPendingBg": "toolPendingBg",
|
|
40
41
|
"toolSuccessBg": "toolSuccessBg",
|
|
41
42
|
"toolErrorBg": "toolErrorBg",
|
|
42
43
|
"toolTitle": "",
|
|
43
44
|
"toolOutput": "gray",
|
|
44
|
-
"mdHeading": "#
|
|
45
|
-
"mdLink": "#
|
|
45
|
+
"mdHeading": "#F97316",
|
|
46
|
+
"mdLink": "#FB923C",
|
|
46
47
|
"mdLinkUrl": "dimGray",
|
|
47
48
|
"mdCode": "#e5c1ff",
|
|
48
49
|
"mdCodeBlock": "#9CDCFE",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"toolDiffAdded": "green",
|
|
55
56
|
"toolDiffRemoved": "red",
|
|
56
57
|
"toolDiffContext": "gray",
|
|
57
|
-
"link": "#
|
|
58
|
+
"link": "#FB923C",
|
|
58
59
|
"syntaxComment": "#6A9955",
|
|
59
60
|
"syntaxKeyword": "#569CD6",
|
|
60
61
|
"syntaxFunction": "#DCDCAA",
|
|
@@ -66,19 +67,19 @@
|
|
|
66
67
|
"syntaxPunctuation": "#D4D4D4",
|
|
67
68
|
"thinkingOff": "darkGray",
|
|
68
69
|
"thinkingMinimal": "dimGray",
|
|
69
|
-
"thinkingLow": "#
|
|
70
|
-
"thinkingMedium": "#
|
|
71
|
-
"thinkingHigh": "#
|
|
72
|
-
"thinkingXhigh": "#
|
|
73
|
-
"bashMode": "
|
|
70
|
+
"thinkingLow": "#EA580C",
|
|
71
|
+
"thinkingMedium": "#F97316",
|
|
72
|
+
"thinkingHigh": "#FB923C",
|
|
73
|
+
"thinkingXhigh": "#FDBA74",
|
|
74
|
+
"bashMode": "orange",
|
|
74
75
|
"statusLineBg": "#121212",
|
|
75
76
|
"statusLineSep": 244,
|
|
76
|
-
"statusLineModel": "#
|
|
77
|
-
"statusLinePath": "#
|
|
77
|
+
"statusLineModel": "#FB923C",
|
|
78
|
+
"statusLinePath": "#F97316",
|
|
78
79
|
"statusLineGitClean": "#5faf5f",
|
|
79
80
|
"statusLineGitDirty": "#d7af5f",
|
|
80
81
|
"statusLineContext": "#8787af",
|
|
81
|
-
"statusLineSpend": "#
|
|
82
|
+
"statusLineSpend": "#FB923C",
|
|
82
83
|
"statusLineStaged": 70,
|
|
83
84
|
"statusLineDirty": 178,
|
|
84
85
|
"statusLineUntracked": 39,
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "
|
|
2
|
+
"$schema": "sns-agent",
|
|
3
3
|
"name": "light",
|
|
4
4
|
"vars": {
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"orange": "#EA580C",
|
|
6
|
+
"orangeDeep": "#C2410C",
|
|
7
|
+
"orangeLight": "#F97316",
|
|
7
8
|
"green": "#588458",
|
|
8
9
|
"red": "#aa5555",
|
|
9
10
|
"yellow": "#9a7326",
|
|
10
11
|
"mediumGray": "#6c6c6c",
|
|
11
12
|
"dimGray": "#767676",
|
|
12
13
|
"lightGray": "#b0b0b0",
|
|
13
|
-
"selectedBg": "#
|
|
14
|
-
"userMsgBg": "#
|
|
15
|
-
"toolPendingBg": "#
|
|
16
|
-
"toolSuccessBg": "#
|
|
17
|
-
"toolErrorBg": "#
|
|
18
|
-
"customMsgBg": "#
|
|
14
|
+
"selectedBg": "#fdf0e6",
|
|
15
|
+
"userMsgBg": "#faf5f0",
|
|
16
|
+
"toolPendingBg": "#f0f0f0",
|
|
17
|
+
"toolSuccessBg": "#f0f8f0",
|
|
18
|
+
"toolErrorBg": "#f8f0f0",
|
|
19
|
+
"customMsgBg": "#fff5eb"
|
|
19
20
|
},
|
|
20
21
|
"colors": {
|
|
21
|
-
"accent": "
|
|
22
|
-
"border": "
|
|
23
|
-
"borderAccent": "
|
|
22
|
+
"accent": "orange",
|
|
23
|
+
"border": "orange",
|
|
24
|
+
"borderAccent": "orange",
|
|
24
25
|
"borderMuted": "lightGray",
|
|
25
26
|
"success": "green",
|
|
26
27
|
"error": "red",
|
|
@@ -34,22 +35,22 @@
|
|
|
34
35
|
"userMessageText": "",
|
|
35
36
|
"customMessageBg": "customMsgBg",
|
|
36
37
|
"customMessageText": "",
|
|
37
|
-
"customMessageLabel": "#
|
|
38
|
+
"customMessageLabel": "#C2410C",
|
|
38
39
|
"toolPendingBg": "toolPendingBg",
|
|
39
40
|
"toolSuccessBg": "toolSuccessBg",
|
|
40
41
|
"toolErrorBg": "toolErrorBg",
|
|
41
42
|
"toolTitle": "",
|
|
42
43
|
"toolOutput": "mediumGray",
|
|
43
|
-
"mdHeading": "
|
|
44
|
-
"mdLink": "
|
|
44
|
+
"mdHeading": "orange",
|
|
45
|
+
"mdLink": "orangeLight",
|
|
45
46
|
"mdLinkUrl": "dimGray",
|
|
46
|
-
"mdCode": "
|
|
47
|
+
"mdCode": "orangeDeep",
|
|
47
48
|
"mdCodeBlock": "green",
|
|
48
49
|
"mdCodeBlockBorder": "mediumGray",
|
|
49
50
|
"mdQuote": "mediumGray",
|
|
50
51
|
"mdQuoteBorder": "mediumGray",
|
|
51
52
|
"mdHr": "mediumGray",
|
|
52
|
-
"mdListBullet": "
|
|
53
|
+
"mdListBullet": "orange",
|
|
53
54
|
"toolDiffAdded": "green",
|
|
54
55
|
"toolDiffRemoved": "red",
|
|
55
56
|
"toolDiffContext": "mediumGray",
|
|
@@ -64,30 +65,30 @@
|
|
|
64
65
|
"syntaxPunctuation": "#000000",
|
|
65
66
|
"thinkingOff": "lightGray",
|
|
66
67
|
"thinkingMinimal": "#767676",
|
|
67
|
-
"thinkingLow": "
|
|
68
|
-
"thinkingMedium": "
|
|
69
|
-
"thinkingHigh": "
|
|
70
|
-
"thinkingXhigh": "#
|
|
71
|
-
"bashMode": "
|
|
72
|
-
"statusLineBg": "#
|
|
68
|
+
"thinkingLow": "orangeDeep",
|
|
69
|
+
"thinkingMedium": "orange",
|
|
70
|
+
"thinkingHigh": "orangeLight",
|
|
71
|
+
"thinkingXhigh": "#FDBA74",
|
|
72
|
+
"bashMode": "orange",
|
|
73
|
+
"statusLineBg": "#f0f0f0",
|
|
73
74
|
"statusLineSep": "#808080",
|
|
74
|
-
"statusLineModel": "#
|
|
75
|
-
"statusLinePath": "#
|
|
75
|
+
"statusLineModel": "#EA580C",
|
|
76
|
+
"statusLinePath": "#C2410C",
|
|
76
77
|
"statusLineGitClean": "#005f00",
|
|
77
78
|
"statusLineGitDirty": "#af5f00",
|
|
78
79
|
"statusLineContext": "#5f5f87",
|
|
79
|
-
"statusLineSpend": "#
|
|
80
|
+
"statusLineSpend": "#EA580C",
|
|
80
81
|
"statusLineStaged": 28,
|
|
81
82
|
"statusLineDirty": 136,
|
|
82
83
|
"statusLineUntracked": 31,
|
|
83
84
|
"statusLineOutput": 133,
|
|
84
85
|
"statusLineCost": 133,
|
|
85
|
-
"statusLineSubagents": "
|
|
86
|
+
"statusLineSubagents": "orange",
|
|
86
87
|
"pythonMode": "yellow"
|
|
87
88
|
},
|
|
88
89
|
"export": {
|
|
89
|
-
"pageBg": "#
|
|
90
|
+
"pageBg": "#faf8f5",
|
|
90
91
|
"cardBg": "#ffffff",
|
|
91
|
-
"infoBg": "#
|
|
92
|
+
"infoBg": "#fff5eb"
|
|
92
93
|
}
|
|
93
94
|
}
|
|
@@ -569,7 +569,7 @@ export class UiHelpers {
|
|
|
569
569
|
theme.bold(theme.fg("warning", "Update Available")) +
|
|
570
570
|
"\n" +
|
|
571
571
|
theme.fg("muted", `New version ${newVersion} is available. Run: `) +
|
|
572
|
-
theme.fg("accent", "
|
|
572
|
+
theme.fg("accent", "snsagent update"),
|
|
573
573
|
1,
|
|
574
574
|
0,
|
|
575
575
|
),
|
|
@@ -214,14 +214,17 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
214
214
|
aliases: ["providers"],
|
|
215
215
|
description: "Open provider setup",
|
|
216
216
|
allowArgs: true,
|
|
217
|
-
subcommands: [
|
|
217
|
+
subcommands: [
|
|
218
|
+
{ name: "providers", description: "Configure sign-in and web search providers" },
|
|
219
|
+
{ name: "byok", description: "Add custom AI provider (Base URL + API Key)" },
|
|
220
|
+
],
|
|
218
221
|
handleTui: async (command, runtime) => {
|
|
219
222
|
const args = command.args.trim().toLowerCase();
|
|
220
|
-
const opensProviders = args === "" || args === "providers";
|
|
223
|
+
const opensProviders = args === "" || args === "providers" || args === "byok";
|
|
221
224
|
if (opensProviders) {
|
|
222
225
|
await runtime.ctx.showProviderSetup();
|
|
223
226
|
} else {
|
|
224
|
-
runtime.ctx.showWarning(`Usage: /${command.name} [providers]`);
|
|
227
|
+
runtime.ctx.showWarning(`Usage: /${command.name} [providers|byok]`);
|
|
225
228
|
}
|
|
226
229
|
runtime.ctx.editor.setText("");
|
|
227
230
|
},
|
package/src/stt/recorder.ts
CHANGED
|
@@ -355,7 +355,7 @@ async function startRecordingWithRecorder(recorder: ResolvedRecorder, outputPath
|
|
|
355
355
|
export async function startRecording(outputPath: string): Promise<RecordingHandle> {
|
|
356
356
|
const recorders = detectRecorders();
|
|
357
357
|
if (recorders.length === 0) {
|
|
358
|
-
throw new Error("No audio recorder available — run `
|
|
358
|
+
throw new Error("No audio recorder available — run `snsagent setup speech`");
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
const failures: string[] = [];
|
|
@@ -372,7 +372,7 @@ export async function startRecording(outputPath: string): Promise<RecordingHandl
|
|
|
372
372
|
});
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
throw new Error(`No audio recorder could start — run \`
|
|
375
|
+
throw new Error(`No audio recorder could start — run \`snsagent setup speech\`.\n${failures.join("\n")}`);
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
/**
|
|
@@ -515,7 +515,7 @@ export async function startStreamingRecording(
|
|
|
515
515
|
): Promise<StreamingRecordingHandle | null> {
|
|
516
516
|
const recorders = detectRecorders();
|
|
517
517
|
if (recorders.length === 0) {
|
|
518
|
-
throw new Error("No audio recorder available — run `
|
|
518
|
+
throw new Error("No audio recorder available — run `snsagent setup speech`");
|
|
519
519
|
}
|
|
520
520
|
const streamingRecorders = recorders.filter(recorder => recorder.tool !== "powershell");
|
|
521
521
|
if (streamingRecorders.length === 0) return null;
|
package/src/tts/player.ts
CHANGED
|
@@ -93,7 +93,7 @@ export async function playAudioFile(filePath: string, options: PlayAudioOptions
|
|
|
93
93
|
if (commands.length === 0) {
|
|
94
94
|
throw new Error(
|
|
95
95
|
"No audio player available. Install PulseAudio (paplay) or ALSA (aplay), " +
|
|
96
|
-
"or run `
|
|
96
|
+
"or run `snsagent setup speech` to download a bundled ffmpeg.",
|
|
97
97
|
);
|
|
98
98
|
}
|
|
99
99
|
|
package/src/tts/tts-client.ts
CHANGED
|
@@ -379,7 +379,7 @@ export class TtsClient {
|
|
|
379
379
|
|
|
380
380
|
/**
|
|
381
381
|
* The TTS subprocess is spawned `unref`'d so an idle worker never blocks
|
|
382
|
-
* process exit. A short-lived CLI command (`
|
|
382
|
+
* process exit. A short-lived CLI command (`snsagent say`) awaiting a request would
|
|
383
383
|
* otherwise let the event loop drain and exit before the audio arrives, so we
|
|
384
384
|
* `ref` the worker exactly while at least one request is pending.
|
|
385
385
|
*/
|
package/src/tui/chat-blocks.ts
CHANGED
|
@@ -16,7 +16,7 @@ const ROLE_LABEL: Record<MessageRole, string> = {
|
|
|
16
16
|
error: "error",
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const BULLET = chalk.
|
|
19
|
+
const BULLET = chalk.hex("#F97316")("●");
|
|
20
20
|
|
|
21
21
|
export interface ChatBlockOptions {
|
|
22
22
|
role: MessageRole;
|
|
@@ -73,7 +73,7 @@ export function renderChatBlock(opts: ChatBlockOptions): string {
|
|
|
73
73
|
const parts: string[] = [header, ...indented];
|
|
74
74
|
|
|
75
75
|
if (opts.streaming) {
|
|
76
|
-
parts.push(` ${chalk.dim("│")} ${chalk.
|
|
76
|
+
parts.push(` ${chalk.dim("│")} ${chalk.hex("#F97316")("…")} ${chalk.dim("thinking")}`);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
return parts.join("\n");
|
|
@@ -99,7 +99,7 @@ export function renderToolBlock(
|
|
|
99
99
|
status: "running" | "done" | "error",
|
|
100
100
|
detail?: string,
|
|
101
101
|
): string {
|
|
102
|
-
const icon = status === "running" ? chalk.
|
|
102
|
+
const icon = status === "running" ? chalk.hex("#F97316")("●") : status === "done" ? chalk.green("●") : chalk.red("●");
|
|
103
103
|
const label = ` ${icon} tool:${toolName} `;
|
|
104
104
|
const detailText = detail ? chalk.dim(` ${detail}`) : "";
|
|
105
105
|
return label + detailText;
|
|
@@ -107,5 +107,5 @@ export function renderToolBlock(
|
|
|
107
107
|
|
|
108
108
|
/** Session header — single line. */
|
|
109
109
|
export function renderSessionHeader(model: string, version: string): string {
|
|
110
|
-
return ` ${chalk.
|
|
110
|
+
return ` ${chalk.hex("#F97316")("SNS")} ${chalk.bold("snsagent")} ${chalk.dim(`v${version}`)} ${chalk.dim("·")} ${chalk.hex("#F97316")(model)}\n`;
|
|
111
111
|
}
|
package/src/tui/chat-ui.ts
CHANGED
|
@@ -98,7 +98,7 @@ const BUILTIN_COMMANDS: SlashCommand[] = [
|
|
|
98
98
|
description: "Show available commands",
|
|
99
99
|
handler: (_args, ctx) => {
|
|
100
100
|
const lines = BUILTIN_COMMANDS.map(
|
|
101
|
-
c => ` ${chalk.
|
|
101
|
+
c => ` ${chalk.hex("#F97316")(c.name.padEnd(14))}${chalk.dim(c.description)}`
|
|
102
102
|
).join("\n");
|
|
103
103
|
ctx.print(renderChatBlock({
|
|
104
104
|
role: "system",
|
|
@@ -176,7 +176,7 @@ export async function runChatSession(config: ChatSessionConfig): Promise<void> {
|
|
|
176
176
|
|
|
177
177
|
// Readline loop
|
|
178
178
|
while (true) {
|
|
179
|
-
const promptStr = chalk.
|
|
179
|
+
const promptStr = chalk.hex("#F97316")("you") + chalk.dim(" › ");
|
|
180
180
|
let line: string;
|
|
181
181
|
try {
|
|
182
182
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
@@ -224,7 +224,7 @@ export async function runChatSession(config: ChatSessionConfig): Promise<void> {
|
|
|
224
224
|
const spinner = ora({
|
|
225
225
|
text: chalk.dim("thinking..."),
|
|
226
226
|
spinner: "dots",
|
|
227
|
-
color: "
|
|
227
|
+
color: "yellow",
|
|
228
228
|
}).start();
|
|
229
229
|
|
|
230
230
|
try {
|
package/src/tui/code-cell.ts
CHANGED
|
@@ -247,15 +247,15 @@ export function renderCollapsibleOutput(
|
|
|
247
247
|
// Collapsed: just header + hint
|
|
248
248
|
const lineCount = content.split("\n").length;
|
|
249
249
|
const hint = chalk.dim(` (${lineCount} lines, click to expand)`);
|
|
250
|
-
lines.push(` ${chalk.
|
|
250
|
+
lines.push(` ${chalk.hex("#F97316")("▸")} ${chalk.bold(label)}${hint}`);
|
|
251
251
|
return lines;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
// Expanded: bordered block
|
|
255
|
-
const accent = chalk.
|
|
255
|
+
const accent = chalk.hex("#F97316");
|
|
256
256
|
|
|
257
257
|
// Expanded: bordered block
|
|
258
|
-
const accentFn = (s: string) => chalk.
|
|
258
|
+
const accentFn = (s: string) => chalk.hex("#F97316")(s);
|
|
259
259
|
|
|
260
260
|
// Header
|
|
261
261
|
const headerText = ` ${label} `;
|
|
@@ -47,7 +47,7 @@ export function renderCommandPalette(opts: CommandPaletteOptions): string {
|
|
|
47
47
|
const lines: string[] = [];
|
|
48
48
|
|
|
49
49
|
// Search line
|
|
50
|
-
lines.push(` ${chalk.
|
|
50
|
+
lines.push(` ${chalk.hex("#F97316")("?")} ${query}${chalk.dim(" (↑↓ navigate · Enter select · Esc cancel)")}`);
|
|
51
51
|
lines.push("");
|
|
52
52
|
|
|
53
53
|
if (filtered.length === 0) {
|
|
@@ -55,8 +55,8 @@ export function renderCommandPalette(opts: CommandPaletteOptions): string {
|
|
|
55
55
|
} else {
|
|
56
56
|
filtered.forEach((cmd, idx) => {
|
|
57
57
|
const isActive = idx === highlighted;
|
|
58
|
-
const prefix = isActive ? chalk.
|
|
59
|
-
const name = isActive ? chalk.
|
|
58
|
+
const prefix = isActive ? chalk.hex("#F97316")("●") : " ";
|
|
59
|
+
const name = isActive ? chalk.hex("#F97316").bold(cmd.name) : chalk.hex("#F97316")(cmd.name);
|
|
60
60
|
const desc = chalk.dim(cmd.description);
|
|
61
61
|
const cat = chalk.dim(` [${cmd.category}]`);
|
|
62
62
|
const shortcut = cmd.shortcut ? chalk.dim(` ${cmd.shortcut}`) : "";
|
package/src/tui/splash.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SNS
|
|
3
|
-
*
|
|
4
|
-
* gradient, no separator boxes. Reads like a status line.
|
|
2
|
+
* SNS Agent splash — clean, minimal, no boxes.
|
|
3
|
+
* Orange accent dot + flat info rows. Zero OMP/Pi Agent visual language.
|
|
5
4
|
*/
|
|
6
5
|
import chalk from "chalk";
|
|
7
6
|
import { readFileSync } from "node:fs";
|
|
@@ -48,31 +47,26 @@ export interface SplashInfo {
|
|
|
48
47
|
nodeVersion?: string;
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
* One-line prefix used throughout the TUI.
|
|
53
|
-
* `●` = present, default text. No rounded boxes.
|
|
54
|
-
*/
|
|
55
|
-
const BULLET = chalk.cyan("●");
|
|
50
|
+
const DOT = chalk.hex("#F97316")("●");
|
|
56
51
|
|
|
57
52
|
export function renderSplash(info: SplashInfo = {}): string {
|
|
58
53
|
const ver = readVersion();
|
|
59
54
|
const lines: string[] = [];
|
|
60
55
|
|
|
61
|
-
// Brand
|
|
62
|
-
lines.push(` ${
|
|
63
|
-
lines.push(` ${chalk.dim("coding agent CLI")}`);
|
|
56
|
+
// Brand — orange dot + bold name + dim version
|
|
57
|
+
lines.push(` ${DOT} ${chalk.bold("SNS")} ${chalk.dim(`v${ver}`)}`);
|
|
64
58
|
lines.push("");
|
|
65
59
|
|
|
66
|
-
// Status
|
|
67
|
-
const row = (label: string, value: string) =>
|
|
60
|
+
// Status rows
|
|
61
|
+
const row = (label: string, value: string) =>
|
|
62
|
+
` ${chalk.dim("·")} ${chalk.dim(label.padEnd(12))}${value}`;
|
|
68
63
|
if (info.model) lines.push(row("model", `${info.provider ?? "unknown"}/${info.model}`));
|
|
69
64
|
if (info.cwd) lines.push(row("dir", info.cwd));
|
|
70
65
|
if (info.platform) lines.push(row("platform", info.platform));
|
|
71
|
-
lines.push(row("version", ver));
|
|
72
66
|
|
|
73
|
-
//
|
|
67
|
+
// Footer
|
|
74
68
|
lines.push("");
|
|
75
|
-
lines.push(` ${chalk.dim("
|
|
69
|
+
lines.push(` ${chalk.dim("chat to configure · /help for commands")}`);
|
|
76
70
|
|
|
77
71
|
return lines.join("\n") + "\n";
|
|
78
72
|
}
|
|
@@ -80,7 +74,7 @@ export function renderSplash(info: SplashInfo = {}): string {
|
|
|
80
74
|
export function renderInlineHeader(info: SplashInfo = {}): string {
|
|
81
75
|
const ver = readVersion();
|
|
82
76
|
const model = info.model
|
|
83
|
-
? chalk.
|
|
77
|
+
? chalk.hex("#F97316")(`${info.provider ?? "?"}/${info.model}`)
|
|
84
78
|
: chalk.dim("no model");
|
|
85
|
-
return ` ${
|
|
79
|
+
return ` ${DOT} ${chalk.bold("SNS")} ${chalk.dim(`v${ver}`)} ${chalk.dim("·")} ${model}\n`;
|
|
86
80
|
}
|