From 82f600060cf6b830cb8ef5e162f915983e5569e7 Mon Sep 17 00:00:00 2001 From: freelanceagent1 <217720360+freelanceagent1@users.noreply.github.com> Date: Wed, 15 Apr 2026 01:17:29 -0700 Subject: [PATCH 1/2] fix(cli): remove clack prompt dependency from setup wizard --- bun.lock | 35 ++-- packages/opencode-plugin/package.json | 1 - packages/opencode-plugin/src/cli/prompts.ts | 184 +++++++++++++++----- 3 files changed, 149 insertions(+), 71 deletions(-) diff --git a/bun.lock b/bun.lock index 240b19f..91d7f19 100644 --- a/bun.lock +++ b/bun.lock @@ -16,12 +16,11 @@ }, "packages/opencode-plugin": { "name": "@cortexkit/aft-opencode", - "version": "0.11.0", + "version": "0.12.2", "bin": { "aft-opencode": "dist/cli.js", }, "dependencies": { - "@clack/prompts": "^1.2.0", "@opencode-ai/plugin": "^1.2.26", "@opencode-ai/sdk": "^1.2.26", "comment-json": "^4.6.2", @@ -32,11 +31,11 @@ "typescript": "^5.8.0", }, "optionalDependencies": { - "@cortexkit/aft-darwin-arm64": "0.11.0", - "@cortexkit/aft-darwin-x64": "0.11.0", - "@cortexkit/aft-linux-arm64": "0.11.0", - "@cortexkit/aft-linux-x64": "0.11.0", - "@cortexkit/aft-win32-x64": "0.11.0", + "@cortexkit/aft-darwin-arm64": "0.12.2", + "@cortexkit/aft-darwin-x64": "0.12.2", + "@cortexkit/aft-linux-arm64": "0.12.2", + "@cortexkit/aft-linux-x64": "0.12.2", + "@cortexkit/aft-win32-x64": "0.12.2", }, "peerDependencies": { "@opencode-ai/plugin": ">=1.2.0", @@ -62,21 +61,17 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ=="], - "@clack/core": ["@clack/core@1.2.0", "", { "dependencies": { "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg=="], + "@cortexkit/aft-darwin-arm64": ["@cortexkit/aft-darwin-arm64@0.12.2", "", { "os": "darwin", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-r2nPUFQkYjuXLJ2ejgvuia+a+XXJdA6DhstgpOQZ9KY7I41GDGsIdnABflNEzlmFCbR5KqkA17uNZ1yPAeeazg=="], - "@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="], + "@cortexkit/aft-darwin-x64": ["@cortexkit/aft-darwin-x64@0.12.2", "", { "os": "darwin", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-V9RoQf8XP+GljDNOvd9ptNoo5fjl6cFD5+/+5HuHsejNY+NzBJCQ+VDUgiNWxcyUzRd3I9gCK7wOhIH/HBsavw=="], - "@cortexkit/aft-darwin-arm64": ["@cortexkit/aft-darwin-arm64@0.11.0", "", { "os": "darwin", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-JovPgbg3vGqg9b8iYqoUgJML1vwqgfPEU5WnSwMMH/Tmn0t2gMVkbZ94+NBntGQ9NL4M3DPXoJQHRjDgIXnh2w=="], + "@cortexkit/aft-linux-arm64": ["@cortexkit/aft-linux-arm64@0.12.2", "", { "os": "linux", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-ZiNUWaHHZei+DPSqzrGrlf2iCR1IwQo0acKsZmvckh5j4Cb5i5ERpt1HeW7ClxwgbEZnNZ5br83O/ciBCfihJA=="], - "@cortexkit/aft-darwin-x64": ["@cortexkit/aft-darwin-x64@0.11.0", "", { "os": "darwin", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-zVK1QpHYJ4wfSd9tLkUHysRSYJZjkVHMfvYGjLyxLDCxe03GIVEjOjsMJ0DEgxLBV2czcIjjtXGEV83gtAbaMQ=="], - - "@cortexkit/aft-linux-arm64": ["@cortexkit/aft-linux-arm64@0.11.0", "", { "os": "linux", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-YyYGa12uJPN7R0Vx7LlPUPVOx2y0T/L8YUmlo2XyaFi0RAwGAW5VBBP8P8kPe1QzSrLapYELN8jsRVs+NRbCVw=="], - - "@cortexkit/aft-linux-x64": ["@cortexkit/aft-linux-x64@0.11.0", "", { "os": "linux", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-lKBLXPTK43NXJx4dZcE8WpEnyvzotOHNv/a4FN+Rb7iL9DkmMNnbXfN7sna7go7NLtudNR+WInSaxpEUU63zmA=="], + "@cortexkit/aft-linux-x64": ["@cortexkit/aft-linux-x64@0.12.2", "", { "os": "linux", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-RcAZRt6rKxtzl8jrUx3M2P9IQvtF4Ff1x9/z2cZcuTBRn7xGueJ+WPeAGj1R1osSeEbzTPt04wSdeiVOL1tZGg=="], "@cortexkit/aft-opencode": ["@cortexkit/aft-opencode@workspace:packages/opencode-plugin"], - "@cortexkit/aft-win32-x64": ["@cortexkit/aft-win32-x64@0.11.0", "", { "os": "win32", "cpu": "x64", "bin": { "aft": "bin/aft.exe" } }, "sha512-5d/gJEcZqvvx4HFz0nZDuGrTEHO4fbmWB8jHHjUc92iBdvYw7wW7mS++MHIHFKw9ByvTTcVKa5oYMJAtq3CTsg=="], + "@cortexkit/aft-win32-x64": ["@cortexkit/aft-win32-x64@0.12.2", "", { "os": "win32", "cpu": "x64", "bin": { "aft": "bin/aft.exe" } }, "sha512-PYtxd2N6uaVYWqC+hXJ9Aobx+RsMOT5zuEL98wE3HHv+z5ricFfy06b1OjuHfzqYetQj1WS7B1zNfQ/PNO41bA=="], "@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.26", "", { "dependencies": { "@opencode-ai/sdk": "1.2.26", "zod": "4.1.8" } }, "sha512-pC71KGAI9T0+S84KpbEq9THp5pT7KOq+GmfdXkvQ7KSH5zi+iASWRhqorir73sKmEj2MQfpbe1BxdcU5qbeOwA=="], @@ -92,14 +87,6 @@ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "fast-string-truncated-width": ["fast-string-truncated-width@1.2.1", "", {}, "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow=="], - - "fast-string-width": ["fast-string-width@1.1.0", "", { "dependencies": { "fast-string-truncated-width": "^1.2.0" } }, "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ=="], - - "fast-wrap-ansi": ["fast-wrap-ansi@0.1.6", "", { "dependencies": { "fast-string-width": "^1.1.0" } }, "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w=="], - - "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], diff --git a/packages/opencode-plugin/package.json b/packages/opencode-plugin/package.json index 28f86a1..71da7af 100644 --- a/packages/opencode-plugin/package.json +++ b/packages/opencode-plugin/package.json @@ -28,7 +28,6 @@ "prepublishOnly": "bun run build" }, "dependencies": { - "@clack/prompts": "^1.2.0", "@opencode-ai/plugin": "^1.2.26", "@opencode-ai/sdk": "^1.2.26", "comment-json": "^4.6.2", diff --git a/packages/opencode-plugin/src/cli/prompts.ts b/packages/opencode-plugin/src/cli/prompts.ts index 536e0d1..4f58dc0 100644 --- a/packages/opencode-plugin/src/cli/prompts.ts +++ b/packages/opencode-plugin/src/cli/prompts.ts @@ -1,47 +1,128 @@ -import { - confirm as clackConfirm, - text as clackText, - intro, - isCancel, - log, - note, - outro, - select, - spinner, -} from "@clack/prompts"; - -export { intro, log, note, outro, spinner }; - -function handleCancel(value: unknown): void { - if (isCancel(value)) { - log.warn("Setup cancelled."); - process.exit(0); +import { createInterface } from "node:readline/promises"; +import { stdin as input, stdout as output } from "node:process"; + +const isInteractive = Boolean(input.isTTY && output.isTTY); + +const ansi = { + reset: "\u001b[0m", + dim: "\u001b[2m", + cyan: "\u001b[36m", + green: "\u001b[32m", + yellow: "\u001b[33m", + red: "\u001b[31m", + bold: "\u001b[1m", +}; + +function color(text: string, code: string): string { + return isInteractive ? `${code}${text}${ansi.reset}` : text; +} + +function prefix(kind: string, code: string): string { + return color(kind, code); +} + +async function promptLine(message: string): Promise { + const rl = createInterface({ input, output }); + try { + return await rl.question(message); + } finally { + rl.close(); } } +export const log = { + info(message: string): void { + console.log(`${prefix("info", ansi.cyan)} ${message}`); + }, + success(message: string): void { + console.log(`${prefix("success", ansi.green)} ${message}`); + }, + warn(message: string): void { + console.warn(`${prefix("warn", ansi.yellow)} ${message}`); + }, + error(message: string): void { + console.error(`${prefix("error", ansi.red)} ${message}`); + }, +}; + +export function intro(message: string): void { + console.log(""); + console.log(color(message, `${ansi.bold}${ansi.cyan}`)); +} + +export function outro(message: string): void { + console.log(color(message, `${ansi.bold}${ansi.green}`)); +} + +export function note(message: string, title = "Note"): void { + console.log(`${color(title, `${ansi.bold}${ansi.dim}`)}\n${message}`); +} + +export function spinner() { + let active = false; + return { + start(message: string) { + active = true; + console.log(`${prefix("…", ansi.cyan)} ${message}`); + }, + stop(message: string) { + if (!active) { + console.log(`${prefix("done", ansi.green)} ${message}`); + return; + } + active = false; + console.log(`${prefix("done", ansi.green)} ${message}`); + }, + }; +} + export async function confirm(message: string, defaultYes = true): Promise { - const result = await clackConfirm({ - message, - initialValue: defaultYes, - }); - handleCancel(result); - return result as boolean; + if (!isInteractive) { + return defaultYes; + } + + const suffix = defaultYes ? " [Y/n] " : " [y/N] "; + const answer = (await promptLine(`${message}${suffix}`)).trim().toLowerCase(); + if (!answer) { + return defaultYes; + } + if (["y", "yes"].includes(answer)) { + return true; + } + if (["n", "no"].includes(answer)) { + return false; + } + log.warn("Unrecognized response, using default."); + return defaultYes; } export async function selectOne( message: string, options: { label: string; value: string; recommended?: boolean }[], ): Promise { - const result = await select({ - message, - options: options.map((option) => ({ - label: option.recommended ? `${option.label} (recommended)` : option.label, - value: option.value, - hint: option.recommended ? "recommended" : undefined, - })), + if (!options.length) { + throw new Error("selectOne requires at least one option"); + } + + if (!isInteractive) { + const recommended = options.find((option) => option.recommended); + return (recommended ?? options[0]).value; + } + + console.log(message); + options.forEach((option, index) => { + const marker = option.recommended ? " (recommended)" : ""; + console.log(` ${index + 1}. ${option.label}${marker}`); }); - handleCancel(result); - return result as string; + + while (true) { + const answer = (await promptLine("Select an option: ")).trim(); + const choice = Number.parseInt(answer, 10); + if (Number.isFinite(choice) && choice >= 1 && choice <= options.length) { + return options[choice - 1].value; + } + log.warn("Please enter a valid option number."); + } } export async function text( @@ -52,17 +133,28 @@ export async function text( validate?: (value: string) => string | Error | undefined; } = {}, ): Promise { - const promptOptions: Parameters[0] = { - message, - ...(options.placeholder ? { placeholder: options.placeholder } : {}), - ...(options.defaultValue !== undefined ? { defaultValue: options.defaultValue } : {}), - ...(options.validate - ? { - validate: (value) => options.validate?.(value ?? ""), - } - : {}), - }; - const result = await clackText(promptOptions); - handleCancel(result); - return result as string; + const defaultValue = options.defaultValue ?? ""; + if (!isInteractive) { + const validation = options.validate?.(defaultValue); + if (validation) { + throw new Error(validation); + } + return defaultValue; + } + + const hint = options.placeholder + ? ` (${options.placeholder})` + : defaultValue + ? ` [default: ${defaultValue}]` + : ""; + + while (true) { + const answer = await promptLine(`${message}${hint}: `); + const value = answer === "" ? defaultValue : answer; + const validation = options.validate?.(value); + if (!validation) { + return value; + } + log.warn(validation); + } } From 07cefc0f5090032fd67fa8d50bbe6292b6843c0b Mon Sep 17 00:00:00 2001 From: freelanceagent1 <217720360+freelanceagent1@users.noreply.github.com> Date: Thu, 16 Apr 2026 09:11:16 -0700 Subject: [PATCH 2/2] fix(cli): normalize prompt validator errors --- bun.lock | 22 ++++++++++----------- packages/opencode-plugin/src/cli/prompts.ts | 11 +++++++++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/bun.lock b/bun.lock index 91d7f19..bab2b28 100644 --- a/bun.lock +++ b/bun.lock @@ -16,7 +16,7 @@ }, "packages/opencode-plugin": { "name": "@cortexkit/aft-opencode", - "version": "0.12.2", + "version": "0.13.0", "bin": { "aft-opencode": "dist/cli.js", }, @@ -31,11 +31,11 @@ "typescript": "^5.8.0", }, "optionalDependencies": { - "@cortexkit/aft-darwin-arm64": "0.12.2", - "@cortexkit/aft-darwin-x64": "0.12.2", - "@cortexkit/aft-linux-arm64": "0.12.2", - "@cortexkit/aft-linux-x64": "0.12.2", - "@cortexkit/aft-win32-x64": "0.12.2", + "@cortexkit/aft-darwin-arm64": "0.13.0", + "@cortexkit/aft-darwin-x64": "0.13.0", + "@cortexkit/aft-linux-arm64": "0.13.0", + "@cortexkit/aft-linux-x64": "0.13.0", + "@cortexkit/aft-win32-x64": "0.13.0", }, "peerDependencies": { "@opencode-ai/plugin": ">=1.2.0", @@ -61,17 +61,17 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ=="], - "@cortexkit/aft-darwin-arm64": ["@cortexkit/aft-darwin-arm64@0.12.2", "", { "os": "darwin", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-r2nPUFQkYjuXLJ2ejgvuia+a+XXJdA6DhstgpOQZ9KY7I41GDGsIdnABflNEzlmFCbR5KqkA17uNZ1yPAeeazg=="], + "@cortexkit/aft-darwin-arm64": ["@cortexkit/aft-darwin-arm64@0.13.0", "", { "os": "darwin", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-e+7bk2+LECkn4zZ0JPBHYf8dCVR36JskLdWZ0Xh1LjPTpPu6eT6ZS3SuTVbdpbQTTTjegRI1mVfnwm5dvIKEcg=="], - "@cortexkit/aft-darwin-x64": ["@cortexkit/aft-darwin-x64@0.12.2", "", { "os": "darwin", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-V9RoQf8XP+GljDNOvd9ptNoo5fjl6cFD5+/+5HuHsejNY+NzBJCQ+VDUgiNWxcyUzRd3I9gCK7wOhIH/HBsavw=="], + "@cortexkit/aft-darwin-x64": ["@cortexkit/aft-darwin-x64@0.13.0", "", { "os": "darwin", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-vmd7QtygrCDYcPt3wHH+LdraPxhn6WWVXbS0FxMBrhXblfLt7MkyBytpvWjkgmJpQZYgrIquFtIHx5YJ3D8raQ=="], - "@cortexkit/aft-linux-arm64": ["@cortexkit/aft-linux-arm64@0.12.2", "", { "os": "linux", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-ZiNUWaHHZei+DPSqzrGrlf2iCR1IwQo0acKsZmvckh5j4Cb5i5ERpt1HeW7ClxwgbEZnNZ5br83O/ciBCfihJA=="], + "@cortexkit/aft-linux-arm64": ["@cortexkit/aft-linux-arm64@0.13.0", "", { "os": "linux", "cpu": "arm64", "bin": { "aft": "bin/aft" } }, "sha512-7ADp8QZjXJqGvTh9tV8RL3YmgVH97/F3QqD4m/XBJINvx3GiCowR2NqDbdkwg4dX38b1QFbeEcQFuIxev0RVtw=="], - "@cortexkit/aft-linux-x64": ["@cortexkit/aft-linux-x64@0.12.2", "", { "os": "linux", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-RcAZRt6rKxtzl8jrUx3M2P9IQvtF4Ff1x9/z2cZcuTBRn7xGueJ+WPeAGj1R1osSeEbzTPt04wSdeiVOL1tZGg=="], + "@cortexkit/aft-linux-x64": ["@cortexkit/aft-linux-x64@0.13.0", "", { "os": "linux", "cpu": "x64", "bin": { "aft": "bin/aft" } }, "sha512-egpmnk5W3/p+qLUmpmZ1jnk2QS8pMA2eBXa0CtN9uKunwRsw544VgXABtjQmj5kYkQRtAjGO0s/5pwkYeMqO1Q=="], "@cortexkit/aft-opencode": ["@cortexkit/aft-opencode@workspace:packages/opencode-plugin"], - "@cortexkit/aft-win32-x64": ["@cortexkit/aft-win32-x64@0.12.2", "", { "os": "win32", "cpu": "x64", "bin": { "aft": "bin/aft.exe" } }, "sha512-PYtxd2N6uaVYWqC+hXJ9Aobx+RsMOT5zuEL98wE3HHv+z5ricFfy06b1OjuHfzqYetQj1WS7B1zNfQ/PNO41bA=="], + "@cortexkit/aft-win32-x64": ["@cortexkit/aft-win32-x64@0.13.0", "", { "os": "win32", "cpu": "x64", "bin": { "aft": "bin/aft.exe" } }, "sha512-8bDXSIhWngcNGUSOpa5dx3mmhszjTdtU+CuxWFxlltcDTHcLrUEkvCybJH/4QHXbf1jZIDaADv8EauP7WqxfLA=="], "@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.26", "", { "dependencies": { "@opencode-ai/sdk": "1.2.26", "zod": "4.1.8" } }, "sha512-pC71KGAI9T0+S84KpbEq9THp5pT7KOq+GmfdXkvQ7KSH5zi+iASWRhqorir73sKmEj2MQfpbe1BxdcU5qbeOwA=="], diff --git a/packages/opencode-plugin/src/cli/prompts.ts b/packages/opencode-plugin/src/cli/prompts.ts index 4f58dc0..1fb3caf 100644 --- a/packages/opencode-plugin/src/cli/prompts.ts +++ b/packages/opencode-plugin/src/cli/prompts.ts @@ -21,6 +21,13 @@ function prefix(kind: string, code: string): string { return color(kind, code); } +function normalizeValidationMessage(value: string | Error | undefined): string | undefined { + if (!value) { + return undefined; + } + return value instanceof Error ? value.message : value; +} + async function promptLine(message: string): Promise { const rl = createInterface({ input, output }); try { @@ -135,7 +142,7 @@ export async function text( ): Promise { const defaultValue = options.defaultValue ?? ""; if (!isInteractive) { - const validation = options.validate?.(defaultValue); + const validation = normalizeValidationMessage(options.validate?.(defaultValue)); if (validation) { throw new Error(validation); } @@ -151,7 +158,7 @@ export async function text( while (true) { const answer = await promptLine(`${message}${hint}: `); const value = answer === "" ? defaultValue : answer; - const validation = options.validate?.(value); + const validation = normalizeValidationMessage(options.validate?.(value)); if (!validation) { return value; }