@cruxy/cli 0.11.0 → 0.13.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 (71) hide show
  1. package/dist/approval/prompt.js +17 -15
  2. package/dist/cli/commands/checkpoint.js +6 -4
  3. package/dist/cli/commands/config.js +10 -7
  4. package/dist/cli/commands/index.js +16 -15
  5. package/dist/cli/commands/init.js +5 -3
  6. package/dist/cli/commands/login.js +5 -3
  7. package/dist/cli/commands/pr.js +8 -7
  8. package/dist/cli/commands/rollback.js +7 -6
  9. package/dist/cli/commands/run.js +26 -7
  10. package/dist/cli/commands/skills.js +12 -10
  11. package/dist/cli/program.js +7 -6
  12. package/dist/cli/repl.js +11 -9
  13. package/dist/cli/session-factory.d.ts +2 -1
  14. package/dist/cli/session-factory.js +10 -3
  15. package/dist/components/frame.js +3 -1
  16. package/dist/components/fuzzy.d.ts +4 -4
  17. package/dist/components/fuzzy.js +14 -13
  18. package/dist/components/select.js +8 -7
  19. package/dist/config/schema.d.ts +123 -0
  20. package/dist/config/schema.js +40 -0
  21. package/dist/errors/constructors.d.ts +21 -0
  22. package/dist/errors/constructors.js +58 -0
  23. package/dist/errors/format.js +8 -8
  24. package/dist/errors/types.d.ts +5 -0
  25. package/dist/errors/types.js +11 -0
  26. package/dist/onboarding/flow.js +6 -6
  27. package/dist/onboarding/steps.js +11 -11
  28. package/dist/plan/approve.js +6 -6
  29. package/dist/plan/render.js +26 -18
  30. package/dist/render/capabilities.js +4 -0
  31. package/dist/render/diff.d.ts +6 -7
  32. package/dist/render/diff.js +33 -22
  33. package/dist/render/highlight.d.ts +3 -3
  34. package/dist/render/highlight.js +15 -15
  35. package/dist/render/index.d.ts +1 -1
  36. package/dist/render/plain-renderer.d.ts +2 -1
  37. package/dist/render/plain-renderer.js +7 -6
  38. package/dist/render/state.d.ts +7 -2
  39. package/dist/render/state.js +16 -10
  40. package/dist/render/tty-renderer.d.ts +2 -1
  41. package/dist/render/tty-renderer.js +20 -17
  42. package/dist/render/types.d.ts +7 -0
  43. package/dist/sandbox/detect.d.ts +22 -0
  44. package/dist/sandbox/detect.js +67 -0
  45. package/dist/sandbox/docker-runtime.d.ts +32 -0
  46. package/dist/sandbox/docker-runtime.js +263 -0
  47. package/dist/sandbox/index.d.ts +7 -0
  48. package/dist/sandbox/index.js +5 -0
  49. package/dist/sandbox/policy.d.ts +17 -0
  50. package/dist/sandbox/policy.js +90 -0
  51. package/dist/sandbox/service.d.ts +57 -0
  52. package/dist/sandbox/service.js +64 -0
  53. package/dist/sandbox/types.d.ts +114 -0
  54. package/dist/sandbox/types.js +17 -0
  55. package/dist/subagent/orchestrator.d.ts +7 -0
  56. package/dist/subagent/orchestrator.js +22 -6
  57. package/dist/testing/run-tests-tool.d.ts +5 -1
  58. package/dist/testing/run-tests-tool.js +8 -1
  59. package/dist/testing/sandbox-runner.d.ts +16 -0
  60. package/dist/testing/sandbox-runner.js +47 -0
  61. package/dist/theme/index.d.ts +2 -0
  62. package/dist/theme/index.js +2 -0
  63. package/dist/theme/resolve.d.ts +32 -0
  64. package/dist/theme/resolve.js +73 -0
  65. package/dist/theme/tokens.d.ts +104 -0
  66. package/dist/theme/tokens.js +52 -0
  67. package/dist/tools/shell/run-command.js +35 -1
  68. package/dist/tools/types.d.ts +10 -0
  69. package/dist/utils/logger.d.ts +2 -0
  70. package/dist/utils/logger.js +7 -4
  71. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The terminal design system (U.1): one typed {@link Theme} of semantic tokens
