fix(cli): a confirm-gated command without a TTY says why it stopped (bug7)

`peer kick <pk>` from a script printed a bare «cancelled.» and looked
like it ran and did nothing. Without a TTY the gate now explains that it
needs confirmation and how to give it (--yes, --approval-mode yolo, or a
policy rule), audits it as cancelled-no-tty, and still exits
PERMISSION_DENIED. kick/disconnect/ban help lines show [-y].

Spec: .artifacts/specs/2026-09-26-session-identity-and-routing.md §6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-09-26 21:05:09 +01:00
parent 3fab89c61a
commit f66f085a41
2 changed files with 13 additions and 3 deletions

View File

@@ -84,9 +84,9 @@ Mesh (alias: "workspace" — claudemesh workspace <verb> mirrors each)
Peer (resource form, recommended)
claudemesh peer list see who's online (alias: peers)
claudemesh peer kick <p> end session (alias: kick)
claudemesh peer disconnect <p> soft disconnect (alias: disconnect)
claudemesh peer ban <p> ban + revoke (alias: ban)
claudemesh peer kick <p> [-y] end session (alias: kick)
claudemesh peer disconnect <p> [-y] soft disconnect (alias: disconnect)
claudemesh peer ban <p> [-y] ban + revoke (alias: ban)
claudemesh peer unban <p> lift a ban (alias: unban)
claudemesh peer bans list banned members (alias: bans)
claudemesh peer verify [p] safety numbers (alias: verify)

View File

@@ -314,6 +314,16 @@ export async function gate(ctx: CheckContext, opts?: { policyPath?: string }): P
}
// prompt
if (ctx.yes) return true;
// bug7 (2026-09-26): with no TTY there's no one to ask. It used to print a
// bare "cancelled." — which read like the command ran and did nothing.
if (!process.stdin.isTTY || !process.stdout.isTTY) {
process.stderr.write(
`\n ✘ ${ctx.resource} ${ctx.verb} needs confirmation and there's no terminal to ask on.` +
`\n re-run with --yes (or --approval-mode yolo, or a \`decision: allow\` rule in ${USER_POLICY_PATH}).\n`,
);
audit({ ...ctx, decision: "cancelled-no-tty" });
return false;
}
const reason = result.reason ? ` — ${result.reason}` : "";
const confirmed = await confirmPrompt(
`\n ⚠ ${ctx.resource} ${ctx.verb}${reason}. Continue?`,