@sns-myagent/cli 0.2.0 → 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.
Files changed (61) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +7 -8
  3. package/package.json +3 -3
  4. package/scripts/apply-pi-natives-patch.js +82 -56
  5. package/src/adapters/telegram/bot.ts +41 -1
  6. package/src/adapters/telegram/bridge.ts +186 -0
  7. package/src/adapters/telegram/handler.ts +138 -13
  8. package/src/adapters/telegram/index.ts +12 -2
  9. package/src/agents/__tests__/config.test.ts +79 -0
  10. package/src/agents/__tests__/resilience.test.ts +119 -0
  11. package/src/agents/__tests__/strategies.test.ts +83 -0
  12. package/src/agents/config.ts +367 -0
  13. package/src/agents/ensemble.ts +224 -0
  14. package/src/agents/resilience.ts +332 -0
  15. package/src/agents/strategies/best-of-n.ts +108 -0
  16. package/src/agents/strategies/consensus.ts +105 -0
  17. package/src/agents/strategies/critic.ts +131 -0
  18. package/src/agents/strategies/types.ts +68 -0
  19. package/src/async/__tests__/task-runner.test.ts +162 -0
  20. package/src/async/__tests__/task-store.test.ts +146 -0
  21. package/src/async/index.ts +28 -1
  22. package/src/async/notifier.ts +133 -0
  23. package/src/async/task-runner.ts +175 -0
  24. package/src/async/task-store.ts +170 -0
  25. package/src/async/types.ts +70 -0
  26. package/src/cli/entry.ts +3 -1
  27. package/src/cli/index.ts +69 -54
  28. package/src/config/index.ts +1 -1
  29. package/src/debug/index.ts +1 -1
  30. package/src/modes/components/welcome.ts +13 -6
  31. package/src/modes/controllers/event-controller.ts +1 -1
  32. package/src/modes/setup-wizard/scenes/splash.ts +1 -1
  33. package/src/session/agent-session.ts +1 -1
  34. package/src/slash-commands/builtin-registry.ts +63 -0
  35. package/src/slash-commands/helpers/task.ts +181 -0
  36. package/src/tbm/__tests__/tbm.test.ts +660 -0
  37. package/src/tbm/comm-modes.ts +165 -0
  38. package/src/tbm/config.ts +136 -0
  39. package/src/tbm/context-delta.ts +146 -0
  40. package/src/tbm/context-pyramid.ts +202 -0
  41. package/src/tbm/dashboard.ts +131 -0
  42. package/src/tbm/index.ts +247 -0
  43. package/src/tbm/lazy-skills.ts +182 -0
  44. package/src/tbm/response-cache.ts +220 -0
  45. package/src/tbm/tombstone.ts +189 -0
  46. package/src/tbm/tool-compress.ts +230 -0
  47. package/src/tools/ask.ts +1 -1
  48. package/src/tui/chat-blocks.ts +205 -0
  49. package/src/tui/chat-ui.ts +270 -0
  50. package/src/tui/code-cell.ts +90 -1
  51. package/src/tui/command-palette.ts +189 -0
  52. package/src/tui/index.ts +4 -0
  53. package/src/tui/splash.ts +130 -0
  54. package/src/ui/banner.ts +69 -29
  55. package/src/ui/colors.ts +24 -0
  56. package/src/ui/error-display.ts +130 -0
  57. package/src/ui/gradient.ts +104 -0
  58. package/src/ui/index.ts +15 -0
  59. package/src/ui/memory-toast.ts +102 -0
  60. package/src/ui/status-bar.ts +36 -30
  61. package/bin/snscoder.js +0 -98
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Standalone gradient utility for SNS-MyAgent terminal UI.
3
+ *
4
+ * Provides reusable gradient renderers for brand colors.
5
+ * Single source of truth for all gradient-based decorations.
6
+ */
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
+
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
+ }
44
+
45
+ /**
46
+ * Create a gradient border line spanning the given width.
47
+ */
48
+ export function gradientLine(width: number): string {
49
+ const line = "─".repeat(width);
50
+ return brandGradient(line);
51
+ }
52
+
53
+ /**
54
+ * Create a gradient border with label in the center.
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
+ }
63
+
64
+ /**
65
+ * Create a role-specific gradient for a message role.
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
+ }
95
+
96
+ // ── Internal helpers ──
97
+
98
+ function adjustBrightness(hex: string, amount: number): string {
99
+ const num = parseInt(hex.replace("#", ""), 16);
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
+ }
package/src/ui/index.ts CHANGED
@@ -4,3 +4,18 @@ export { showBanner } from "./banner.js";
4
4
  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
+ export {
8
+ brandGradient,
9
+ accentGradient,
10
+ subtleGradient,
11
+ brand,
12
+ accentGrad,
13
+ subtle,
14
+ gradientLine,
15
+ labeledGradientLine,
16
+ roleGradient,
17
+ statusGradient,
18
+ gradientBg,
19
+ } from "./gradient.js";
20
+ export { renderErrorDisplay, renderQuickError, renderWarning, type ErrorDisplayOptions, type ErrorSeverity } from "./error-display.js";
21
+ export { renderMemoryToast, renderMemoryRecall, renderMemorySave, clearToastLine, type MemoryToastOptions, type ToastType } from "./memory-toast.js";
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Memory toast notification component for SNS-MyAgent terminal UI.
3
+ *
4
+ * Shows brief, non-intrusive memory recall/save notifications.
5
+ * Auto-dismiss style — fades after a short duration.
6
+ */
7
+ import chalk from "chalk";
8
+ import gradient from "gradient-string";
9
+ import { BRAND_GRADIENT } from "./colors.js";
10
+
11
+ export type ToastType = "recall" | "save" | "forget" | "info";
12
+
13
+ export interface MemoryToastOptions {
14
+ /** Type of memory operation. */
15
+ type: ToastType;
16
+ /** Short description of what happened. */
17
+ message: string;
18
+ /** Memory ID (optional, shown as dim). */
19
+ memoryId?: string;
20
+ /** Relevance score (0-1). */
21
+ relevance?: number;
22
+ /** Duration in ms before auto-dismiss (default: 3000). */
23
+ duration?: number;
24
+ }
25
+
26
+ const TOAST_CONFIG = {
27
+ recall: { icon: "🧠", label: "MEMORY", gradient: ["#00d2ff", "#7b2ff7"] as [string, string] },
28
+ save: { icon: "💾", label: "SAVED", gradient: ["#00ff88", "#00d2ff"] as [string, string] },
29
+ forget: { icon: "🗑", label: "FORGOT", gradient: ["#ff8c00", "#ff4444"] as [string, string] },
30
+ info: { icon: "ℹ", label: "INFO", gradient: ["#7b2ff7", "#ff6b9d"] as [string, string] },
31
+ } as const;
32
+
33
+ /**
34
+ * Render a memory toast notification line.
35
+ * Returns a string that can be printed and then cleared.
36
+ */
37
+ export function renderMemoryToast(opts: MemoryToastOptions): string {
38
+ const config = TOAST_CONFIG[opts.type];
39
+ const grad = gradient(config.gradient);
40
+
41
+ const icon = config.icon;
42
+ const label = grad(chalk.bold(` ${config.label} `));
43
+ const msg = chalk.white(opts.message);
44
+
45
+ const parts = [icon, label, msg];
46
+
47
+ if (opts.memoryId) {
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(" ")}`;
58
+ }
59
+
60
+ /**
61
+ * Render a memory recall toast with context snippet.
62
+ */
63
+ export function renderMemoryRecall(
64
+ query: string,
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
+ : "";
75
+
76
+ const lines = [
77
+ `${icon} ${label} ${queryStr}${scoreStr}`,
78
+ ` ${chalk.dim(snippet.slice(0, 120))}${snippet.length > 120 ? chalk.dim("...") : ""}`,
79
+ ];
80
+
81
+ return lines.join("\n");
82
+ }
83
+
84
+ /**
85
+ * Render a memory save confirmation toast.
86
+ */
87
+ export function renderMemorySave(content: string, scope?: string): string {
88
+ const grad = gradient(["#00ff88", "#00d2ff"]);
89
+ const icon = "💾";
90
+ const label = grad(chalk.bold(" SAVED "));
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)}`;
95
+ }
96
+
97
+ /**
98
+ * Clear toast line (ANSI escape to clear current line).
99
+ */
100
+ export function clearToastLine(): void {
101
+ process.stdout.write("\x1b[2K\r");
102
+ }
@@ -3,29 +3,32 @@
3
3
  *
