kly 0.1.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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/dist/ai/context.mjs +79 -0
  4. package/dist/ai/context.mjs.map +1 -0
  5. package/dist/ai/storage.mjs +50 -0
  6. package/dist/ai/storage.mjs.map +1 -0
  7. package/dist/bin/kly.d.mts +1 -0
  8. package/dist/bin/kly.mjs +2888 -0
  9. package/dist/bin/kly.mjs.map +1 -0
  10. package/dist/bin/launcher-vTpgdO9n.mjs +3 -0
  11. package/dist/bin/permissions-2r_7ZqaH.mjs +3 -0
  12. package/dist/cli.mjs +229 -0
  13. package/dist/cli.mjs.map +1 -0
  14. package/dist/define-app.d.mts +33 -0
  15. package/dist/define-app.d.mts.map +1 -0
  16. package/dist/define-app.mjs +183 -0
  17. package/dist/define-app.mjs.map +1 -0
  18. package/dist/index.d.mts +16 -0
  19. package/dist/index.mjs +15 -0
  20. package/dist/mcp/index.mjs +4 -0
  21. package/dist/mcp/schema-converter.d.mts +13 -0
  22. package/dist/mcp/schema-converter.d.mts.map +1 -0
  23. package/dist/mcp/schema-converter.mjs +30 -0
  24. package/dist/mcp/schema-converter.mjs.map +1 -0
  25. package/dist/mcp/server.d.mts +33 -0
  26. package/dist/mcp/server.d.mts.map +1 -0
  27. package/dist/mcp/server.mjs +92 -0
  28. package/dist/mcp/server.mjs.map +1 -0
  29. package/dist/permissions/index.mjs +123 -0
  30. package/dist/permissions/index.mjs.map +1 -0
  31. package/dist/sandbox/bundled-executor.d.mts +17 -0
  32. package/dist/sandbox/bundled-executor.d.mts.map +1 -0
  33. package/dist/sandbox/bundled-executor.mjs +175 -0
  34. package/dist/sandbox/bundled-executor.mjs.map +1 -0
  35. package/dist/sandbox/ipc-client.mjs +40 -0
  36. package/dist/sandbox/ipc-client.mjs.map +1 -0
  37. package/dist/sandbox/sandboxed-context.mjs +14 -0
  38. package/dist/sandbox/sandboxed-context.mjs.map +1 -0
  39. package/dist/shared/constants.mjs +36 -0
  40. package/dist/shared/constants.mjs.map +1 -0
  41. package/dist/shared/runtime-mode.mjs +59 -0
  42. package/dist/shared/runtime-mode.mjs.map +1 -0
  43. package/dist/tool.d.mts +42 -0
  44. package/dist/tool.d.mts.map +1 -0
  45. package/dist/tool.mjs +38 -0
  46. package/dist/tool.mjs.map +1 -0
  47. package/dist/types.d.mts +282 -0
  48. package/dist/types.d.mts.map +1 -0
  49. package/dist/types.mjs +19 -0
  50. package/dist/types.mjs.map +1 -0
  51. package/dist/ui/components/confirm.d.mts +13 -0
  52. package/dist/ui/components/confirm.d.mts.map +1 -0
  53. package/dist/ui/components/confirm.mjs +37 -0
  54. package/dist/ui/components/confirm.mjs.map +1 -0
  55. package/dist/ui/components/form.d.mts +50 -0
  56. package/dist/ui/components/form.d.mts.map +1 -0
  57. package/dist/ui/components/form.mjs +92 -0
  58. package/dist/ui/components/form.mjs.map +1 -0
  59. package/dist/ui/components/input.d.mts +29 -0
  60. package/dist/ui/components/input.d.mts.map +1 -0
  61. package/dist/ui/components/input.mjs +42 -0
  62. package/dist/ui/components/input.mjs.map +1 -0
  63. package/dist/ui/components/select.d.mts +41 -0
  64. package/dist/ui/components/select.d.mts.map +1 -0
  65. package/dist/ui/components/select.mjs +50 -0
  66. package/dist/ui/components/select.mjs.map +1 -0
  67. package/dist/ui/components/spinner.d.mts +28 -0
  68. package/dist/ui/components/spinner.d.mts.map +1 -0
  69. package/dist/ui/components/spinner.mjs +35 -0
  70. package/dist/ui/components/spinner.mjs.map +1 -0
  71. package/dist/ui/components/table.d.mts +60 -0
  72. package/dist/ui/components/table.d.mts.map +1 -0
  73. package/dist/ui/components/table.mjs +143 -0
  74. package/dist/ui/components/table.mjs.map +1 -0
  75. package/dist/ui/index.d.mts +9 -0
  76. package/dist/ui/utils/colors.d.mts +38 -0
  77. package/dist/ui/utils/colors.d.mts.map +1 -0
  78. package/dist/ui/utils/colors.mjs +64 -0
  79. package/dist/ui/utils/colors.mjs.map +1 -0
  80. package/dist/ui/utils/output.d.mts +23 -0
  81. package/dist/ui/utils/output.d.mts.map +1 -0
  82. package/dist/ui/utils/output.mjs +42 -0
  83. package/dist/ui/utils/output.mjs.map +1 -0
  84. package/dist/ui/utils/tty.d.mts +9 -0
  85. package/dist/ui/utils/tty.d.mts.map +1 -0
  86. package/dist/ui/utils/tty.mjs +12 -0
  87. package/dist/ui/utils/tty.mjs.map +1 -0
  88. package/package.json +81 -0
