Files
oh-my-opencode/src/cli/cli-installer.ts
T

174 lines
5.9 KiB
TypeScript

import color from "picocolors"
import { PLUGIN_NAME, PUBLISHED_PACKAGE_NAME } from "../shared"
import type { InstallArgs } from "./types"
import {
addPluginToOpenCodeConfig,
detectCurrentConfig,
getOpenCodeVersion,
isOpenCodeInstalled,
writeOmoConfig,
} from "./config-manager"
import {
SYMBOLS,
argsToConfig,
detectedToInitialValues,
formatConfigSummary,
printBox,
printError,
printHeader,
printInfo,
printStep,
printSuccess,
printWarning,
validateNonTuiArgs,
} from "./install-validators"
import { getUnsupportedOpenCodeVersionMessage } from "./minimum-opencode-version"
import { createCliPostHog, getPostHogDistinctId } from "../shared/posthog"
export async function runCliInstaller(args: InstallArgs, version: string): Promise<number> {
const posthog = createCliPostHog()
const distinctId = getPostHogDistinctId()
const validation = validateNonTuiArgs(args)
if (!validation.valid) {
printHeader(false)
printError("Validation failed:")
for (const err of validation.errors) {
console.log(` ${SYMBOLS.bullet} ${err}`)
}
console.log()
printInfo(
`Usage: bunx ${PUBLISHED_PACKAGE_NAME} install --no-tui --claude=<no|yes|max20> --gemini=<no|yes> --copilot=<no|yes>`,
)
console.log()
return 1
}
const detected = detectCurrentConfig()
const isUpdate = detected.isInstalled
printHeader(isUpdate)
const totalSteps = 4
let step = 1
printStep(step++, totalSteps, "Checking OpenCode installation...")
const installed = await isOpenCodeInstalled()
const openCodeVersion = await getOpenCodeVersion()
if (!installed) {
printWarning(
"OpenCode binary not found. Plugin will be configured, but you'll need to install OpenCode to use it.",
)
printInfo("Visit https://opencode.ai/docs for installation instructions")
} else {
printSuccess(`OpenCode ${openCodeVersion ?? ""} detected`)
const unsupportedVersionMessage = getUnsupportedOpenCodeVersionMessage(openCodeVersion)
if (unsupportedVersionMessage) {
printWarning(unsupportedVersionMessage)
posthog.capture({ distinctId, event: "install_failed", properties: { command: "install", reason: "unsupported_opencode_version", is_update: isUpdate } })
await posthog.shutdown()
return 1
}
}
if (isUpdate) {
const initial = detectedToInitialValues(detected)
printInfo(`Current config: Claude=${initial.claude}, Gemini=${initial.gemini}`)
}
const config = argsToConfig(args)
printStep(step++, totalSteps, `Adding ${PLUGIN_NAME} plugin...`)
const pluginResult = await addPluginToOpenCodeConfig(version)
if (!pluginResult.success) {
printError(`Failed: ${pluginResult.error}`)
posthog.capture({ distinctId, event: "install_failed", properties: { command: "install", reason: "plugin_config_write_failed", is_update: isUpdate } })
await posthog.shutdown()
return 1
}
printSuccess(
`Plugin ${isUpdate ? "verified" : "added"} ${SYMBOLS.arrow} ${color.dim(pluginResult.configPath)}`,
)
printStep(step++, totalSteps, `Writing ${PLUGIN_NAME} configuration...`)
const omoResult = writeOmoConfig(config)
if (!omoResult.success) {
printError(`Failed: ${omoResult.error}`)
posthog.capture({ distinctId, event: "install_failed", properties: { command: "install", reason: "omo_config_write_failed", is_update: isUpdate } })
await posthog.shutdown()
return 1
}
printSuccess(`Config written ${SYMBOLS.arrow} ${color.dim(omoResult.configPath)}`)
printBox(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete")
if (!config.hasClaude) {
printInfo(
"Note: Sisyphus agent performs best with Claude Opus 4.5+. " +
"Other models work but may have reduced orchestration quality.",
)
}
if (
!config.hasClaude &&
!config.hasOpenAI &&
!config.hasGemini &&
!config.hasCopilot &&
!config.hasOpencodeZen
) {
printWarning("No model providers configured. Using opencode/big-pickle as fallback.")
}
console.log(`${SYMBOLS.star} ${color.bold(color.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`)
console.log(` Run ${color.cyan("opencode")} to start!`)
console.log()
printInfo(
"Anonymous telemetry is enabled by default. Disable it with OMO_SEND_ANONYMOUS_TELEMETRY=0 or OMO_DISABLE_POSTHOG=1.",
)
printInfo("Docs: docs/legal/privacy-policy.md and docs/legal/terms-of-service.md")
console.log()
printBox(
`${color.bold("Pro Tip:")} Include ${color.cyan("ultrawork")} (or ${color.cyan("ulw")}) in your prompt.\n` +
`All features work like magic-parallel agents, background tasks,\n` +
`deep exploration, and relentless execution until completion.`,
"The Magic Word",
)
console.log(`${SYMBOLS.star} ${color.yellow("If you found this helpful, consider starring the repo!")}`)
console.log(
` ${color.dim("gh api --silent --method PUT /user/starred/code-yeongyu/oh-my-openagent >/dev/null 2>&1 || true")}`,
)
console.log()
console.log(color.dim("oMoMoMoMo... Enjoy!"))
console.log()
posthog.capture({
distinctId,
event: "install_completed",
properties: {
command: "install",
is_update: isUpdate,
has_claude: config.hasClaude,
has_openai: config.hasOpenAI,
has_gemini: config.hasGemini,
has_copilot: config.hasCopilot,
has_opencode_zen: config.hasOpencodeZen,
},
})
await posthog.shutdown()
if ((config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) {
printBox(
`Run ${color.cyan("opencode auth login")} and select your provider:\n` +
(config.hasClaude ? ` ${SYMBOLS.bullet} Anthropic ${color.gray("→ Claude Pro/Max")}\n` : "") +
(config.hasGemini ? ` ${SYMBOLS.bullet} Google ${color.gray("→ Gemini")}\n` : "") +
(config.hasCopilot ? ` ${SYMBOLS.bullet} GitHub ${color.gray("→ Copilot")}` : ""),
"Authenticate Your Providers",
)
}
return 0
}