4
4
  * Renders a bottom-line summary: model, tokens, session time, memory hits.
5
5
  * Uses ANSI to draw a single-line bar at the bottom of the terminal.
6
+ * Premium gradient styling.
6
7
  */
7
8
 
8
- import { accent, muted, primary, success } from "./colors.js";
9
+ import chalk from "chalk";
10
+ import gradient from "gradient-string";
11
+ import { BRAND_GRADIENT } from "./colors.js";
9
12
 
10
13
  /** Runtime state passed into the status bar renderer. */
11
14
  export interface StatusBarState {
12
- /** Active model name. */
13
- model: string;
14
- /** Total tokens used in the current session. */
15
- tokensUsed: number;
16
- /** Session start timestamp (ms since epoch). */
17
- sessionStarted: number;
18
- /** Number of memory/context hits. */
19
- memoryHits: number;
15
+ /** Active model name. */
16
+ model: string;
17
+ /** Total tokens used in the current session. */
18
+ tokensUsed: number;
19
+ /** Session start timestamp (ms since epoch). */
20
+ sessionStarted: number;
21
+ /** Number of memory/context hits. */
22
+ memoryHits: number;
20
23
  }
21
24
 
22
25
  function formatDuration(ms: number): string {
23
- const secs = Math.floor(ms / 1000);
24
- const mins = Math.floor(secs / 60);
25
- const hrs = Math.floor(mins / 60);
26
- if (hrs > 0) return `${hrs}h${mins % 60}m`;
27
- if (mins > 0) return `${mins}m${secs % 60}s`;
28
- return `${secs}s`;
26
+ const secs = Math.floor(ms / 1000);
27
+ const mins = Math.floor(secs / 60);
28
+ const hrs = Math.floor(mins / 60);
29
+ if (hrs > 0) return `${hrs}h${mins % 60}m`;
30
+ if (mins > 0) return `${mins}m${secs % 60}s`;
31
+ return `${secs}s`;
29
32
  }