@@ -0,0 +1,92 @@
1
+ import { isMCP, isSandbox } from "../../shared/runtime-mode.mjs";
2
+ import { isTTY } from "../utils/tty.mjs";
3
+ import { sendIPCRequest } from "../../sandbox/ipc-client.mjs";
4
+ import * as p from "@clack/prompts";
5
+ import pc from "picocolors";
6
+
7
+ //#region src/ui/components/form.ts
8
+ /**
9
+ * Prompt for multiple fields sequentially
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const values = await form({
14
+ * title: "User Registration",
15
+ * fields: [
16
+ * { name: "name", label: "Your name", type: "string", required: true },
17
+ * { name: "age", label: "Your age", type: "number", defaultValue: 18 },
18
+ * { name: "role", label: "Role", type: "enum", enumValues: ["admin", "user"] },
19
+ * { name: "subscribe", label: "Subscribe to newsletter?", type: "boolean" }
20
+ * ]
21
+ * });
22
+ * // values: { name: string, age: number, role: string, subscribe: boolean }
23
+ * ```
24
+ */
25
+ async function form(config) {
26
+ if (isSandbox()) return sendIPCRequest("prompt:form", config);
27
+ const result = {};
28
+ if (config.title) console.log(`\n${pc.bold(config.title)}\n`);
29
+ if (!isTTY()) {
30
+ if (isMCP()) {
31
+ const requiredFields = config.fields.filter((f) => f.required && f.defaultValue === void 0).map((f) => f.name);
32
+ if (requiredFields.length > 0) throw new Error(`Interactive form not available in MCP mode. All parameters must be defined in the tool's inputSchema. Missing required fields: ${requiredFields.join(", ")}`);
33
+ }
34
+ for (const field of config.fields) result[field.name] = field.defaultValue;
35
+ return result;
36
+ }
37
+ for (const field of config.fields) {
38
+ const label = field.description ? `${field.label} (${field.description})` : field.label;
39
+ if (field.type === "boolean") {
40
+ const value = await p.confirm({
41
+ message: label,
42
+ initialValue: field.defaultValue
43
+ });
44
+ if (p.isCancel(value)) {
45
+ p.cancel("Operation cancelled");
46
+ process.exit(0);
47
+ }
48
+ result[field.name] = value;
49
+ } else if (field.type === "enum" && field.enumValues?.length) {
50
+ const value = await p.select({
51
+ message: label,
52
+ options: field.enumValues.map((v) => ({
53
+ label: v,
54
+ value: v
55
+ }))
56
+ });
57
+ if (p.isCancel(value)) {
58
+ p.cancel("Operation cancelled");
59
+ process.exit(0);
60
+ }
61
+ result[field.name] = value;
62
+ } else if (field.type === "number") {
63
+ const strValue = await p.text({
64
+ message: label,
65
+ defaultValue: field.defaultValue?.toString(),
66
+ validate: (value) => {
67
+ if (value && Number.isNaN(Number.parseFloat(value))) return "Please enter a valid number";
68
+ }
69
+ });
70
+ if (p.isCancel(strValue)) {
71
+ p.cancel("Operation cancelled");
72
+ process.exit(0);
73
+ }
74
+ result[field.name] = Number.parseFloat(strValue);
75
+ } else {
76
+ const value = await p.text({
77
+ message: label,
78
+ defaultValue: field.defaultValue
79
+ });
80
+ if (p.isCancel(value)) {
81
+ p.cancel("Operation cancelled");
82
+ process.exit(0);
83
+ }
84
+ result[field.name] = value;
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+
90
+ //#endregion
91
+ export { form };
92
+ //# sourceMappingURL=form.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.mjs","names":["result: Record<string, unknown>"],"sources":["../../../src/ui/components/form.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Field definition for forms\n */\nexport interface FormField {\n /** Field name/key */\n name: string;\n /** Display label */\n label: string;\n /** Field type */\n type: \"string\" | \"number\" | \"boolean\" | \"enum\";\n /** Whether field is required */\n required?: boolean;\n /** Default value */\n defaultValue?: unknown;\n /** Description/help text */\n description?: string;\n /** Enum options (for type: \"enum\") */\n enumValues?: string[];\n}\n\n/**\n * Configuration for form component\n */\nexport interface FormConfig {\n /** Form fields */\n fields: FormField[];\n /** Form title */\n title?: string;\n}\n\n/**\n * Prompt for multiple fields sequentially\n *\n * @example\n * ```typescript\n * const values = await form({\n * title: \"User Registration\",\n * fields: [\n * { name: \"name\", label: \"Your name\", type: \"string\", required: true },\n * { name: \"age\", label: \"Your age\", type: \"number\", defaultValue: 18 },\n * { name: \"role\", label: \"Role\", type: \"enum\", enumValues: [\"admin\", \"user\"] },\n * { name: \"subscribe\", label: \"Subscribe to newsletter?\", type: \"boolean\" }\n * ]\n * });\n * // values: { name: string, age: number, role: string, subscribe: boolean }\n * ```\n */\nexport async function form(\n config: FormConfig,\n): Promise<Record<string, unknown>> {\n // Sandbox mode: use IPC to request form from host\n if (isSandbox()) {\n return sendIPCRequest<Record<string, unknown>>(\"prompt:form\", config);\n }\n\n const result: Record<string, unknown> = {};\n\n if (config.title) {\n console.log(`\\n${pc.bold(config.title)}\\n`);\n }\n\n // Non-TTY fallback: return defaults or throw in MCP mode\n if (!isTTY()) {\n // In MCP mode, interactive forms are not allowed\n if (isMCP()) {\n const requiredFields = config.fields\n .filter((f) => f.required && f.defaultValue === undefined)\n .map((f) => f.name);\n\n if (requiredFields.length > 0) {\n throw new Error(\n `Interactive form not available in MCP mode. All parameters must be defined in the tool's inputSchema. Missing required fields: ${requiredFields.join(\", \")}`,\n );\n }\n }\n\n for (const field of config.fields) {\n result[field.name] = field.defaultValue;\n }\n return result;\n }\n\n for (const field of config.fields) {\n const label = field.description\n ? `${field.label} (${field.description})`\n : field.label;\n\n if (field.type === \"boolean\") {\n const value = await p.confirm({\n message: label,\n initialValue: field.defaultValue as boolean | undefined,\n });\n\n if (p.isCancel(value)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n result[field.name] = value;\n } else if (field.type === \"enum\" && field.enumValues?.length) {\n const value = await p.select({\n message: label,\n options: field.enumValues.map((v) => ({\n label: v,\n value: v,\n })),\n });\n\n if (p.isCancel(value)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n result[field.name] = value;\n } else if (field.type === \"number\") {\n const strValue = await p.text({\n message: label,\n defaultValue: field.defaultValue?.toString(),\n validate: (value) => {\n if (value && Number.isNaN(Number.parseFloat(value))) {\n return \"Please enter a valid number\";\n }\n return undefined;\n },\n });\n\n if (p.isCancel(strValue)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n result[field.name] = Number.parseFloat(strValue);\n } else {\n const value = await p.text({\n message: label,\n defaultValue: field.defaultValue as string | undefined,\n });\n\n if (p.isCancel(value)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n result[field.name] = value;\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqDA,eAAsB,KACpB,QACkC;AAElC,KAAI,WAAW,CACb,QAAO,eAAwC,eAAe,OAAO;CAGvE,MAAMA,SAAkC,EAAE;AAE1C,KAAI,OAAO,MACT,SAAQ,IAAI,KAAK,GAAG,KAAK,OAAO,MAAM,CAAC,IAAI;AAI7C,KAAI,CAAC,OAAO,EAAE;AAEZ,MAAI,OAAO,EAAE;GACX,MAAM,iBAAiB,OAAO,OAC3B,QAAQ,MAAM,EAAE,YAAY,EAAE,iBAAiB,OAAU,CACzD,KAAK,MAAM,EAAE,KAAK;AAErB,OAAI,eAAe,SAAS,EAC1B,OAAM,IAAI,MACR,kIAAkI,eAAe,KAAK,KAAK,GAC5J;;AAIL,OAAK,MAAM,SAAS,OAAO,OACzB,QAAO,MAAM,QAAQ,MAAM;AAE7B,SAAO;;AAGT,MAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,QAAQ,MAAM,cAChB,GAAG,MAAM,MAAM,IAAI,MAAM,YAAY,KACrC,MAAM;AAEV,MAAI,MAAM,SAAS,WAAW;GAC5B,MAAM,QAAQ,MAAM,EAAE,QAAQ;IAC5B,SAAS;IACT,cAAc,MAAM;IACrB,CAAC;AAEF,OAAI,EAAE,SAAS,MAAM,EAAE;AACrB,MAAE,OAAO,sBAAsB;AAC/B,YAAQ,KAAK,EAAE;;AAGjB,UAAO,MAAM,QAAQ;aACZ,MAAM,SAAS,UAAU,MAAM,YAAY,QAAQ;GAC5D,MAAM,QAAQ,MAAM,EAAE,OAAO;IAC3B,SAAS;IACT,SAAS,MAAM,WAAW,KAAK,OAAO;KACpC,OAAO;KACP,OAAO;KACR,EAAE;IACJ,CAAC;AAEF,OAAI,EAAE,SAAS,MAAM,EAAE;AACrB,MAAE,OAAO,sBAAsB;AAC/B,YAAQ,KAAK,EAAE;;AAGjB,UAAO,MAAM,QAAQ;aACZ,MAAM,SAAS,UAAU;GAClC,MAAM,WAAW,MAAM,EAAE,KAAK;IAC5B,SAAS;IACT,cAAc,MAAM,cAAc,UAAU;IAC5C,WAAW,UAAU;AACnB,SAAI,SAAS,OAAO,MAAM,OAAO,WAAW,MAAM,CAAC,CACjD,QAAO;;IAIZ,CAAC;AAEF,OAAI,EAAE,SAAS,SAAS,EAAE;AACxB,MAAE,OAAO,sBAAsB;AAC/B,YAAQ,KAAK,EAAE;;AAGjB,UAAO,MAAM,QAAQ,OAAO,WAAW,SAAS;SAC3C;GACL,MAAM,QAAQ,MAAM,EAAE,KAAK;IACzB,SAAS;IACT,cAAc,MAAM;IACrB,CAAC;AAEF,OAAI,EAAE,SAAS,MAAM,EAAE;AACrB,MAAE,OAAO,sBAAsB;AAC/B,YAAQ,KAAK,EAAE;;AAGjB,UAAO,MAAM,QAAQ;;;AAIzB,QAAO"}
@@ -0,0 +1,29 @@
1
+ //#region src/ui/components/input.d.ts
2
+ /**
3
+ * Configuration for input component
4
+ */
5
+ interface InputConfig {
6
+ /** Prompt message */
7
+ prompt: string;
8
+ /** Default value */
9
+ defaultValue?: string;
10
+ /** Placeholder text */
11
+ placeholder?: string;
12
+ /** Maximum input length */
13
+ maxLength?: number;
14
+ }
15
+ /**
16
+ * Prompt for text input
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const name = await input({
21
+ * prompt: "What's your name?",
22
+ * defaultValue: "Anonymous"
23
+ * });
24
+ * ```
25
+ */
26
+ declare function input(config: InputConfig): Promise<string>;
27
+ //#endregion
28
+ export { InputConfig, input };
29
+ //# sourceMappingURL=input.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.mts","names":[],"sources":["../../../src/ui/components/input.ts"],"sourcesContent":[],"mappings":";;AAQA;AAsBA;UAtBiB,WAAA;;;;;;;;;;;;;;;;;;;;;iBAsBK,KAAA,SAAc,cAAc"}
@@ -0,0 +1,42 @@
1
+ import { isMCP, isSandbox } from "../../shared/runtime-mode.mjs";
2
+ import { isTTY } from "../utils/tty.mjs";
3
+ import { sendIPCRequest } from "../../sandbox/ipc-client.mjs";
4
+ import * as p from "@clack/prompts";
5
+
6
+ //#region src/ui/components/input.ts
7
+ /**
8
+ * Prompt for text input
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const name = await input({
13
+ * prompt: "What's your name?",
14
+ * defaultValue: "Anonymous"
15
+ * });
16
+ * ```
17
+ */
18
+ async function input(config) {
19
+ if (isSandbox()) return sendIPCRequest("prompt:input", config);
20
+ if (!isTTY()) {
21
+ if (config.defaultValue !== void 0) return config.defaultValue;
22
+ if (isMCP()) throw new Error(`Interactive input not available in MCP mode. All parameters must be defined in the tool's inputSchema. Missing parameter: ${config.prompt}`);
23
+ throw new Error("Interactive input not available in non-TTY mode. Please provide all required arguments.");
24
+ }
25
+ const result = await p.text({
26
+ message: config.prompt,
27
+ defaultValue: config.defaultValue,
28
+ placeholder: config.placeholder,
29
+ validate: config.maxLength ? (value) => {
30
+ if (value && value.length > config.maxLength) return `Input must be ${config.maxLength} characters or less`;
31
+ } : void 0
32
+ });
33
+ if (p.isCancel(result)) {
34
+ p.cancel("Operation cancelled");
35
+ process.exit(0);
36
+ }
37
+ return result;
38
+ }
39
+
40
+ //#endregion
41
+ export { input };
42
+ //# sourceMappingURL=input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.mjs","names":[],"sources":["../../../src/ui/components/input.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Configuration for input component\n */\nexport interface InputConfig {\n /** Prompt message */\n prompt: string;\n /** Default value */\n defaultValue?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Maximum input length */\n maxLength?: number;\n}\n\n/**\n * Prompt for text input\n *\n * @example\n * ```typescript\n * const name = await input({\n * prompt: \"What's your name?\",\n * defaultValue: \"Anonymous\"\n * });\n * ```\n */\nexport async function input(config: InputConfig): Promise<string> {\n // Sandbox mode: use IPC to request input from host\n if (isSandbox()) {\n return sendIPCRequest<string>(\"prompt:input\", config);\n }\n\n // Non-TTY fallback: return default or throw\n if (!isTTY()) {\n if (config.defaultValue !== undefined) {\n return config.defaultValue;\n }\n\n // Provide MCP-specific error message\n if (isMCP()) {\n throw new Error(\n `Interactive input not available in MCP mode. All parameters must be defined in the tool's inputSchema. Missing parameter: ${config.prompt}`,\n );\n }\n\n throw new Error(\n \"Interactive input not available in non-TTY mode. Please provide all required arguments.\",\n );\n }\n\n const result = await p.text({\n message: config.prompt,\n defaultValue: config.defaultValue,\n placeholder: config.placeholder,\n validate: config.maxLength\n ? (value) => {\n if (value && value.length > config.maxLength!) {\n return `Input must be ${config.maxLength} characters or less`;\n }\n return undefined;\n }\n : undefined,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8BA,eAAsB,MAAM,QAAsC;AAEhE,KAAI,WAAW,CACb,QAAO,eAAuB,gBAAgB,OAAO;AAIvD,KAAI,CAAC,OAAO,EAAE;AACZ,MAAI,OAAO,iBAAiB,OAC1B,QAAO,OAAO;AAIhB,MAAI,OAAO,CACT,OAAM,IAAI,MACR,6HAA6H,OAAO,SACrI;AAGH,QAAM,IAAI,MACR,0FACD;;CAGH,MAAM,SAAS,MAAM,EAAE,KAAK;EAC1B,SAAS,OAAO;EAChB,cAAc,OAAO;EACrB,aAAa,OAAO;EACpB,UAAU,OAAO,aACZ,UAAU;AACT,OAAI,SAAS,MAAM,SAAS,OAAO,UACjC,QAAO,iBAAiB,OAAO,UAAU;MAI7C;EACL,CAAC;AAEF,KAAI,EAAE,SAAS,OAAO,EAAE;AACtB,IAAE,OAAO,sBAAsB;AAC/B,UAAQ,KAAK,EAAE;;AAGjB,QAAO"}
@@ -0,0 +1,41 @@
1
+ //#region src/ui/components/select.d.ts
2
+ /**
3
+ * Option for select menus
4
+ */
5
+ interface SelectOption<T = string> {
6
+ /** Display name */
7
+ name: string;
8
+ /** Optional description shown below name */
9
+ description?: string;
10
+ /** Value returned when selected */
11
+ value: T;
12
+ /** Disable this option */
13
+ disabled?: boolean;
14
+ }
15
+ /**
16
+ * Configuration for select component
17
+ */
18
+ interface SelectConfig<T> {
19
+ /** Options to choose from */
20
+ options: SelectOption<T>[];
21
+ /** Prompt message */
22
+ prompt?: string;
23
+ }
24
+ /**
25
+ * Show a selection menu and wait for user choice
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const color = await select({
30
+ * options: [
31
+ * { name: "Red", value: "red" },
32
+ * { name: "Blue", value: "blue", description: "Ocean color" },
33
+ * ],
34
+ * prompt: "Pick a color"
35
+ * });
36
+ * ```
37
+ */
38
+ declare function select<T = string>(config: SelectConfig<T>): Promise<T>;
39
+ //#endregion
40
+ export { SelectConfig, SelectOption, select };
41
+ //# sourceMappingURL=select.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.d.mts","names":[],"sources":["../../../src/ui/components/select.ts"],"sourcesContent":[],"mappings":";;AAQA;AAcA;AAqBsB,UAnCL,YAmCW,CAAA,IAAA,MAAA,CAAA,CAAA;EAAkC;EAAb,IAAA,EAAA,MAAA;EAA0B;EAAR,WAAA,CAAA,EAAA,MAAA;EAAO;SA7BjE;;;;;;;UAQQ;;WAEN,aAAa;;;;;;;;;;;;;;;;;;iBAmBF,2BAA2B,aAAa,KAAK,QAAQ"}
@@ -0,0 +1,50 @@
1
+ import { isMCP, isSandbox } from "../../shared/runtime-mode.mjs";
2
+ import { isTTY } from "../utils/tty.mjs";
3
+ import { sendIPCRequest } from "../../sandbox/ipc-client.mjs";
4
+ import * as p from "@clack/prompts";
5
+
6
+ //#region src/ui/components/select.ts
7
+ /**
8
+ * Show a selection menu and wait for user choice
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const color = await select({
13
+ * options: [
14
+ * { name: "Red", value: "red" },
15
+ * { name: "Blue", value: "blue", description: "Ocean color" },
16
+ * ],
17
+ * prompt: "Pick a color"
18
+ * });
19
+ * ```
20
+ */
21
+ async function select(config) {
22
+ if (isSandbox()) return sendIPCRequest("prompt:select", {
23
+ prompt: config.prompt ?? "Select an option",
24
+ options: config.options
25
+ });
26
+ if (!isTTY()) {
27
+ if (isMCP()) throw new Error(`Interactive selection not available in MCP mode. All parameters must be defined in the tool's inputSchema. Selection prompt: ${config.prompt}`);
28
+ const firstOption = config.options[0];
29
+ if (!firstOption) throw new Error("No options provided");
30
+ return firstOption.value;
31
+ }
32
+ const mappedOptions = config.options.map((opt) => ({
33
+ label: opt.name,
34
+ value: opt.value,
35
+ ...opt.description && { hint: opt.description }
36
+ }));
37
+ const result = await p.select({
38
+ message: config.prompt ?? "Select an option",
39
+ options: mappedOptions
40
+ });
41
+ if (p.isCancel(result)) {
42
+ p.cancel("Operation cancelled");
43
+ process.exit(0);
44
+ }
45
+ return result;
46
+ }
47
+
48
+ //#endregion
49
+ export { select };
50
+ //# sourceMappingURL=select.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.mjs","names":[],"sources":["../../../src/ui/components/select.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\nimport { sendIPCRequest } from \"../../sandbox/ipc-client\";\nimport { isMCP, isSandbox } from \"../../shared/runtime-mode\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Option for select menus\n */\nexport interface SelectOption<T = string> {\n /** Display name */\n name: string;\n /** Optional description shown below name */\n description?: string;\n /** Value returned when selected */\n value: T;\n /** Disable this option */\n disabled?: boolean;\n}\n\n/**\n * Configuration for select component\n */\nexport interface SelectConfig<T> {\n /** Options to choose from */\n options: SelectOption<T>[];\n /** Prompt message */\n prompt?: string;\n}\n\n/**\n * Show a selection menu and wait for user choice\n *\n * @example\n * ```typescript\n * const color = await select({\n * options: [\n * { name: \"Red\", value: \"red\" },\n * { name: \"Blue\", value: \"blue\", description: \"Ocean color\" },\n * ],\n * prompt: \"Pick a color\"\n * });\n * ```\n */\nexport async function select<T = string>(config: SelectConfig<T>): Promise<T> {\n // Sandbox mode: use IPC to request select from host\n if (isSandbox()) {\n return sendIPCRequest<T>(\"prompt:select\", {\n prompt: config.prompt ?? \"Select an option\",\n options: config.options,\n });\n }\n\n // Non-TTY fallback: auto-select first option or throw in MCP mode\n if (!isTTY()) {\n // In MCP mode, interactive selection is not allowed\n if (isMCP()) {\n throw new Error(\n `Interactive selection not available in MCP mode. All parameters must be defined in the tool's inputSchema. Selection prompt: ${config.prompt}`,\n );\n }\n\n const firstOption = config.options[0];\n if (!firstOption) {\n throw new Error(\"No options provided\");\n }\n return firstOption.value;\n }\n\n // Map to @clack/prompts Option format\n // Using type assertion due to complex Option<T> conditional types\n const mappedOptions = config.options.map((opt) => ({\n label: opt.name,\n value: opt.value as unknown,\n ...(opt.description && { hint: opt.description }),\n }));\n\n const result = await p.select({\n message: config.prompt ?? \"Select an option\",\n options: mappedOptions,\n });\n\n if (p.isCancel(result)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return result as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,eAAsB,OAAmB,QAAqC;AAE5E,KAAI,WAAW,CACb,QAAO,eAAkB,iBAAiB;EACxC,QAAQ,OAAO,UAAU;EACzB,SAAS,OAAO;EACjB,CAAC;AAIJ,KAAI,CAAC,OAAO,EAAE;AAEZ,MAAI,OAAO,CACT,OAAM,IAAI,MACR,gIAAgI,OAAO,SACxI;EAGH,MAAM,cAAc,OAAO,QAAQ;AACnC,MAAI,CAAC,YACH,OAAM,IAAI,MAAM,sBAAsB;AAExC,SAAO,YAAY;;CAKrB,MAAM,gBAAgB,OAAO,QAAQ,KAAK,SAAS;EACjD,OAAO,IAAI;EACX,OAAO,IAAI;EACX,GAAI,IAAI,eAAe,EAAE,MAAM,IAAI,aAAa;EACjD,EAAE;CAEH,MAAM,SAAS,MAAM,EAAE,OAAO;EAC5B,SAAS,OAAO,UAAU;EAC1B,SAAS;EACV,CAAC;AAEF,KAAI,EAAE,SAAS,OAAO,EAAE;AACtB,IAAE,OAAO,sBAAsB;AAC/B,UAAQ,KAAK,EAAE;;AAGjB,QAAO"}
@@ -0,0 +1,28 @@
1
+ //#region src/ui/components/spinner.d.ts
2
+ /**
3
+ * Handle for controlling a spinner
4
+ */
5
+ interface SpinnerHandle {
6
+ /** Update spinner message */
7
+ update(message: string): void;
8
+ /** Mark as successful and stop */
9
+ succeed(message?: string): void;
10
+ /** Mark as failed and stop */
11
+ fail(message?: string): void;
12
+ /** Stop spinner without status */
13
+ stop(): void;
14
+ }
15
+ /**
16
+ * Create a spinner for showing progress
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const spin = spinner("Loading...");
21
+ * await doSomething();
22
+ * spin.succeed("Done!");
23
+ * ```
24
+ */
25
+ declare function spinner(message: string): SpinnerHandle;
26
+ //#endregion
27
+ export { SpinnerHandle, spinner };
28
+ //# sourceMappingURL=spinner.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.mts","names":[],"sources":["../../../src/ui/components/spinner.ts"],"sourcesContent":[],"mappings":";;AAKA;AAqBA;UArBiB,aAAA;;;;;;;;;;;;;;;;;;;;iBAqBD,OAAA,mBAA0B"}
@@ -0,0 +1,35 @@
1
+ import * as p from "@clack/prompts";
2
+
3
+ //#region src/ui/components/spinner.ts
4
+ /**
5
+ * Create a spinner for showing progress
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const spin = spinner("Loading...");
10
+ * await doSomething();
11
+ * spin.succeed("Done!");
12
+ * ```
13
+ */
14
+ function spinner(message) {
15
+ const instance = p.spinner();
16
+ instance.start(message);
17
+ return {
18
+ update(msg) {
19
+ instance.message(msg);
20
+ },
21
+ succeed(msg) {
22
+ instance.stop(msg ?? message);
23
+ },
24
+ fail(msg) {
25
+ instance.stop(msg ?? message);
26
+ },
27
+ stop() {
28
+ instance.stop();
29
+ }
30
+ };
31
+ }
32
+
33
+ //#endregion
34
+ export { spinner };
35
+ //# sourceMappingURL=spinner.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.mjs","names":[],"sources":["../../../src/ui/components/spinner.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\n\n/**\n * Handle for controlling a spinner\n */\nexport interface SpinnerHandle {\n /** Update spinner message */\n update(message: string): void;\n /** Mark as successful and stop */\n succeed(message?: string): void;\n /** Mark as failed and stop */\n fail(message?: string): void;\n /** Stop spinner without status */\n stop(): void;\n}\n\n/**\n * Create a spinner for showing progress\n *\n * @example\n * ```typescript\n * const spin = spinner(\"Loading...\");\n * await doSomething();\n * spin.succeed(\"Done!\");\n * ```\n */\nexport function spinner(message: string): SpinnerHandle {\n const instance = p.spinner();\n instance.start(message);\n\n return {\n update(msg: string) {\n instance.message(msg);\n },\n\n succeed(msg?: string) {\n instance.stop(msg ?? message);\n },\n\n fail(msg?: string) {\n instance.stop(msg ?? message);\n },\n\n stop() {\n instance.stop();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgB,QAAQ,SAAgC;CACtD,MAAM,WAAW,EAAE,SAAS;AAC5B,UAAS,MAAM,QAAQ;AAEvB,QAAO;EACL,OAAO,KAAa;AAClB,YAAS,QAAQ,IAAI;;EAGvB,QAAQ,KAAc;AACpB,YAAS,KAAK,OAAO,QAAQ;;EAG/B,KAAK,KAAc;AACjB,YAAS,KAAK,OAAO,QAAQ;;EAG/B,OAAO;AACL,YAAS,MAAM;;EAElB"}
@@ -0,0 +1,60 @@
1
+ //#region src/ui/components/table.d.ts
2
+ /**
3
+ * Column alignment options
4
+ */
5
+ type ColumnAlign = "left" | "center" | "right";
6
+ /**
7
+ * Column definition for table
8
+ */
9
+ interface TableColumn<T = Record<string, unknown>> {
10
+ /** Column key in the data object */
11
+ key: keyof T;
12
+ /** Display header text */
13
+ header: string;
14
+ /** Column alignment (default: left) */
15
+ align?: ColumnAlign;
16
+ /** Fixed column width (auto-calculated if not specified) */
17
+ width?: number;
18
+ /** Custom formatter function for cell values */
19
+ formatter?: (value: unknown, row: T) => string;
20
+ }
21
+ /**
22
+ * Configuration for table component
23
+ */
24
+ interface TableConfig<T = Record<string, unknown>> {
25
+ /** Column definitions */
26
+ columns: TableColumn<T>[];
27
+ /** Data rows */
28
+ rows: T[];
29
+ /** Show header row (default: true) */
30
+ showHeader?: boolean;
31
+ /** Show borders (default: true in TTY mode) */
32
+ showBorders?: boolean;
33
+ /** Table title (optional) */
34
+ title?: string;
35
+ }
36
+ /**
37
+ * Display a table with columns and rows
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * table({
42
+ * title: "Users",
43
+ * columns: [
44
+ * { key: "name", header: "Name" },
45
+ * { key: "age", header: "Age", align: "right" },
46
+ * { key: "status", header: "Status", formatter: (val) =>
47
+ * val === "active" ? pc.green("✓ Active") : pc.red("✗ Inactive")
48
+ * },
49
+ * ],
50
+ * rows: [
51
+ * { name: "Alice", age: 25, status: "active" },
52
+ * { name: "Bob", age: 30, status: "inactive" },
53
+ * ],
54
+ * });
55
+ * ```
56
+ */
57
+ declare function table<T = Record<string, unknown>>(config: TableConfig<T>): void;
58
+ //#endregion
59
+ export { ColumnAlign, TableColumn, TableConfig, table };
60
+ //# sourceMappingURL=table.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.d.mts","names":[],"sources":["../../../src/ui/components/table.ts"],"sourcesContent":[],"mappings":";;AAMA;AAKA;AAAiC,KALrB,WAAA,GAKqB,MAAA,GAAA,QAAA,GAAA,OAAA;;;;AAUI,UAVpB,WAUoB,CAAA,IAVJ,MAUI,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA;EAMpB;EAAgB,GAAA,EAAA,MAdpB,CAcoB;EAEV;EAAZ,MAAA,EAAA,MAAA;EAEH;EAAC,KAAA,CAAA,EAdC,WAcD;EA4MO;EAAU,KAAA,CAAA,EAAA,MAAA;EACJ;EAAZ,SAAA,CAAA,EAAA,CAAA,KAAA,EAAA,OAAA,EAAA,GAAA,EAvN0B,CAuN1B,EAAA,GAAA,MAAA;;;;;UAjNO,gBAAgB;;WAEtB,YAAY;;QAEf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4MQ,UAAU,iCAChB,YAAY"}
@@ -0,0 +1,143 @@
1
+ import { isTTY } from "../utils/tty.mjs";
2
+ import { formatText, pc } from "../utils/colors.mjs";
3
+
4
+ //#region src/ui/components/table.ts
5
+ /**
6
+ * Align text within a given width
7
+ */
8
+ function alignText(text, width, align) {
9
+ const textLength = stripAnsi(text).length;
10
+ const padding = Math.max(0, width - textLength);
11
+ switch (align) {
12
+ case "right": return " ".repeat(padding) + text;
13
+ case "center": {
14
+ const leftPad = Math.floor(padding / 2);
15
+ const rightPad = padding - leftPad;
16
+ return " ".repeat(leftPad) + text + " ".repeat(rightPad);
17
+ }
18
+ default: return text + " ".repeat(padding);
19
+ }
20
+ }
21
+ /**
22
+ * Strip ANSI escape codes from string for length calculation
23
+ */
24
+ function stripAnsi(str) {
25
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
26
+ }
27
+ /**
28
+ * Calculate column widths based on content
29
+ */
30
+ function calculateColumnWidths(columns, rows, showHeader) {
31
+ return columns.map((col) => {
32
+ if (col.width !== void 0) return col.width;
33
+ let maxWidth = showHeader ? col.header.length : 0;
34
+ for (const row of rows) {
35
+ const value = row[col.key];
36
+ const length = stripAnsi(col.formatter ? col.formatter(value, row) : String(value ?? "")).length;
37
+ maxWidth = Math.max(maxWidth, length);
38
+ }
39
+ return maxWidth;
40
+ });
41
+ }
42
+ /**
43
+ * Format a single cell value
44
+ */
45
+ function formatCell(value, row, column) {
46
+ if (column.formatter) return column.formatter(value, row);
47
+ if (value === null || value === void 0) return pc.dim("-");
48
+ return String(value);
49
+ }
50
+ /**
51
+ * Render table in TTY mode with borders and styling
52
+ */
53
+ function renderTTY(config) {
54
+ const { columns, rows, showHeader = true, showBorders = true, title } = config;
55
+ const lines = [];
56
+ const widths = calculateColumnWidths(columns, rows, showHeader);
57
+ if (title) {
58
+ lines.push("");
59
+ lines.push(formatText(`${title}`, { bold: true }));
60
+ lines.push("");
61
+ }
62
+ if (showHeader) {
63
+ const headerCells = columns.map((col, i) => {
64
+ return alignText(formatText(col.header, {
65
+ bold: true,
66
+ color: "cyan"
67
+ }), widths[i], col.align ?? "left");
68
+ });
69
+ lines.push(headerCells.join(" "));
70
+ if (showBorders) {
71
+ const separatorParts = widths.map((w) => "─".repeat(w));
72
+ lines.push(pc.gray(separatorParts.join("─")));
73
+ }
74
+ }
75
+ for (const row of rows) {
76
+ const cells = columns.map((col, i) => {
77
+ const value = row[col.key];
78
+ return alignText(formatCell(value, row, col), widths[i], col.align ?? "left");
79
+ });
80
+ lines.push(cells.join(" "));
81
+ }
82
+ if (showBorders && rows.length > 0) {
83
+ const separatorParts = widths.map((w) => "─".repeat(w));
84
+ lines.push(pc.gray(separatorParts.join("─")));
85
+ }
86
+ return lines.join("\n");
87
+ }
88
+ /**
89
+ * Render table in non-TTY mode (plain text)
90
+ */
91
+ function renderPlain(config) {
92
+ const { columns, rows, showHeader = true, title } = config;
93
+ const lines = [];
94
+ const widths = calculateColumnWidths(columns, rows, showHeader);
95
+ if (title) {
96
+ lines.push("");
97
+ lines.push(title);
98
+ lines.push("");
99
+ }
100
+ if (showHeader) {
101
+ const headerCells = columns.map((col, i) => alignText(col.header, widths[i], col.align ?? "left"));
102
+ lines.push(headerCells.join(" "));
103
+ const separator = columns.map((_, i) => "-".repeat(widths[i])).join(" ");
104
+ lines.push(separator);
105
+ }
106
+ for (const row of rows) {
107
+ const cells = columns.map((col, i) => {
108
+ const value = row[col.key];
109
+ return alignText(stripAnsi(formatCell(value, row, col)), widths[i], col.align ?? "left");
110
+ });
111
+ lines.push(cells.join(" "));
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ /**
116
+ * Display a table with columns and rows
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * table({
121
+ * title: "Users",
122
+ * columns: [
123
+ * { key: "name", header: "Name" },
124
+ * { key: "age", header: "Age", align: "right" },
125
+ * { key: "status", header: "Status", formatter: (val) =>
126
+ * val === "active" ? pc.green("✓ Active") : pc.red("✗ Inactive")
127
+ * },
128
+ * ],
129
+ * rows: [
130
+ * { name: "Alice", age: 25, status: "active" },
131
+ * { name: "Bob", age: 30, status: "inactive" },
132
+ * ],
133
+ * });
134
+ * ```
135
+ */
136
+ function table(config) {
137
+ const output = isTTY() ? renderTTY(config) : renderPlain(config);
138
+ console.log(output);
139
+ }
140
+
141
+ //#endregion
142
+ export { table };
143
+ //# sourceMappingURL=table.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.mjs","names":["lines: string[]"],"sources":["../../../src/ui/components/table.ts"],"sourcesContent":["import { formatText, pc } from \"../utils/colors\";\nimport { isTTY } from \"../utils/tty\";\n\n/**\n * Column alignment options\n */\nexport type ColumnAlign = \"left\" | \"center\" | \"right\";\n\n/**\n * Column definition for table\n */\nexport interface TableColumn<T = Record<string, unknown>> {\n /** Column key in the data object */\n key: keyof T;\n /** Display header text */\n header: string;\n /** Column alignment (default: left) */\n align?: ColumnAlign;\n /** Fixed column width (auto-calculated if not specified) */\n width?: number;\n /** Custom formatter function for cell values */\n formatter?: (value: unknown, row: T) => string;\n}\n\n/**\n * Configuration for table component\n */\nexport interface TableConfig<T = Record<string, unknown>> {\n /** Column definitions */\n columns: TableColumn<T>[];\n /** Data rows */\n rows: T[];\n /** Show header row (default: true) */\n showHeader?: boolean;\n /** Show borders (default: true in TTY mode) */\n showBorders?: boolean;\n /** Table title (optional) */\n title?: string;\n}\n\n/**\n * Align text within a given width\n */\nfunction alignText(text: string, width: number, align: ColumnAlign): string {\n const textLength = stripAnsi(text).length;\n const padding = Math.max(0, width - textLength);\n\n switch (align) {\n case \"right\":\n return \" \".repeat(padding) + text;\n case \"center\": {\n const leftPad = Math.floor(padding / 2);\n const rightPad = padding - leftPad;\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\n }\n default:\n return text + \" \".repeat(padding);\n }\n}\n\n/**\n * Strip ANSI escape codes from string for length calculation\n */\nfunction stripAnsi(str: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI codes regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\n/**\n * Calculate column widths based on content\n */\nfunction calculateColumnWidths<T>(\n columns: TableColumn<T>[],\n rows: T[],\n showHeader: boolean,\n): number[] {\n return columns.map((col) => {\n // Use fixed width if specified\n if (col.width !== undefined) {\n return col.width;\n }\n\n // Calculate max width from header and data\n let maxWidth = showHeader ? col.header.length : 0;\n\n for (const row of rows) {\n const value = row[col.key];\n const formatted = col.formatter\n ? col.formatter(value, row)\n : String(value ?? \"\");\n const length = stripAnsi(formatted).length;\n maxWidth = Math.max(maxWidth, length);\n }\n\n return maxWidth;\n });\n}\n\n/**\n * Format a single cell value\n */\nfunction formatCell<T>(value: unknown, row: T, column: TableColumn<T>): string {\n if (column.formatter) {\n return column.formatter(value, row);\n }\n\n if (value === null || value === undefined) {\n return pc.dim(\"-\");\n }\n\n return String(value);\n}\n\n/**\n * Render table in TTY mode with borders and styling\n */\nfunction renderTTY<T>(config: TableConfig<T>): string {\n const {\n columns,\n rows,\n showHeader = true,\n showBorders = true,\n title,\n } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(formatText(`${title}`, { bold: true }));\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) => {\n const text = formatText(col.header, { bold: true, color: \"cyan\" });\n return alignText(text, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(headerCells.join(\" \"));\n\n // Header separator\n if (showBorders) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(formatted, widths[i]!, col.align ?? \"left\");\n });\n\n lines.push(cells.join(\" \"));\n }\n\n // Bottom border (optional)\n if (showBorders && rows.length > 0) {\n const separatorParts = widths.map((w) => \"─\".repeat(w));\n lines.push(pc.gray(separatorParts.join(\"─\")));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render table in non-TTY mode (plain text)\n */\nfunction renderPlain<T>(config: TableConfig<T>): string {\n const { columns, rows, showHeader = true, title } = config;\n const lines: string[] = [];\n\n // Calculate column widths\n const widths = calculateColumnWidths(columns, rows, showHeader);\n\n // Title\n if (title) {\n lines.push(\"\");\n lines.push(title);\n lines.push(\"\");\n }\n\n // Header row\n if (showHeader) {\n const headerCells = columns.map((col, i) =>\n alignText(col.header, widths[i]!, col.align ?? \"left\"),\n );\n lines.push(headerCells.join(\" \"));\n\n // Separator\n const separator = columns.map((_, i) => \"-\".repeat(widths[i]!)).join(\" \");\n lines.push(separator);\n }\n\n // Data rows\n for (const row of rows) {\n const cells = columns.map((col, i) => {\n const value = row[col.key];\n const formatted = formatCell(value, row, col);\n return alignText(stripAnsi(formatted), widths[i]!, col.align ?? \"left\");\n });\n lines.push(cells.join(\" \"));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Display a table with columns and rows\n *\n * @example\n * ```typescript\n * table({\n * title: \"Users\",\n * columns: [\n * { key: \"name\", header: \"Name\" },\n * { key: \"age\", header: \"Age\", align: \"right\" },\n * { key: \"status\", header: \"Status\", formatter: (val) =>\n * val === \"active\" ? pc.green(\"✓ Active\") : pc.red(\"✗ Inactive\")\n * },\n * ],\n * rows: [\n * { name: \"Alice\", age: 25, status: \"active\" },\n * { name: \"Bob\", age: 30, status: \"inactive\" },\n * ],\n * });\n * ```\n */\nexport function table<T = Record<string, unknown>>(\n config: TableConfig<T>,\n): void {\n const output = isTTY() ? renderTTY(config) : renderPlain(config);\n console.log(output);\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { confirm } from "./components/confirm.mjs";
2
+ import { FormConfig, FormField, form } from "./components/form.mjs";
3
+ import { InputConfig, input } from "./components/input.mjs";
4
+ import { SelectConfig, SelectOption, select } from "./components/select.mjs";
5
+ import { SpinnerHandle, spinner } from "./components/spinner.mjs";
6
+ import { ColumnAlign, TableColumn, TableConfig, table } from "./components/table.mjs";
7
+ import { AnsiColor, formatText, pc, theme } from "./utils/colors.mjs";
8
+ import { error, help, output } from "./utils/output.mjs";
9
+ import { isTTY } from "./utils/tty.mjs";
@@ -0,0 +1,38 @@
1
+ import pc$1 from "picocolors";
2
+
3
+ //#region src/ui/utils/colors.d.ts
4
+
5
+ /**
6
+ * Default color theme for UI components (hex values for reference)
7
+ */
8
+ declare const theme: {
9
+ readonly primary: "#3b82f6";
10
+ readonly success: "#10b981";
11
+ readonly warning: "#f59e0b";
12
+ readonly error: "#ef4444";
13
+ readonly info: "#06b6d4";
14
+ readonly background: "#161b22";
15
+ readonly surface: "#1e293b";
16
+ readonly border: "#30363d";
17
+ readonly text: "#c9d1d9";
18
+ readonly textDim: "#8b949e";
19
+ readonly textBright: "#ffffff";
20
+ readonly focused: "#3b82f6";
21
+ readonly selected: "#3b82f6";
22
+ readonly hover: "#334155";
23
+ readonly disabled: "#6e7681";
24
+ };
25
+ type AnsiColor = "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray";
26
+ /**
27
+ * Format text with picocolors
28
+ */
29
+ declare function formatText(text: string, options?: {
30
+ color?: AnsiColor;
31
+ bold?: boolean;
32
+ dim?: boolean;
33
+ italic?: boolean;
34
+ underline?: boolean;
35
+ }): string;
36
+ //#endregion
37
+ export { AnsiColor, formatText, pc$1 as pc, theme };
38
+ //# sourceMappingURL=colors.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.mts","names":[],"sources":["../../../src/ui/utils/colors.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAuBY,cAvBC,KAuBQ,EAAA;EAaL,SAAA,OAAU,EAAA,SAGd;;;;;;;;;;;;;;;;KAhBA,SAAA;;;;iBAaI,UAAA;UAGJ"}