fix(plugins): retry claude plugin install|update without -y on an old CLI
An older Claude Code has no -y on `plugin install|update` and exits with "error: unknown option '-y'", so the dashboard's Update now chain failed at its plugin_update step (and so did Update all, the plugin buttons and the superpowers auto-install). Those versions also predate the confirmation prompt -y answers, so every wrapper now retries the same argv without it when, and only when, the CLI rejects the flag. /agenthub-update, which the failure message points users to, falls back the same way.
This commit is contained in:
@@ -116,6 +116,22 @@ describe("cc-plugin-cli argv construction", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("retries without -y on a Claude Code too old to know the flag", async () => {
|
||||
// Older CLIs answer `error: unknown option '-y'` and exit 1; they also
|
||||
// never prompt, so the same command without the flag is the right retry.
|
||||
const restore = stubClaude(
|
||||
'for a in "$@"; do [ "$a" = "-y" ] && { echo "error: unknown option \'-y\'" >&2; exit 1; }; done\n' +
|
||||
'for a in "$@"; do echo "$a"; done'
|
||||
);
|
||||
try {
|
||||
const r = await cli.pluginUpdate({ spec: "demo@m" });
|
||||
assert.equal(r.ok, true);
|
||||
assert.deepEqual(argvOf(r), ["plugin", "update", "demo@m"]);
|
||||
} finally {
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
it("passes -y on install so a non-TTY run never waits for a prompt", async () => {
|
||||
const restore = stubClaude();
|
||||
try {
|
||||
|
||||
@@ -130,7 +130,23 @@ function serialize(fn) {
|
||||
* @param {{timeoutMs?:number}} [opts]
|
||||
* @returns {Promise<{ok:boolean, code:number|null, stdout:string, stderr:string, args:string[]}>}
|
||||
*/
|
||||
function runClaude(args, opts = {}) {
|
||||
// A Claude Code older than `plugin install|update -y` rejects the flag outright.
|
||||
// It also predates the confirmation prompt the flag answers, so the same
|
||||
// command without it is the correct retry, not a weaker one.
|
||||
const NO_YES_FLAG = /unknown option '-y'/;
|
||||
|
||||
async function runClaude(args, opts = {}) {
|
||||
const result = await runClaudeOnce(args, opts);
|
||||
if (!result.ok && args.includes("-y") && NO_YES_FLAG.test(result.stderr)) {
|
||||
return runClaudeOnce(
|
||||
args.filter((a) => a !== "-y"),
|
||||
opts
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function runClaudeOnce(args, opts = {}) {
|
||||
const timeout = opts.timeoutMs || DEFAULT_TIMEOUT_MS;
|
||||
return new Promise((resolve, reject) => {
|
||||
// Only pin CLAUDE_CONFIG_DIR when the operator explicitly set CLAUDE_HOME
|
||||
|
||||
@@ -128,6 +128,18 @@ function missingSkillPlugins() {
|
||||
* @returns {{ok: boolean, output: string}}
|
||||
*/
|
||||
function runClaudePlugin(args, timeoutMs) {
|
||||
const result = runClaudePluginOnce(args, timeoutMs);
|
||||
// An older Claude Code rejects `-y` and never prompts; retry without it.
|
||||
if (!result.ok && args.includes("-y") && /unknown option '-y'/.test(result.output)) {
|
||||
return runClaudePluginOnce(
|
||||
args.filter((a) => a !== "-y"),
|
||||
timeoutMs
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function runClaudePluginOnce(args, timeoutMs) {
|
||||
const env = { ...process.env };
|
||||
if (process.env.CLAUDE_HOME) env.CLAUDE_CONFIG_DIR = getClaudeHome();
|
||||
const result = spawnSync("claude", args, {
|
||||
|
||||
Reference in New Issue
Block a user