3
+ * — color *roles* (never raw hues at call sites), a glyph set with ASCII
4
+ * fallbacks, and structure primitives. Every rendered surface resolves ONE
5
+ * theme (from {@link RenderCapabilities}) and references roles like
6
+ * `theme.danger(x)` / `theme.glyph.success`, so the CLI reads as one product
7
+ * and NO_COLOR / dumb-terminal degradation is handled in exactly one place.
8
+ *
9
+ * Picocolors is imported only by `resolve.ts`; this file is pure types + the
10
+ * two glyph tables.
11
+ */
12
+ /** A text styler: wraps a string in ANSI, or the identity under NO_COLOR. */
13
+ export type Styler = (text: string) => string;
14
+ /**
15
+ * Language-token colors for the streaming syntax highlighter. A distinct
16
+ * sub-palette (not status semantics), centralized here so every color choice
17
+ * lives in the theme.
18
+ */
19
+ export interface ThemeSyntax {
20
+ keyword: Styler;
21
+ string: Styler;
22
+ number: Styler;
23
+ comment: Styler;
24
+ }
25
+ /**
26
+ * The glyph vocabulary. Concrete strings (resolved to the unicode or ASCII
27
+ * table), so a call site writes `theme.glyph.success`, never a literal "✓".
28
+ */
29
+ export interface ThemeGlyphs {
30
+ /** Success / done. */
31
+ success: string;
32
+ /** Failure / error. */
33
+ failure: string;
34
+ /** Not-yet-started (plan step). */
35
+ pending: string;
36
+ /** In-progress marker (static, non-animated). */
37
+ running: string;
38
+ /** Selected-row pointer in pickers. */
39
+ pointer: string;
40
+ /** Input caret (REPL prompt, fuzzy query). */
41
+ caret: string;
42
+ /** Next-step / relation arrow. */
43
+ arrow: string;
44
+ caretUp: string;
45
+ caretDown: string;
46
+ /** Text-cursor bar in the fuzzy query line. */
47
+ cursorBar: string;
48
+ bullet: string;
49
+ /** Inline separator (the ` · ` joiner uses this). */
50
+ sep: string;
51
+ /** Truncation marker. */
52
+ ellipsis: string;
53
+ /** Subagent activity marker. */
54
+ play: string;
55
+ /** Animated spinner frames (used only where cursor control exists). */
56
+ spinnerFrames: readonly string[];
57
+ /** Non-animated spinner glyph (no-cursor terminals). */
58
+ spinnerStatic: string;
59
+ }
60
+ /**
61
+ * The resolved design system handed to a surface. Color roles are stylers
62
+ * (identity under NO_COLOR); `glyph`/`sep` are concrete strings; the structure
63
+ * helpers give consistent indentation, headings, and key/value alignment.
64
+ */
65
+ export interface Theme {
66
+ /** Errors, destructive risk, failure. (red) */
67
+ danger: Styler;
68
+ /** Reversible-mutation risk, warnings. (yellow) */
69
+ warning: Styler;
70
+ /** Done / ok. (green) */
71
+ success: Styler;
72
+ /** Brand / interactive / links / pointers. (cyan) */
73
+ accent: Styler;
74
+ /** Secondary text: hints, causes, codes, rationale. (dim) */
75
+ muted: Styler;
76
+ /** Emphasis / headings — a weight, hue-independent; composes with a color. (bold) */
77
+ strong: Styler;
78
+ /** Syntax-highlighting sub-palette. */
79
+ syntax: ThemeSyntax;
80
+ /** Glyph vocabulary (unicode or ASCII per capabilities). */
81
+ glyph: ThemeGlyphs;
82
+ /** Bold heading (alias of {@link strong}, named for intent). */
83
+ heading: Styler;
84
+ /** Prefix every line with `2 * level` spaces. */
85
+ indent(text: string, level?: number): string;
86
+ /** Aligned `key value` with a bold key (optionally padded to `keyWidth`). */
87
+ kv(key: string, value: string, keyWidth?: number): string;
88
+ /** The inline joiner, e.g. ` · ` (unicode) / ` - ` (ascii). */
89
+ sep: string;
90
+ readonly color: boolean;
91
+ readonly unicode: boolean;
92
+ }
93
+ /** The two axes a theme is resolved from — a structural subset of RenderCapabilities. */
94
+ export interface ThemeCapabilities {
95
+ color: boolean;
96
+ unicode: boolean;
97
+ }
98
+ /** The unicode glyph table (real terminals). */
99
+ export declare const UNICODE_GLYPHS: ThemeGlyphs;
100
+ /**
101
+ * The ASCII glyph table (TERM=dumb / CRUXY_ASCII). Readable, single-byte, no
102
+ * mojibake — the intentional U.1 degradation for unicode-unsafe terminals.
103
+ */
104
+ export declare const ASCII_GLYPHS: ThemeGlyphs;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The terminal design system (U.1): one typed {@link Theme} of semantic tokens
3
+ * — color *roles* (never raw hues at call sites), a glyph set with ASCII
4
+ * fallbacks, and structure primitives. Every rendered surface resolves ONE
5
+ * theme (from {@link RenderCapabilities}) and references roles like
6
+ * `theme.danger(x)` / `theme.glyph.success`, so the CLI reads as one product
7
+ * and NO_COLOR / dumb-terminal degradation is handled in exactly one place.
8
+ *
9
+ * Picocolors is imported only by `resolve.ts`; this file is pure types + the
10
+ * two glyph tables.
11
+ */
12
+ /** The unicode glyph table (real terminals). */
13
+ export const UNICODE_GLYPHS = {
14
+ success: "✓",
15
+ failure: "✗",
16
+ pending: "○",
17
+ running: "◐",
18
+ pointer: "❯",
19
+ caret: "›",
20
+ arrow: "→",
21
+ caretUp: "↑",
22
+ caretDown: "↓",
23
+ cursorBar: "▏",
24
+ bullet: "•",
25
+ sep: "·",
26
+ ellipsis: "…",
27
+ play: "⏵",
28
+ spinnerFrames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
29
+ spinnerStatic: "◐",
30
+ };
31
+ /**
32
+ * The ASCII glyph table (TERM=dumb / CRUXY_ASCII). Readable, single-byte, no
33
+ * mojibake — the intentional U.1 degradation for unicode-unsafe terminals.
34
+ */
35
+ export const ASCII_GLYPHS = {
36
+ success: "[ok]",
37
+ failure: "[x]",
38
+ pending: "[ ]",
39
+ running: "~",
40
+ pointer: ">",
41
+ caret: ">",
42
+ arrow: "->",
43
+ caretUp: "^",
44
+ caretDown: "v",
45
+ cursorBar: "|",
46
+ bullet: "*",
47
+ sep: "-",
48
+ ellipsis: "...",
49
+ play: ">",
50
+ spinnerFrames: ["-", "\\", "|", "/"],
51
+ spinnerStatic: "~",
52
+ };
@@ -26,9 +26,43 @@ export const runCommandTool = {
26
26
  error: decision.feedback ?? "command denied by the user",
27
27
  };
