@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
106 lines (103 loc) • 4.38 kB
JavaScript
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { AGENT_RULES_DIR, detectAgentRules, LUNORA_SKILL_NAMES } from '@lunora/config';
import { join, relative, dirname } from '@visulima/path';
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
const resolveBundledSkillsDirectory = (startDirectory = dirname(fileURLToPath(import.meta.url))) => {
let directory = startDirectory;
for (let index = 0; index < 6; index += 1) {
const packageJson = join(directory, "package.json");
if (existsSync(packageJson)) {
try {
const parsed = JSON.parse(readFileSync(packageJson, "utf8"));
if (parsed.name === "@lunora/cli") {
const skills = join(directory, "skills");
return existsSync(skills) ? skills : void 0;
}
} catch {
}
}
const parent = dirname(directory);
if (parent === directory) {
break;
}
directory = parent;
}
return void 0;
};
const listBundledSkills = (skillsDirectory) => readdirSync(skillsDirectory).filter((name) => {
const directory = join(skillsDirectory, name);
return statSync(directory).isDirectory() && existsSync(join(directory, "SKILL.md"));
});
const copySkill = (source, destination, overwrite) => {
mkdirSync(destination, { recursive: true });
let wrote = false;
for (const entry of readdirSync(source)) {
const from = join(source, entry);
const to = join(destination, entry);
if (statSync(from).isDirectory()) {
wrote = copySkill(from, to, overwrite) || wrote;
continue;
}
if (existsSync(to) && !overwrite) {
continue;
}
writeFileSync(to, readFileSync(from));
wrote = true;
}
return wrote;
};
const runRulesInstall = (options) => {
const cwd = options.cwd ?? process.cwd();
const overwrite = options.overwrite === true;
const skillsDirectory = resolveBundledSkillsDirectory();
if (skillsDirectory === void 0) {
options.logger.error("rules: could not locate the bundled skills (is @lunora/cli installed correctly?).");
return { code: 1, installed: [], skipped: [] };
}
const installed = [];
const skipped = [];
for (const name of listBundledSkills(skillsDirectory)) {
const destination = join(cwd, AGENT_RULES_DIR, name);
const wrote = copySkill(join(skillsDirectory, name), destination, overwrite);
if (wrote) {
installed.push(name);
} else {
skipped.push(name);
}
}
const target = relative(cwd, join(cwd, AGENT_RULES_DIR)) || AGENT_RULES_DIR;
if (installed.length > 0) {
options.logger.success(`Installed ${String(installed.length)} Lunora skill(s) into ${target}/: ${installed.join(", ")}.`);
}
if (skipped.length > 0) {
options.logger.info(`Skipped ${String(skipped.length)} existing skill(s) (re-run with --overwrite to replace): ${skipped.join(", ")}.`);
}
options.logger.info("Your AI coding agent will pick these up automatically. Start with the `lunora` skill.");
return { code: 0, installed, skipped };
};
const runRulesCheck = (options) => {
const cwd = options.cwd ?? process.cwd();
const status = detectAgentRules(cwd);
if (status.installed) {
options.logger.success(`Lunora agent rules are installed (${String(status.present.length)}/${String(LUNORA_SKILL_NAMES.length)} skills).`);
if (status.missing.length > 0) {
options.logger.info(`Missing: ${status.missing.join(", ")}. Run \`lunora rules install\` to add them.`);
}
return { code: 0, installed: status.present, skipped: [] };
}
options.logger.warn("Lunora agent rules are not installed. Run `lunora rules install` so your AI agent knows how to use Lunora.");
return { code: options.strict === true ? 1 : 0, installed: status.present, skipped: [] };
};
const execute = defineHandler(({ argument, cwd, logger, options }) => {
const subcommand = argument[0] ?? "check";
if (subcommand === "install") {
return runRulesInstall({ cwd, logger, overwrite: options.overwrite === true });
}
if (subcommand === "check") {
return runRulesCheck({ cwd, logger, strict: options.strict === true });
}
logger.error("rules: unknown subcommand. Usage: lunora rules <install|check>");
return { code: 1 };
});
export { execute, resolveBundledSkillsDirectory, runRulesCheck, runRulesInstall };