@kya-os/cli
Version:
CLI for MCP-I setup and management
107 lines • 4.52 kB
JavaScript
import chalk from "chalk";
import { Command } from "commander";
import { renderSetupResult } from "../utils/dco/render.js";
import { runDcoSetup } from "../utils/dco/setup.js";
import { collectDcoStatus } from "../utils/dco/status.js";
export async function dcoSetup(options = {}) {
if (!options.json) {
console.log(chalk.cyan("\nSetting up DCO sign-off, commit signing, and PR attribution\n"));
}
const setupOptions = {
global: options.global,
dryRun: options.dryRun,
skipGithub: options.skipGithub,
skipClaude: options.skipClaude,
force: options.force,
interactive: options.json || options.yes ? false : undefined,
};
let result;
try {
result = await runDcoSetup(setupOptions);
}
catch (error) {
// Belt and braces: runDcoSetup reports failures as step results, but no
// escaping rejection may take down the process without a message.
const message = error instanceof Error ? error.message : String(error);
if (options.json) {
console.log(JSON.stringify({ success: false, error: message }, null, 2));
}
else {
console.error(chalk.red(`\nSetup failed unexpectedly: ${message}\n`));
}
// v8 ignore: process.exit is unreachable under vitest by design (the
// repo-mandated test-environment guard) and must stay that way.
/* v8 ignore start */
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
process.exit(1);
}
/* v8 ignore stop */
return;
}
if (options.json) {
console.log(JSON.stringify({
success: result.ok,
actionRequired: result.actionRequired,
steps: result.steps,
agent: result.agent,
human: result.human,
privateKeyPath: result.privateKeyPath,
fingerprint: result.fingerprint,
// Surfaced to automation too: without it a caller only sees a
// successful identities step and may assume no later re-prompt.
metadataPersistWarning: result.metadataPersistWarning,
}, null, 2));
}
else {
renderSetupResult(result);
}
// v8 ignore: same test-environment guard as above.
/* v8 ignore start */
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
process.exit(result.ok ? (result.actionRequired ? 2 : 0) : 1);
}
/* v8 ignore stop */
}
export async function dcoStatus(options = {}) {
const result = await collectDcoStatus(process.cwd());
if (options.json) {
console.log(JSON.stringify(result, null, 2));
return;
}
console.log(chalk.cyan("\nDCO / attribution status\n"));
const width = Math.max(...result.checks.map((c) => c.label.length));
for (const check of result.checks) {
const state = check.state === "ok"
? chalk.green("ok")
: check.state === "warn"
? chalk.yellow("warn")
: chalk.red("missing");
console.log(` ${check.label.padEnd(width)} ${state}`);
if (check.detail) {
console.log(` ${" ".repeat(width)} ${chalk.gray(check.detail)}`);
}
}
console.log("");
}
export function createDcoCommand() {
const dco = new Command("dco").description("Set up DCO sign-off, commit signing, and PR attribution for your agent");
dco
.command("setup")
.description("Sign commits with your agent's identity key and attribute PRs to your agent. " +
"Exit codes: 0 = complete, 1 = a step failed, 2 = complete but user action pending.")
.option("--global", "Write git config globally instead of repo-local")
.option("--dry-run", "Show what would change without writing anything")
.option("--json", "Output in JSON format for automation")
.option("--skip-github", "Skip uploading the signing key to GitHub")
.option("--skip-claude", "Skip Claude Code attribution configuration")
.option("--force", "Overwrite conflicting existing signing configuration")
.option("-y, --yes", "Run non-interactively, never prompt")
.action((options) => dcoSetup(options));
dco
.command("status")
.description("Show the current DCO/attribution setup state")
.option("--json", "Output in JSON format for automation")
.action((options) => dcoStatus(options));
return dco;
}
//# sourceMappingURL=dco.js.map