28
28
  }
29
- return runBounded(input.command, ctx);
29
+ // Substrate is chosen SOLELY by ctx.sandbox: present → run in the box (C.16),
30
+ // never on the host; absent → host, unchanged. There is no fallback path — a
31
+ // sandbox that can't run throws a coded error (see runSandboxed) rather than
32
+ // silently reaching runBounded.
33
+ return ctx.sandbox
34
+ ? runSandboxed(input.command, ctx)
35
+ : runBounded(input.command, ctx);
30
36
  },
31
37
  };
38
+ /**
39
+ * Run inside the sandbox and map the neutral ExecResult onto the IDENTICAL
40
+ * ToolResult the host path produces — same "exit code N" framing, same
41
+ * truncation note, same timeout message — so the tool is substrate-agnostic.
42
+ * A container-start / image failure throws a coded CruxyError from
43
+ * `sandbox.exec` and propagates; we deliberately do not catch it (fail loud).
44
+ */
45
+ function runSandboxed(command, ctx) {
46
+ const { timeoutMs, maxOutputBytes } = ctx.config.shell;
47
+ return ctx
48
+ .sandbox.exec(command, {
49
+ cwd: ctx.cwd,
50
+ timeoutMs,
51
+ maxOutputBytes,
52
+ capture: "head",
53
+ })
54
+ .then((result) => {
55
+ if (result.timedOut) {
56
+ return { ok: false, error: `timed out after ${timeoutMs}ms` };
57
+ }
58
+ const exit = result.exitCode ?? "unknown";
59
+ let output = `exit code ${exit}\n${result.output}`;
60
+ if (result.outputTruncated) {
61
+ output += `\n… [output truncated at ${maxOutputBytes} bytes]`;
62
+ }
63
+ return { ok: true, output };
64
+ });
65
+ }
32
66
  /** Spawn the command, capture bounded output, and enforce the timeout. */