30
33
 
31
34
  /**
@@ -33,25 +36,28 @@ function formatDuration(ms: number): string {
33
36
  * Overwrites the current line using `\r` + ANSI clear-line.
34
37
  */
35
38
  export function renderStatusBar(state: StatusBarState): void {
36
- const elapsed = formatDuration(Date.now() - state.sessionStarted);
37
- const tokens = state.tokensUsed > 1000
38
- ? `${(state.tokensUsed / 1000).toFixed(1)}k`
39
- : String(state.tokensUsed);
40
-
41
- const segments = [
42
- `${muted("model")} ${primary(state.model)}`,
43
- `${muted("tokens")} ${accent(tokens)}`,
44
- `${muted("time")} ${accent(elapsed)}`,
45
- `${muted("memory")} ${success(String(state.memoryHits))}`,
46
- ];
47
-
48
- const bar = ` ${segments.join(muted(" │ "))} `;
49
- process.stdout.write(`\x1b[2K\r${bar}`);
39
+ const elapsed = formatDuration(Date.now() - state.sessionStarted);
40
+ const tokens = state.tokensUsed > 1000
41
+ ? `${(state.tokensUsed / 1000).toFixed(1)}k`
42
+ : String(state.tokensUsed);
43
+
44
+ const grad = gradient(BRAND_GRADIENT);
45
+
46
+ const segments = [
47
+ `${chalk.dim("model")} ${chalk.cyan(state.model)}`,
48
+ `${chalk.dim("tokens")} ${grad(tokens)}`,
49
+ `${chalk.dim("time")} ${chalk.magenta(elapsed)}`,
50
+ `${chalk.dim("mem")} ${chalk.green(String(state.memoryHits))}`,
51
+ ];
52
+
53
+ const sep = chalk.dim(" │ ");
54
+ const bar = ` ${segments.join(sep)} `;
55
+ process.stdout.write(`\x1b[2K\r${bar}`);
50
56
  }
51
57
 
52
58
  /**
53
59
  * Clear the status bar line (call before printing normal output).
54
60
  */
55
61
  export function clearStatusBar(): void {
56
- process.stdout.write("\x1b[2K\r");
62
+ process.stdout.write("\x1b[2K\r");
57
63
  }
