feat(installer): improve cursor and opencode skill handling

This commit is contained in:
Stefano Fiorini
2026-04-24 02:20:06 -05:00
parent d62899308a
commit 193cd45db8
30 changed files with 3832 additions and 64 deletions
+32 -3
View File
@@ -8,6 +8,7 @@ import {
CLIENTS,
SKILLS,
buildOperationPlan,
detectHelperInstallState,
detectInstalledClients,
detectInstalledSkills,
executeOperation,
@@ -42,7 +43,8 @@ Answers JSON example:
{
"clientId": "codex",
"scope": "global",
"actions": { "create-plan": "install", "web-automation": "skip" }
"actions": { "create-plan": "install", "web-automation": "skip" },
"helperActions": { "reviewer-runtime": "skip" }
}
]
}
@@ -168,7 +170,32 @@ async function interactiveAnswers({ dryRun = false } = {}) {
actions[skill] = chosen;
}
}
selections.push({ clientId, scope, actions });
const helperActions = {};
const workflowNeedsReviewerRuntime = Object.entries(actions).some(([skill, action]) => (
["install", "update", "reinstall"].includes(action) && SKILLS[skill]?.requiresReviewerRuntime
));
const helperTarget = reviewerRuntimeRoot(clientId, scopeInfo.skillsRoot, process.cwd());
const helperState = await detectHelperInstallState({
source: path.join(process.cwd(), CLIENTS[clientId].reviewerRuntime.source),
target: helperTarget,
files: CLIENTS[clientId].reviewerRuntime.files,
});
if (workflowNeedsReviewerRuntime || helperState !== "not-installed") {
const choices = helperState === "not-installed" ? "install/skip" : "update/reinstall/remove/skip";
let defaultAction = "skip";
if (workflowNeedsReviewerRuntime && helperState === "not-installed") defaultAction = "install";
if (workflowNeedsReviewerRuntime && ["stale", "unknown"].includes(helperState)) defaultAction = "update";
const answer = await rl.question(`${clientId}/${scope}/reviewer-runtime is ${helperState}; action (${choices}) [${defaultAction}]: `);
const chosen = answer.trim() || defaultAction;
const allowed = choices.split("/");
if (!allowed.includes(chosen)) {
console.log(`Invalid action '${chosen}', using skip.`);
helperActions["reviewer-runtime"] = "skip";
} else {
helperActions["reviewer-runtime"] = chosen;
}
}
selections.push({ clientId, scope, actions, helperActions });
}
return { selections };
} finally {
@@ -212,6 +239,8 @@ async function main() {
}
}
if (plan.operations.length === 0) return;
if (args.dryRun) {
console.log("\nDry-run mode: no filesystem changes performed.");
return;
@@ -275,7 +304,7 @@ async function main() {
const rows = results.map((op) => ({
client: op.clientId,
scope: op.scope,
item: op.skill || op.helper || op.kind,
item: op.item || op.skill || op.helper || op.kind,
action: op.displayAction || op.action,
status: op.status,
details: op.details || op.target || "",