33
67
  function runBounded(command, ctx) {
34
68
  const { timeoutMs, maxOutputBytes } = ctx.config.shell;
@@ -1,6 +1,7 @@
1
1
  import type { z, ZodTypeAny } from "zod";
2
2
  import type { CruxyConfig } from "../config/index.js";
3
3
  import type { ApprovalDecision } from "../approval/types.js";
4
+ import type { SandboxService } from "../sandbox/index.js";
4
5
  import type { logger } from "../utils/logger.js";
5
6
  /** The leveled logger instance shared across the CLI. */
6
7
  type Logger = typeof logger;
@@ -131,6 +132,15 @@ export interface ToolContext {
131
132
  * tools never call this.
132
133
  */
133
134
  requestApproval(action: ApproveAction): Promise<ApprovalDecision>;
135
+ /**
136
+ * Isolation substrate for the shell + test tools (C.16). Present ONLY when
137
+ * the sandbox is enabled; when set, `run_command`/`run_tests` execute the
138
+ * approved command inside the container and NEVER on the host. Its mere
139
+ * presence is the switch — there is no host fallback once it is set (an
140
+ * unavailable runtime fails loud at construction, before this is ever
141
+ * populated). Absent → host execution, unchanged.
142
+ */
143
+ sandbox?: SandboxService;
134
144
  }
135
145
  /**
136
146
  * The one interface every tool implements. `parameters` is a zod schema; it both
@@ -2,6 +2,8 @@ export declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error", "si
2
2
  export type LogLevel = (typeof LOG_LEVELS)[number];
3
3
  declare class Logger {
4
4
  private level;
5
+ /** Diagnostics go to stderr, so the theme resolves against stderr's color. */
6
+ private readonly theme;
5
7
  setLevel(level: LogLevel): void;
6
8
  getLevel(): LogLevel;
7
9
  private enabled;
@@ -1,4 +1,5 @@
1
- import pc from "picocolors";
1
+ import { shouldUseColor } from "../errors/format.js";
2
+ import { themeForColor } from "../theme/index.js";
2
3
  export const LOG_LEVELS = ["debug", "info", "warn", "error", "silent"];
3
4
  const WEIGHT = {
4
5
  debug: 10,
@@ -9,6 +10,8 @@ const WEIGHT = {
9
10
  };
10
11
  class Logger {
11
12
  level = "info";
13
+ /** Diagnostics go to stderr, so the theme resolves against stderr's color. */
14
+ theme = themeForColor(shouldUseColor(process.stderr));
12
15
  setLevel(level) {
13
16
  this.level = level;
14
17
  }
@@ -20,7 +23,7 @@ class Logger {
20
23
  }
21
24
  debug(...args) {
22
25
  if (this.enabled("debug"))
23
- console.error(pc.dim("debug"), ...args);
26
+ console.error(this.theme.muted("debug"), ...args);
24
27
  }
25
28
  info(...args) {
26
29
  if (this.enabled("info"))
@@ -28,11 +31,11 @@ class Logger {
28
31
  }
29
32
  warn(...args) {
30
33
  if (this.enabled("warn"))
31
- console.error(pc.yellow("warn"), ...args);
34
+ console.error(this.theme.warning("warn"), ...args);
32
35
  }
33
36
  error(...args) {
34
37
  if (this.enabled("error"))
35
- console.error(pc.red("error"), ...args);
38
+ console.error(this.theme.danger("error"), ...args);
36
39
  }
37
40
  /** Primary user-facing output — always written to stdout. */
38
41
  print(...args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxy/cli",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "an agentic coding CLI",
5
5
  "type": "module",
6
6
  "bin": {