package/bin/snscoder.js DELETED
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env node
2
- // bin/snscoder.js — npm-installed shim that locates and spawns the
3
- // platform-specific prebuilt binary downloaded by scripts/fetch-binary.mjs
4
- // (or built locally via `bun scripts/build-binary.ts`).
5
- //
6
- // Runs on plain Node 18+. Uses spawnSync so npm postinstall script chaining
7
- // behaves predictably and exit codes propagate correctly.
8
-
9
- import { existsSync, statSync } from "node:fs";
10
- import { spawnSync } from "node:child_process";
11
- import { dirname, join, resolve } from "node:path";
12
- import { fileURLToPath } from "node:url";
13
-
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = dirname(__filename);
16
- const REPO_ROOT = resolve(__dirname, "..");
17
-
18
- const platform = process.platform;
19
- const arch = process.arch;
20
- const isWin = platform === "win32";
21
-
22
- const binaryName = isWin ? "snscoder.exe" : "snscoder";
23
- const binaryPath = join(REPO_ROOT, "bin", binaryName);
24
- const altBinaryPath = join(REPO_ROOT, "bin", `${binaryName}-${platform}-${arch}${isWin ? ".exe" : ""}`);
25
-
26
- // Local dev fallback: if a sibling dist/omp or dist/cli.js exists (source build),
27
- // prefer invoking the Node entry so contributors without a fetched binary still work.
28
- const devEntryCandidates = [
29
- join(REPO_ROOT, "dist", "omp"), // compiled Bun binary from `bun scripts/build-binary.ts`
30
- join(REPO_ROOT, "dist", "cli.js"), // legacy Node entry
31
- ];
32
-
33
- function missingHelp() {
34
- const lines = [
35
- "",
36
- `\u001b[31m✗ snscoder binary not found at ${binaryPath}\u001b[0m`,
37
- "",
38
- " SNS-MyAgent ships a platform-specific prebuilt binary that is downloaded",
39
- " on `npm install` (or fetched explicitly via `npm run fetch-binary`). The",
40
- " binary appears to be missing from this install.",
41
- "",
42
- " \u001b[1mFix — pick one:\u001b[0m",
43
- " 1. Re-run the npm postinstall step:",
44
- " npm rebuild # re-runs postinstall for current pkg",
45
- " npm install -g @sns-myagent/cli --force # full reinstall",
46
- " 2. Use the recommended installer (handles Bun + binary end-to-end):",
47
- " curl -fsSL https://sns.myagent.id/install.sh | bash",
48
- " 3. Windows PowerShell:",
49
- " irm raw.githubusercontent.com/Reihantt6/sns-myagent/main/install.ps1 | iex",
50
- " 4. Build from source (requires Bun >= 1.3.14):",
51
- " git clone https://github.com/Reihantt6/sns-myagent",
52
- " cd sns-myagent && bun install && bun run build",
53
- "",
54
- " If a release hasn't been published yet, you will see a warning from",
55
- " scripts/fetch-binary.mjs during install — that's expected and not a bug.",
56
- "",
57
- ];
58
- process.stderr.write(lines.join("\n"));
59
- }
60
-
61
- function pickExecutable() {
62
- if (existsSync(binaryPath) && statSync(binaryPath).isFile()) {
63
- return { path: binaryPath, args: [] };
64
- }
65
- if (existsSync(altBinaryPath) && statSync(altBinaryPath).isFile()) {
66
- return { path: altBinaryPath, args: [] };
67
- }
68
- // Local dev fallback: bun build artifact or legacy Node entry.
69
- for (const candidate of devEntryCandidates) {
70
- if (existsSync(candidate) && statSync(candidate).isFile()) {
71
- const isNodeEntry = candidate.endsWith(".js");
72
- return {
73
- path: isNodeEntry ? process.execPath : candidate,
74
- args: isNodeEntry ? [candidate] : [],
75
- };
76
- }
77
- }
78
- return null;
79
- }
80
-
81
- const exe = pickExecutable();
82
- if (!exe) {
83
- missingHelp();
84
- process.exit(1);
85
- }
86
-
87
- const result = spawnSync(exe.path, [...exe.args, ...process.argv.slice(2)], {
88
- stdio: "inherit",
89
- // Forward env so callers can still pass API keys etc. through npm-global env.
90
- env: process.env,
91
- });
92
-
93
- // Propagate signal-based termination (e.g. SIGINT from Ctrl-C) rather than
94
- // reporting exit code 1.
95
- if (result.signal) {
96
- process.exit(1);
97
- }
98
- process.exit(result.status ?? 1);