import type { Command } from 'commander';
import { resolve } from 'path';
import { homedir } from 'os';
import { spawnSync } from 'child_process';
import { writeFileSync } from 'fs';
import { createHash } from 'crypto';
import { auditLogger } from '../audit/index.js';
import { queryAuditLogs } from '../audit/export.js';
import type { AuditEvent } from '../audit/types.js';
import type { Config } from '../config/schema.js';
import type { Skill } from '../skills/index.js';
import { loadAllSkills, SkillInstaller, buildInstallerPlan, loadSkill } from '../skills/index.js';
import type { SkillPermissions } from '../skills/types.js';
import { loadConfigSafe } from './shared.js';

export interface SkillListRow {
  name: string;
  tier: Skill['manifest']['tier'];
  status: 'available' | 'unavailable';
  reason?: string;
}

export interface SkillRefreshSummary {
  total: number;
  available: number;
  unavailable: number;
  tiers: Record<Skill['manifest']['tier'], number>;
}

export interface SkillInstallerPlanView {
  skill: {
    name: string;
    tier: Skill['manifest']['tier'];
    version: string;
    permissions?: SkillPermissions;
  };
  mode: 'dry-run';
  steps: Array<{ installerType: string; command: string }>;
  skipped: Array<{ installerType: string; reason: string }>;
}

export interface SkillInstallPreflightView {
  sourcePath: string;
  skill: SkillInstallerPlanView['skill'];
  mode: 'dry-run';
  steps: SkillInstallerPlanView['steps'];
  skipped: SkillInstallerPlanView['skipped'];
}

function renderSkillPermissions(perms?: SkillPermissions): string[] {
  if (!perms) {
    return [];
  }

  const lines: string[] = ['Permissions:'];
  if (perms.tool_groups && perms.tool_groups.length > 0) {
    lines.push(`- tool_groups: ${perms.tool_groups.join(', ')}`);
  }
  if (perms.tools && perms.tools.length > 0) {
    lines.push(`- tools: ${perms.tools.join(', ')}`);
  }
  if (perms.fs?.read && perms.fs.read.length > 0) {
    lines.push(`- fs.read: ${perms.fs.read.join(', ')}`);
  }
  if (perms.fs?.write && perms.fs.write.length > 0) {
    lines.push(`- fs.write: ${perms.fs.write.join(', ')}`);
  }
  if (perms.net && perms.net.length > 0) {
    lines.push(`- net: ${perms.net.map((n) => `${n.host}${n.ports && n.ports.length > 0 ? `:${n.ports.join('|')}` : ''}`).join(', ')}`);
  }
  if (perms.secrets && perms.secrets.length > 0) {
    lines.push(`- secrets: ${perms.secrets.join(', ')}`);
  }
  if (perms.execution_environment) {
    lines.push(`- execution_environment: ${perms.execution_environment}`);
  }
  if (lines.length === 1) {
    return ['Permissions: (none declared)'];
  }
  return lines;
}

export interface SkillInstallerExecutionStubView {
  skill: SkillInstallerPlanView['skill'];
  execution: 'stub';
  mode: SkillInstallActionMode;
  confirmed: boolean;
  execution_enabled: boolean;
  executed: string[];
  reason: SkillInstallerExecutionReason;
  attempted: Array<{ installer_type: string; command: string }>;
  results: Array<{ installer_type: string; command: string; status: SkillInstallerStepStatus; reason: string }>;
  wouldRun: string[];
  skipped: SkillInstallerPlanView['skipped'];
}

export type SkillInstallActionMode = 'plan-only' | 'stub' | 'install';
export type SkillInstallerExecutionReason =
  | 'execution_disabled'
  | 'confirmation_required'
  | 'execution_enabled'
  | 'execution_policy_disabled';
export type SkillInstallerStepStatus = 'blocked' | 'skipped' | 'succeeded' | 'failed';
export type SkillInstallerRunnerMode = 'noop' | 'shell';

export interface SkillInstallerExecutionPolicy {
  confirmed: boolean;
  execution_enabled: boolean;
  reason: SkillInstallerExecutionReason;
}

export interface SkillInstallerCommandRunner {
  run(commands: string[]): SkillInstallerCommandRunResult[];
}

export interface SkillInstallerCommandRunResult {
  command: string;
  status: 'succeeded' | 'failed';
  reason?: string;
}

export function hashSkillInstallerAuditCommand(command: string): string {
  const digest = createHash('sha256').update(command).digest('hex');
  return `sha256:${digest}`;
}

export function sanitizeSkillInstallerAuditReason(reason: string): string {
  if (reason.startsWith('spawn_error:')) {
    return 'spawn_error';
  }
  return reason;
}

export interface ShellRunnerRolloutGuardrailStatus {
  blockers: string[];
}

export interface ShellRunnerAuditWindowSummary {
  command_result_total: number;
  command_result_failed: number;
  allowlist_blocked: number;
  execution_blocked: number;
  hashed_command_count: number;
  unhashed_command_count: number;
}

export interface ShellRunnerAuditTrendSnapshot {
  current: ShellRunnerAuditWindowSummary;
  previous: ShellRunnerAuditWindowSummary;
  deltas: {
    failures: number;
    allowlist_blocks: number;
    hash_coverage_pct: number;
  };
}

export interface ShellRunnerPromotionPolicyStatus {
  eligible: boolean;
  recommendation: 'eligible' | 'not_eligible';
  cadence_days: number;
  reviewed_window_days: number;
  success_rate: number;
  minimum_success_rate: number;
  failures_delta: number;
  allowlist_blocks_delta: number;
  hash_coverage_delta_pct: number;
  blockers: string[];
}

export interface ShellRunnerPromotionContract {
  schema: 'skills.rollout.promotion_contract.v1';
  generated_at: string;
  window_days: number;
  gate: {
    status: 'pass' | 'fail';
    exit_code: 0 | 1;
    reason: 'promotion_eligible' | 'promotion_not_eligible';
    blockers: string[];
  };
  recommendation: ShellRunnerRolloutRecommendation;
  governance: {
    owner: string | null;
    review_cadence_days: number;
    promotion_min_success_rate: number;
  };
  summary: {
    command_result_total: number;
    command_result_failed: number;
    allowlist_blocked: number;
    hash_coverage_pct: number;
  };
  promotion_policy: ShellRunnerPromotionPolicyStatus;
  trend: ShellRunnerAuditTrendSnapshot['deltas'];
}

export type ShellRunnerRolloutRecommendation = 'locked' | 'guarded_observe' | 'guarded_review' | 'expand_candidate';

export function evaluateShellRunnerRolloutGuardrails(
  skillsConfig: Config['skills'],
  auditEnabled: boolean,
): ShellRunnerRolloutGuardrailStatus {
  const blockers: string[] = [];

  if (skillsConfig.installation_execution !== 'enabled') {
    blockers.push('skills.installation_execution must be enabled');
  }

  if (!skillsConfig.allow_shell_runner) {
    blockers.push('skills.allow_shell_runner must be true');
  }

  if (skillsConfig.shell_runner_allowlist.length === 0) {
    blockers.push('skills.shell_runner_allowlist must include at least one pattern');
  }

  if (skillsConfig.shell_runner_allowlist.some((pattern) => pattern.trim() === '*')) {
    blockers.push("skills.shell_runner_allowlist cannot include wildcard-only '*' patterns");
  }

  if (skillsConfig.allow_shell_runner && !skillsConfig.shell_runner_governance.owner) {
    blockers.push('skills.shell_runner_governance.owner must be set when shell runner is enabled');
  }

  if (!auditEnabled) {
    blockers.push('audit.enabled must be true for shell runner rollout review');
  }

  return { blockers };
}

export function summarizeShellRunnerAuditWindow(events: AuditEvent[]): ShellRunnerAuditWindowSummary {
  return events.reduce<ShellRunnerAuditWindowSummary>(
    (summary, event) => {
      if (event.event_type === 'skills.installer.command_result') {
        summary.command_result_total += 1;

        const payload = event.event as Record<string, unknown>;
        const status = typeof payload.status === 'string' ? payload.status : '';
        const reason = typeof payload.reason === 'string' ? payload.reason : '';
        const command = typeof payload.command === 'string' ? payload.command : '';

        if (status === 'failed') {
          summary.command_result_failed += 1;
        }

        if (reason === 'allowlist_blocked') {
          summary.allowlist_blocked += 1;
        }

        if (command.startsWith('sha256:')) {
          summary.hashed_command_count += 1;
        } else {
          summary.unhashed_command_count += 1;
        }

        return summary;
      }

      if (event.event_type === 'skills.installer.execution_blocked') {
        summary.execution_blocked += 1;
      }

      return summary;
    },
    {
      command_result_total: 0,
      command_result_failed: 0,
      allowlist_blocked: 0,
      execution_blocked: 0,
      hashed_command_count: 0,
      unhashed_command_count: 0,
    },
  );
}

export function calculateShellRunnerHashCoveragePercent(summary: ShellRunnerAuditWindowSummary): number {
  const total = summary.hashed_command_count + summary.unhashed_command_count;
  if (total === 0) {
    return 0;
  }
  return (summary.hashed_command_count / total) * 100;
}

export function computeShellRunnerAuditTrendSnapshot(args: {
  events: AuditEvent[];
  currentWindowStartMs: number;
  currentWindowEndMs: number;
}): ShellRunnerAuditTrendSnapshot {
  const previousWindowStartMs = args.currentWindowStartMs - (args.currentWindowEndMs - args.currentWindowStartMs);

  const currentEvents = args.events.filter(
    (event) => event.timestamp >= args.currentWindowStartMs && event.timestamp <= args.currentWindowEndMs,
  );
  const previousEvents = args.events.filter(
    (event) => event.timestamp >= previousWindowStartMs && event.timestamp < args.currentWindowStartMs,
  );

  const current = summarizeShellRunnerAuditWindow(currentEvents);
  const previous = summarizeShellRunnerAuditWindow(previousEvents);

  return {
    current,
    previous,
    deltas: {
      failures: current.command_result_failed - previous.command_result_failed,
      allowlist_blocks: current.allowlist_blocked - previous.allowlist_blocked,
      hash_coverage_pct:
        calculateShellRunnerHashCoveragePercent(current) - calculateShellRunnerHashCoveragePercent(previous),
    },
  };
}

export function evaluateShellRunnerPromotionPolicy(args: {
  trend: ShellRunnerAuditTrendSnapshot;
  reviewedWindowDays: number;
  governance: {
    review_cadence_days: number;
    promotion_min_success_rate: number;
  };
}): ShellRunnerPromotionPolicyStatus {
  const blockers: string[] = [];
  const successRate =
    args.trend.current.command_result_total === 0
      ? 0
      : (args.trend.current.command_result_total - args.trend.current.command_result_failed)
        / args.trend.current.command_result_total;

  if (args.reviewedWindowDays > args.governance.review_cadence_days) {
    blockers.push(
      `review window (${args.reviewedWindowDays}d) exceeds governance cadence (${args.governance.review_cadence_days}d)`,
    );
  }

  if (args.trend.current.command_result_total === 0) {
    blockers.push('no shell-runner command results in current window');
  }

  if (successRate < args.governance.promotion_min_success_rate) {
    blockers.push(
      `success rate ${(successRate * 100).toFixed(2)}% below minimum ${(args.governance.promotion_min_success_rate * 100).toFixed(2)}%`,
    );
  }

  if (args.trend.deltas.failures > 0) {
    blockers.push(`failures increased by ${args.trend.deltas.failures} vs previous window`);
  }

  if (args.trend.deltas.allowlist_blocks > 0) {
    blockers.push(`allowlist blocks increased by ${args.trend.deltas.allowlist_blocks} vs previous window`);
  }

  return {
    eligible: blockers.length === 0,
    recommendation: blockers.length === 0 ? 'eligible' : 'not_eligible',
    cadence_days: args.governance.review_cadence_days,
    reviewed_window_days: args.reviewedWindowDays,
    success_rate: successRate,
    minimum_success_rate: args.governance.promotion_min_success_rate,
    failures_delta: args.trend.deltas.failures,
    allowlist_blocks_delta: args.trend.deltas.allowlist_blocks,
    hash_coverage_delta_pct: args.trend.deltas.hash_coverage_pct,
    blockers,
  };
}

export function recommendShellRunnerRolloutPhase(
  guardrails: ShellRunnerRolloutGuardrailStatus,
  summary: ShellRunnerAuditWindowSummary,
): ShellRunnerRolloutRecommendation {
  if (guardrails.blockers.length > 0) {
    return 'locked';
  }

  if (summary.command_result_total === 0) {
    return 'guarded_observe';
  }

  if (summary.unhashed_command_count > 0 || summary.command_result_failed > 0) {
    return 'guarded_review';
  }

  return 'expand_candidate';
}

export function toShellRunnerPromotionContract(args: {
  generatedAt: string;
  days: number;
  recommendation: ShellRunnerRolloutRecommendation;
  guardrails: ShellRunnerRolloutGuardrailStatus;
  summary: ShellRunnerAuditWindowSummary;
  trend: ShellRunnerAuditTrendSnapshot;
  promotionPolicy: ShellRunnerPromotionPolicyStatus;
  governance: {
    owner: string | null;
    review_cadence_days: number;
    promotion_min_success_rate: number;
  };
}): ShellRunnerPromotionContract {
  const blockers = [...args.guardrails.blockers, ...args.promotionPolicy.blockers];
  const eligible = args.promotionPolicy.eligible && blockers.length === 0;

  return {
    schema: 'skills.rollout.promotion_contract.v1',
    generated_at: args.generatedAt,
    window_days: args.days,
    gate: {
      status: eligible ? 'pass' : 'fail',
      exit_code: eligible ? 0 : 1,
      reason: eligible ? 'promotion_eligible' : 'promotion_not_eligible',
      blockers,
    },
    recommendation: args.recommendation,
    governance: {
      owner: args.governance.owner,
      review_cadence_days: args.governance.review_cadence_days,
      promotion_min_success_rate: args.governance.promotion_min_success_rate,
    },
    summary: {
      command_result_total: args.summary.command_result_total,
      command_result_failed: args.summary.command_result_failed,
      allowlist_blocked: args.summary.allowlist_blocked,
      hash_coverage_pct: calculateShellRunnerHashCoveragePercent(args.summary),
    },
    promotion_policy: args.promotionPolicy,
    trend: args.trend.deltas,
  };
}

function expandHomePath(pathValue: string): string {
  if (pathValue.startsWith('~/')) {
    return resolve(homedir(), pathValue.slice(2));
  }
  return resolve(pathValue);
}

interface SkillShellRunnerAuditLogger {
  skillsInstallerExecutionBlocked(event: {
    skill_name: string;
    phase: 'install' | 'execute';
    execution_requested: boolean;
    execution_enabled: boolean;
    reason: string;
    attempted_command_count: number;
  }): void;
  skillsInstallerCommandResult(event: {
    skill_name: string;
    phase: 'install' | 'execute';
    installer_type: string;
    command: string;
    status: 'blocked' | 'skipped' | 'succeeded' | 'failed';
    reason: string;
  }): void;
}

export function emitShellRunnerAuditEvents(args: {
  skillName: string;
  phase: 'install' | 'execute';
  executionRequested: boolean;
  executionEnabled: boolean;
  reason: SkillInstallerExecutionReason;
  results: SkillInstallerExecutionStubView['results'];
  logger?: SkillShellRunnerAuditLogger | null;
}): void {
  const logger = args.logger ?? (auditLogger as unknown as SkillShellRunnerAuditLogger | null);
  if (!logger) {
    return;
  }

  if (args.executionRequested && !args.executionEnabled) {
    logger.skillsInstallerExecutionBlocked({
      skill_name: args.skillName,
      phase: args.phase,
      execution_requested: args.executionRequested,
      execution_enabled: args.executionEnabled,
      reason: sanitizeSkillInstallerAuditReason(args.reason),
      attempted_command_count: args.results.length,
    });
    return;
  }

  for (const result of args.results) {
    logger.skillsInstallerCommandResult({
      skill_name: args.skillName,
      phase: args.phase,
      installer_type: result.installer_type,
      command: hashSkillInstallerAuditCommand(result.command),
      status: result.status,
      reason: sanitizeSkillInstallerAuditReason(result.reason),
    });
  }
}

export function checkCommandAgainstAllowlist(command: string, allowlist?: string[]): boolean {
  if (!allowlist) {
    return true;
  }

  if (allowlist.length === 0) {
    return false;
  }

  return allowlist.some((pattern) => {
    const regexSource = pattern
      .split('*')
      .map((segment) => segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
      .join('.*');
    return new RegExp(`^${regexSource}$`).test(command);
  });
}

export const noOpSkillInstallerCommandRunner: SkillInstallerCommandRunner = {
  run(_commands: string[]): SkillInstallerCommandRunResult[] {
    return [];
  },
};

export function createShellSkillInstallerCommandRunner(allowlist?: string[]): SkillInstallerCommandRunner {
  return {
    run(commands: string[]): SkillInstallerCommandRunResult[] {
      return commands.map((command) => {
        if (!checkCommandAgainstAllowlist(command, allowlist)) {
          return {
            command,
            status: 'failed',
            reason: 'allowlist_blocked',
          };
        }

        const result = spawnSync(command, {
          shell: true,
          stdio: 'pipe',
          encoding: 'utf-8',
        });

        if (result.error) {
          return {
            command,
            status: 'failed',
            reason: `spawn_error:${result.error.message}`,
          };
        }

        if (result.status === 0) {
          return {
            command,
            status: 'succeeded',
          };
        }

        if (result.status !== null) {
          return {
            command,
            status: 'failed',
            reason: `exit_code_${result.status}`,
          };
        }

        if (result.signal) {
          return {
            command,
            status: 'failed',
            reason: `signal_${result.signal}`,
          };
        }

        return {
          command,
          status: 'failed',
          reason: 'unknown_failure',
        };
      });
    },
  };
}

export function resolveSkillInstallerCommandRunner(
  mode?: string,
  allowlist?: string[],
): { mode: SkillInstallerRunnerMode; runner: SkillInstallerCommandRunner } | { error: string } {
  if (!mode || mode === 'noop') {
    return { mode: 'noop', runner: noOpSkillInstallerCommandRunner };
  }

  if (mode === 'shell') {
    return { mode: 'shell', runner: createShellSkillInstallerCommandRunner(allowlist) };
  }

  return { error: `Invalid runner '${mode}'. Allowed values: noop, shell.` };
}

export function toInstallerExecutionStepEnvelopes(
  steps: Array<{ installerType: string; command: string }>,
  policy: SkillInstallerExecutionPolicy,
): {
  attempted: SkillInstallerExecutionStubView['attempted'];
  results: SkillInstallerExecutionStubView['results'];
} {
  const attempted = steps.map((step) => ({
    installer_type: step.installerType,
    command: step.command,
  }));

  const status: SkillInstallerStepStatus = policy.reason === 'confirmation_required' ? 'blocked' : 'skipped';

  const results = attempted.map((step) => ({
    installer_type: step.installer_type,
    command: step.command,
    status,
    reason: policy.reason,
  }));

  return { attempted, results };
}

export function mergeInstallerExecutionResults(
  attempted: SkillInstallerExecutionStubView['attempted'],
  policy: SkillInstallerExecutionPolicy,
  commandResults: SkillInstallerCommandRunResult[],
): SkillInstallerExecutionStubView['results'] {
  if (!policy.execution_enabled) {
    const blockedStatus: SkillInstallerStepStatus = policy.reason === 'confirmation_required' ? 'blocked' : 'skipped';
    return attempted.map((step) => ({
      installer_type: step.installer_type,
      command: step.command,
      status: blockedStatus,
      reason: policy.reason,
    }));
  }

  const resultByCommand = new Map(commandResults.map((result) => [result.command, result]));
  return attempted.map((step) => {
    const commandResult = resultByCommand.get(step.command);
    if (!commandResult) {
      return {
        installer_type: step.installer_type,
        command: step.command,
        status: 'failed' as const,
        reason: 'runner_no_result',
      };
    }

    return {
      installer_type: step.installer_type,
      command: step.command,
      status: commandResult.status,
      reason:
        commandResult.reason ??
        (commandResult.status === 'succeeded' ? 'runner_reported_success' : 'runner_reported_failure'),
    };
  });
}

export function toSkillListRows(skills: Skill[]): SkillListRow[] {
  return skills
    .map((skill) => ({
      name: skill.manifest.name,
      tier: skill.manifest.tier,
      status: (skill.available ? 'available' : 'unavailable') as SkillListRow['status'],
      reason: skill.unavailableReasons?.join('; '),
    }))
    .sort((a, b) => a.name.localeCompare(b.name));
}

export function renderSkillsTable(rows: SkillListRow[]): string {
  if (rows.length === 0) {
    return 'No skills found.';
  }

  const nameWidth = Math.max('NAME'.length, ...rows.map((row) => row.name.length));
  const tierWidth = Math.max('TIER'.length, ...rows.map((row) => row.tier.length));
  const statusWidth = Math.max('STATUS'.length, ...rows.map((row) => row.status.length));

  const lines = [
    `${'NAME'.padEnd(nameWidth)}  ${'TIER'.padEnd(tierWidth)}  ${'STATUS'.padEnd(statusWidth)}  REASON`,
    `${'-'.repeat(nameWidth)}  ${'-'.repeat(tierWidth)}  ${'-'.repeat(statusWidth)}  ------`,
  ];

  for (const row of rows) {
    lines.push(
      `${row.name.padEnd(nameWidth)}  ${row.tier.padEnd(tierWidth)}  ${row.status.padEnd(statusWidth)}  ${row.reason ?? ''}`,
    );
  }

  return lines.join('\n');
}

export function renderSkillInfo(skill: Skill): string {
  const lines: string[] = [
    `Name: ${skill.manifest.name}`,
    `Description: ${skill.manifest.description}`,
    `Version: ${skill.manifest.version}`,
    `Tier: ${skill.manifest.tier}`,
    `Status: ${skill.available ? 'available' : 'unavailable'}`,
    `Directory: ${skill.directory}`,
  ];

  if (skill.manifest.author) {
    lines.push(`Author: ${skill.manifest.author}`);
  }

  if (skill.manifest.tools && skill.manifest.tools.length > 0) {
    lines.push(`Tools: ${skill.manifest.tools.join(', ')}`);
  }

  if (skill.manifest.permissions) {
    lines.push(...renderSkillPermissions(skill.manifest.permissions));
  }

  if (skill.unavailableReasons && skill.unavailableReasons.length > 0) {
    lines.push(`Unavailable reasons: ${skill.unavailableReasons.join('; ')}`);
  }

  if (skill.manifest.installers && skill.manifest.installers.length > 0) {
    const plan = buildInstallerPlan(skill.manifest.installers);
    lines.push(`Installer plan mode: ${plan.mode}`);
    if (plan.steps.length > 0) {
      lines.push('Installer planned steps:');
      for (const step of plan.steps) {
        lines.push(`- [${step.installerType}] ${step.command}`);
      }
    }
    if (plan.skipped.length > 0) {
      lines.push('Installer skipped steps:');
      for (const skip of plan.skipped) {
        lines.push(`- [${skip.installerType}] ${skip.reason}`);
      }
    }
  }

  return lines.join('\n');
}

export function toSkillInstallerPlanView(skill: Skill): SkillInstallerPlanView {
  const plan = buildInstallerPlan(skill.manifest.installers);
  return {
    skill: {
      name: skill.manifest.name,
      tier: skill.manifest.tier,
      version: skill.manifest.version,
      permissions: skill.manifest.permissions,
    },
    mode: plan.mode,
    steps: plan.steps,
    skipped: plan.skipped,
  };
}

export function renderSkillInstallerPlan(view: SkillInstallerPlanView): string {
  const lines: string[] = [
    `Installer plan for '${view.skill.name}' (${view.skill.tier}, v${view.skill.version})`,
    `Mode: ${view.mode}`,
  ];

  if (view.skill.permissions) {
    lines.push(...renderSkillPermissions(view.skill.permissions));
  }

  if (view.steps.length === 0) {
    lines.push('Planned steps: none');
  } else {
    lines.push('Planned steps:');
    for (const step of view.steps) {
      lines.push(`- [${step.installerType}] ${step.command}`);
    }
  }

  if (view.skipped.length === 0) {
    lines.push('Skipped steps: none');
  } else {
    lines.push('Skipped steps:');
    for (const skip of view.skipped) {
      lines.push(`- [${skip.installerType}] ${skip.reason}`);
    }
  }

  return lines.join('\n');
}

export function toSkillInstallPreflightView(sourcePath: string): SkillInstallPreflightView | null {
  const sourceSkill = loadSkill(resolve(sourcePath), 'managed');
  if (!sourceSkill) {
    return null;
  }

  const planView = toSkillInstallerPlanView(sourceSkill);
  return {
    sourcePath: resolve(sourcePath),
    skill: planView.skill,
    mode: planView.mode,
    steps: planView.steps,
    skipped: planView.skipped,
  };
}

export function renderSkillInstallPreflight(view: SkillInstallPreflightView): string {
  const lines: string[] = [
    `Install preflight for '${view.skill.name}' from ${view.sourcePath}`,
    `Mode: ${view.mode}`,
  ];

  if (view.skill.permissions) {
    lines.push(...renderSkillPermissions(view.skill.permissions));
  }

  if (view.steps.length === 0) {
    lines.push('Planned installer steps: none');
  } else {
    lines.push('Planned installer steps:');
    for (const step of view.steps) {
      lines.push(`- [${step.installerType}] ${step.command}`);
    }
  }

  if (view.skipped.length > 0) {
    lines.push('Skipped installer steps:');
    for (const skip of view.skipped) {
      lines.push(`- [${skip.installerType}] ${skip.reason}`);
    }
  }

  return lines.join('\n');
}

export function toSkillInstallerExecutionStubView(skill: Skill): SkillInstallerExecutionStubView {
  const plan = toSkillInstallerPlanView(skill);
  const policy = evaluateInstallerExecutionPolicy({ mode: 'stub', confirmed: false });
  const stepEnvelopes = toInstallerExecutionStepEnvelopes(plan.steps, policy);
  return {
    skill: plan.skill,
    execution: 'stub',
    mode: 'stub',
    confirmed: policy.confirmed,
    execution_enabled: policy.execution_enabled,
    executed: [],
    reason: policy.reason,
    attempted: stepEnvelopes.attempted,
    results: stepEnvelopes.results,
    wouldRun: plan.steps.map((step) => step.command),
    skipped: plan.skipped,
  };
}

export function toSkillInstallerExecutionStubFromPreflight(
  preflight: SkillInstallPreflightView,
  options?: {
    mode?: SkillInstallActionMode;
    confirmed?: boolean;
    executionRequested?: boolean;
    configPolicyEnabled?: boolean;
  },
): SkillInstallerExecutionStubView {
  const mode = options?.mode ?? 'stub';
  const policy = evaluateInstallerExecutionPolicy({
    mode,
    confirmed: options?.confirmed ?? false,
    executionRequested: options?.executionRequested ?? false,
    configPolicyEnabled: options?.configPolicyEnabled,
  });
  const stepEnvelopes = toInstallerExecutionStepEnvelopes(preflight.steps, policy);
  return {
    skill: preflight.skill,
    execution: 'stub',
    mode,
    confirmed: policy.confirmed,
    execution_enabled: policy.execution_enabled,
    executed: [],
    reason: policy.reason,
    attempted: stepEnvelopes.attempted,
    results: stepEnvelopes.results,
    wouldRun: preflight.steps.map((step) => step.command),
    skipped: preflight.skipped,
  };
}

export function evaluateInstallerExecutionPolicy(opts: {
  mode: SkillInstallActionMode;
  confirmed: boolean;
  executionRequested?: boolean;
  configPolicyEnabled?: boolean;
}): SkillInstallerExecutionPolicy {
  if (opts.mode === 'install' && !opts.confirmed) {
    return {
      confirmed: false,
      execution_enabled: false,
      reason: 'confirmation_required',
    };
  }

  if (opts.mode === 'install' && opts.executionRequested && opts.configPolicyEnabled === false) {
    return {
      confirmed: opts.confirmed,
      execution_enabled: false,
      reason: 'execution_policy_disabled',
    };
  }

  if (opts.mode === 'install' && opts.confirmed && opts.executionRequested) {
    return {
      confirmed: true,
      execution_enabled: true,
      reason: 'execution_enabled',
    };
  }

  return {
    confirmed: opts.confirmed,
    execution_enabled: false,
    reason: 'execution_disabled',
  };
}

export function runInstallerCommandsWithPolicy(
  commands: string[],
  policy: SkillInstallerExecutionPolicy,
  runner: SkillInstallerCommandRunner,
): SkillInstallerCommandRunResult[] {
  if (!policy.execution_enabled) {
    return [];
  }

  return runner.run(commands);
}

export function renderSkillInstallerExecutionStub(view: SkillInstallerExecutionStubView): string {
  const lines: string[] = [
    `Installer execution stub for '${view.skill.name}' (${view.skill.tier}, v${view.skill.version})`,
    view.executed.length > 0 ? 'Installer commands were executed.' : 'No installer commands were executed.',
  ];

  if (view.reason === 'execution_policy_disabled') {
    lines.push('Execution policy blocked installer commands.');
    lines.push('Set skills.installation_execution: enabled in config and re-run with --execute --confirm to allow execution.');
  }

  if (view.wouldRun.length === 0) {
    lines.push('Would run: none');
  } else {
    lines.push('Would run:');
    for (const command of view.wouldRun) {
      lines.push(`- ${command}`);
    }
  }

  if (view.skipped.length > 0) {
    lines.push('Skipped:');
    for (const skip of view.skipped) {
      lines.push(`- [${skip.installerType}] ${skip.reason}`);
    }
  }

  if (view.results.length > 0) {
    lines.push('Results:');
    for (const result of view.results) {
      lines.push(`- [${result.installer_type}] ${result.status} ${result.command} (${result.reason})`);
    }
  }

  return lines.join('\n');
}

export function runSkillExecuteAction(
  skill: Skill,
  opts: {
    asJson: boolean;
    confirmed: boolean;
    executionRequested?: boolean;
    commandRunner?: SkillInstallerCommandRunner;
    configPolicyEnabled?: boolean;
    runnerMode?: SkillInstallerRunnerMode;
  },
): { ok: true; execution: SkillInstallerExecutionStubView } {
  const execution = toSkillInstallerExecutionStubView(skill);
  const policy = evaluateInstallerExecutionPolicy({
    mode: 'install',
    confirmed: opts.confirmed,
    executionRequested: opts.executionRequested ?? false,
    configPolicyEnabled: opts.configPolicyEnabled,
  });

  execution.confirmed = policy.confirmed;
  execution.execution_enabled = policy.execution_enabled;
  execution.reason = policy.reason;

  const commandResults = runInstallerCommandsWithPolicy(
    execution.wouldRun,
    policy,
    opts.commandRunner ?? noOpSkillInstallerCommandRunner,
  );
  execution.executed = commandResults.map((result) => result.command);
  execution.results = mergeInstallerExecutionResults(execution.attempted, policy, commandResults);

  if (opts.runnerMode === 'shell') {
    emitShellRunnerAuditEvents({
      skillName: execution.skill.name,
      phase: 'execute',
      executionRequested: opts.executionRequested ?? false,
      executionEnabled: policy.execution_enabled,
      reason: policy.reason,
      results: execution.results,
    });
  }

  if (opts.asJson) {
    console.log(JSON.stringify(execution, null, 2));
  } else {
    console.log(renderSkillInstallerExecutionStub(execution));
  }

  return { ok: true, execution };
}

export function summarizeSkillsRefresh(skills: Skill[]): SkillRefreshSummary {
  const summary: SkillRefreshSummary = {
    total: skills.length,
    available: 0,
    unavailable: 0,
    tiers: {
      bundled: 0,
      managed: 0,
      workspace: 0,
    },
  };

  for (const skill of skills) {
    if (skill.available) {
      summary.available += 1;
    } else {
      summary.unavailable += 1;
    }
    summary.tiers[skill.manifest.tier] += 1;
  }

  return summary;
}

export function renderSkillsRefreshSummary(summary: SkillRefreshSummary): string {
  return [
    `Refreshed ${summary.total} skills (${summary.available} available, ${summary.unavailable} unavailable).`,
    `By tier: bundled=${summary.tiers.bundled}, managed=${summary.tiers.managed}, workspace=${summary.tiers.workspace}`,
  ].join('\n');
}

function loadSkillsFromConfig(configPath?: string): { skills?: Skill[]; error?: string } {
  const loaded = loadConfigSafe(configPath);
  if (loaded.error || !loaded.config) {
    return { error: loaded.error ?? 'Failed to load config' };
  }

  const defaultManagedDir = resolve(homedir(), '.flynn/workspace/skills');
  const skills = loadAllSkills({
    bundledDir: loaded.config.skills.bundled_dir,
    managedDir: loaded.config.skills.managed_dir ?? defaultManagedDir,
    workspaceDir: loaded.config.skills.workspace_dir,
  });

  return { skills };
}

export function installSkillFromDirectory(installer: SkillInstaller, sourcePath: string): { skill?: Skill; error?: string } {
  const sourceDir = resolve(sourcePath);
  try {
    const skill = installer.install(sourceDir);
    if (!skill) {
      return { error: `Failed to load skill from '${sourceDir}' after installation.` };
    }
    return { skill };
  } catch (error) {
    return { error: error instanceof Error ? error.message : String(error) };
  }
}

export function runSkillInstallAction(
  installer: SkillInstaller,
  sourcePath: string,
  opts: {
    mode: SkillInstallActionMode;
    asJson: boolean;
    confirmed: boolean;
    executionRequested?: boolean;
    commandRunner?: SkillInstallerCommandRunner;
    configPolicyEnabled?: boolean;
    runnerMode?: SkillInstallerRunnerMode;
  },
): { ok: true } | { ok: false; error: string } {
  const preflight = toSkillInstallPreflightView(sourcePath);

  if (opts.mode === 'plan-only') {
    if (!preflight) {
      return { ok: false, error: `Failed to generate install preflight from '${resolve(sourcePath)}'.` };
    }
    if (opts.asJson) {
      const execution = toSkillInstallerExecutionStubFromPreflight(preflight, {
        mode: 'plan-only',
        confirmed: opts.confirmed,
        configPolicyEnabled: opts.configPolicyEnabled,
      });
      console.log(JSON.stringify({ preflight, execution }, null, 2));
    } else {
      console.log(renderSkillInstallPreflight(preflight));
    }
    return { ok: true };
  }

  if (opts.mode === 'stub') {
    if (!preflight) {
      return { ok: false, error: `Failed to generate installer execution stub from '${resolve(sourcePath)}'.` };
    }
    const stub = toSkillInstallerExecutionStubFromPreflight(preflight, {
      mode: 'stub',
      confirmed: opts.confirmed,
      configPolicyEnabled: opts.configPolicyEnabled,
    });
    if (opts.asJson) {
      console.log(JSON.stringify({ execution: stub }, null, 2));
    } else {
      console.log(renderSkillInstallerExecutionStub(stub));
    }
    return { ok: true };
  }

  if (preflight && !opts.asJson) {
    console.log(renderSkillInstallPreflight(preflight));
  }

  const result = installSkillFromDirectory(installer, sourcePath);
  if (result.error || !result.skill) {
    return { ok: false, error: result.error ?? `Failed to install skill from '${sourcePath}'.` };
  }

  const installPolicy = evaluateInstallerExecutionPolicy({
    mode: 'install',
    confirmed: opts.confirmed,
    configPolicyEnabled: opts.configPolicyEnabled,
  });
  const requestedInstallPolicy = evaluateInstallerExecutionPolicy({
    mode: 'install',
    confirmed: opts.confirmed,
    executionRequested: opts.executionRequested ?? false,
    configPolicyEnabled: opts.configPolicyEnabled,
  });

  const execution =
    preflight !== null
      ? toSkillInstallerExecutionStubFromPreflight(preflight, {
        mode: 'install',
        confirmed: opts.confirmed,
        executionRequested: opts.executionRequested ?? false,
        configPolicyEnabled: opts.configPolicyEnabled,
      })
      : {
        skill: {
          name: result.skill.manifest.name,
          version: result.skill.manifest.version,
          tier: result.skill.manifest.tier,
        },
        execution: 'stub' as const,
        mode: 'install' as const,
        confirmed: installPolicy.confirmed,
        execution_enabled: installPolicy.execution_enabled,
        executed: runInstallerCommandsWithPolicy([], installPolicy, opts.commandRunner ?? noOpSkillInstallerCommandRunner),
        reason: installPolicy.reason,
        attempted: [],
        results: [],
        wouldRun: [],
        skipped: [],
      };

  const commandResults = runInstallerCommandsWithPolicy(
    execution.wouldRun,
    requestedInstallPolicy,
    opts.commandRunner ?? noOpSkillInstallerCommandRunner,
  );
  execution.executed = commandResults.map((result) => result.command);
  execution.results = mergeInstallerExecutionResults(
    execution.attempted,
    requestedInstallPolicy,
    commandResults,
  );

  if (opts.runnerMode === 'shell') {
    emitShellRunnerAuditEvents({
      skillName: execution.skill.name,
      phase: 'install',
      executionRequested: opts.executionRequested ?? false,
      executionEnabled: requestedInstallPolicy.execution_enabled,
      reason: requestedInstallPolicy.reason,
      results: execution.results,
    });
  }

  if (opts.asJson) {
    console.log(
      JSON.stringify(
        {
          preflight,
          execution,
          installed: {
            name: result.skill.manifest.name,
            version: result.skill.manifest.version,
            tier: result.skill.manifest.tier,
            directory: result.skill.directory,
          },
        },
        null,
        2,
      ),
    );
  } else {
    console.log(`Installed skill '${result.skill.manifest.name}' (${result.skill.manifest.version}).`);
  }

  return { ok: true };
}

export function uninstallSkillByName(
  installer: SkillInstaller,
  name: string,
  opts: { confirm: boolean; discoveredSkill?: Skill },
): { removed?: true; error?: string } {
  if (!opts.confirm) {
    return { error: 'Refusing to uninstall without --yes. Re-run with --yes to confirm.' };
  }

  if (opts.discoveredSkill && opts.discoveredSkill.manifest.tier !== 'managed' && !installer.isInstalled(name)) {
    return {
      error: `Skill '${name}' is '${opts.discoveredSkill.manifest.tier}' and cannot be uninstalled from managed skills.`,
    };
  }

  if (!installer.uninstall(name)) {
    return { error: `Managed skill '${name}' is not installed.` };
  }

  return { removed: true };
}

export function registerSkillsCommand(program: Command): void {
  const skills = program
    .command('skills')
    .description('Manage Flynn skills')
    .action(() => {
      skills.outputHelp();
    });

  skills
    .command('list')
    .description('List discovered skills')
    .option('--json', 'Output as JSON')
    .option('-c, --config <path>', 'Config file path')
    .action((opts: { json?: boolean; config?: string }) => {
      const loaded = loadSkillsFromConfig(opts.config);
      if (loaded.error || !loaded.skills) {
        console.error(loaded.error ?? 'Failed to load skills');
        process.exitCode = 1;
        return;
      }

      const rows = toSkillListRows(loaded.skills);
      if (opts.json) {
        console.log(JSON.stringify(rows, null, 2));
        return;
      }

      console.log(renderSkillsTable(rows));
    });

  skills
    .command('info <name>')
    .description('Show details for a skill')
    .option('--json', 'Output as JSON')
    .option('-c, --config <path>', 'Config file path')
    .action((name: string, opts: { json?: boolean; config?: string }) => {
      const loaded = loadSkillsFromConfig(opts.config);
      if (loaded.error || !loaded.skills) {
        console.error(loaded.error ?? 'Failed to load skills');
        process.exitCode = 1;
        return;
      }

      const skill = loaded.skills.find((item) => item.manifest.name === name);
      if (!skill) {
        console.error(`Skill '${name}' not found.`);
        process.exitCode = 1;
        return;
      }

      if (opts.json) {
        console.log(JSON.stringify(skill, null, 2));
        return;
      }

      console.log(renderSkillInfo(skill));
    });

  skills
    .command('install <path>')
    .description('Install a skill from a local directory')
    .option('--json', 'Output preflight and install result as JSON')
    .option('--preflight-only', 'Show installer preflight without performing install')
    .option('--stub', 'Show installer execution stub without performing install')
    .option('--confirm', 'Mark installer execution intent as confirmed (required with --execute)')
    .option('--execute', 'Enable installer command execution (requires --confirm and skills.installation_execution=enabled)')
    .option('--runner <mode>', 'Installer runner: noop (default) or shell (requires skills.allow_shell_runner=true and allowlist)')
    .option('-c, --config <path>', 'Config file path')
    .action(
      (pathArg: string, opts: {
        json?: boolean;
        preflightOnly?: boolean;
        stub?: boolean;
        confirm?: boolean;
        execute?: boolean;
        runner?: string;
        config?: string;
      }) => {
        const loaded = loadConfigSafe(opts.config);
        if (loaded.error || !loaded.config) {
          console.error(loaded.error ?? 'Failed to load config');
          process.exitCode = 1;
          return;
        }

        const runnerResolution = resolveSkillInstallerCommandRunner(
          opts.runner,
          loaded.config.skills.shell_runner_allowlist,
        );
        if ('error' in runnerResolution) {
          console.error(runnerResolution.error);
          process.exitCode = 1;
          return;
        }

        if ((opts.execute ?? false) && !(opts.confirm ?? false)) {
          console.error('`--execute` requires `--confirm`. No installer commands were run.');
          process.exitCode = 1;
          return;
        }

        if ((opts.runner ?? 'noop') === 'shell' && !loaded.config.skills.allow_shell_runner) {
          console.error("Runner 'shell' is disabled by config. Set skills.allow_shell_runner: true to enable it.");
          process.exitCode = 1;
          return;
        }

        const defaultManagedDir = resolve(homedir(), '.flynn/workspace/skills');
        const installer = new SkillInstaller(loaded.config.skills.managed_dir ?? defaultManagedDir);

        const mode: SkillInstallActionMode = opts.preflightOnly ? 'plan-only' : opts.stub ? 'stub' : 'install';
        const configPolicyEnabled = loaded.config.skills.installation_execution === 'enabled';
        const result = runSkillInstallAction(installer, pathArg, {
          mode,
          asJson: opts.json ?? false,
          confirmed: opts.confirm ?? false,
          executionRequested: opts.execute ?? false,
          commandRunner: runnerResolution.runner,
          configPolicyEnabled,
          runnerMode: runnerResolution.mode,
        });

        if (!result.ok) {
          console.error(result.error);
          process.exitCode = 1;
        }
      });

  skills
    .command('uninstall <name>')
    .description('Uninstall a managed skill by name')
    .option('--yes', 'Confirm uninstall without prompt')
    .option('-c, --config <path>', 'Config file path')
    .action((name: string, opts: { yes?: boolean; config?: string }) => {
      const loaded = loadConfigSafe(opts.config);
      if (loaded.error || !loaded.config) {
        console.error(loaded.error ?? 'Failed to load config');
        process.exitCode = 1;
        return;
      }

      const defaultManagedDir = resolve(homedir(), '.flynn/workspace/skills');
      const installer = new SkillInstaller(loaded.config.skills.managed_dir ?? defaultManagedDir);
      const discoveredSkills = loadAllSkills({
        bundledDir: loaded.config.skills.bundled_dir,
        managedDir: loaded.config.skills.managed_dir ?? defaultManagedDir,
        workspaceDir: loaded.config.skills.workspace_dir,
      });
      const discovered = discoveredSkills.find(
        (skill) => skill.manifest.name === name && skill.manifest.tier === 'managed',
      ) ?? discoveredSkills.find((skill) => skill.manifest.name === name);

      const result = uninstallSkillByName(installer, name, {
        confirm: opts.yes ?? false,
        discoveredSkill: discovered,
      });

      if (result.error || !result.removed) {
        console.error(result.error ?? `Failed to uninstall skill '${name}'.`);
        process.exitCode = 1;
        return;
      }

      console.log(`Uninstalled skill '${name}'.`);
    });

  skills
    .command('refresh')
    .description('Refresh skill discovery and print summary')
    .option('--json', 'Output as JSON')
    .option('-c, --config <path>', 'Config file path')
    .action((opts: { json?: boolean; config?: string }) => {
      const loaded = loadSkillsFromConfig(opts.config);
      if (loaded.error || !loaded.skills) {
        console.error(loaded.error ?? 'Failed to load skills');
        process.exitCode = 1;
        return;
      }

      const summary = summarizeSkillsRefresh(loaded.skills);
      if (opts.json) {
        console.log(JSON.stringify(summary, null, 2));
        return;
      }

      console.log(renderSkillsRefreshSummary(summary));
    });

  skills
    .command('plan <name>')
    .description('Print dry-run installer plan for a skill')
    .option('--json', 'Output as JSON')
    .option('-c, --config <path>', 'Config file path')
    .action((name: string, opts: { json?: boolean; config?: string }) => {
      const loaded = loadSkillsFromConfig(opts.config);
      if (loaded.error || !loaded.skills) {
        console.error(loaded.error ?? 'Failed to load skills');
        process.exitCode = 1;
        return;
      }

      const skill = loaded.skills.find((item) => item.manifest.name === name);
      if (!skill) {
        console.error(`Skill '${name}' not found.`);
        process.exitCode = 1;
        return;
      }

      const view = toSkillInstallerPlanView(skill);
      if (opts.json) {
        console.log(JSON.stringify(view, null, 2));
        return;
      }

      console.log(renderSkillInstallerPlan(view));
    });

  skills
    .command('rollout-status')
    .description('Show shell runner rollout guardrails and audit review summary')
    .option('--days <n>', 'Look back N days in audit logs (default: 7)', '7')
    .option('--contract', 'Output dedicated machine-readable promotion contract JSON')
    .option('--out <path>', 'Write rollout JSON payload to file')
    .option('--json', 'Output as JSON')
    .option('-c, --config <path>', 'Config file path')
    .action(async (opts: { days?: string; contract?: boolean; out?: string; json?: boolean; config?: string }) => {
      const loaded = loadConfigSafe(opts.config);
      if (loaded.error || !loaded.config) {
        console.error(loaded.error ?? 'Failed to load config');
        process.exitCode = 1;
        return;
      }

      const parsedDays = Number.parseInt(opts.days ?? '7', 10);
      if (!Number.isFinite(parsedDays) || parsedDays <= 0) {
        console.error('`--days` must be a positive integer.');
        process.exitCode = 1;
        return;
      }

      const guardrails = evaluateShellRunnerRolloutGuardrails(loaded.config.skills, loaded.config.audit.enabled);
      const windowDurationMs = parsedDays * 24 * 60 * 60 * 1000;
      const nowMs = Date.now();
      const currentWindowStartMs = nowMs - windowDurationMs;
      const queryStartMs = nowMs - windowDurationMs * 2;
      const auditPath = expandHomePath(loaded.config.audit.path);
      const auditEvents = await queryAuditLogs(auditPath, {
        start_time: queryStartMs,
        event_types: ['skills.installer.command_result', 'skills.installer.execution_blocked'],
      });
      const trend = computeShellRunnerAuditTrendSnapshot({
        events: auditEvents,
        currentWindowStartMs,
        currentWindowEndMs: nowMs,
      });
      const recommendation = recommendShellRunnerRolloutPhase(guardrails, trend.current);
      const governance = loaded.config.skills.shell_runner_governance;
      const promotionPolicy = evaluateShellRunnerPromotionPolicy({
        trend,
        reviewedWindowDays: parsedDays,
        governance: {
          review_cadence_days: governance.review_cadence_days,
          promotion_min_success_rate: governance.promotion_min_success_rate,
        },
      });
      const generatedAt = new Date(nowMs).toISOString();
      const rolloutPayload = {
        generated_at: generatedAt,
        days: parsedDays,
        guardrails,
        summary: trend.current,
        trend,
        recommendation,
        promotion_policy: promotionPolicy,
        governance: {
          owner: governance.owner ?? null,
          review_cadence_days: governance.review_cadence_days,
          promotion_min_success_rate: governance.promotion_min_success_rate,
        },
      };
      const promotionContract = toShellRunnerPromotionContract({
        generatedAt,
        days: parsedDays,
        recommendation,
        guardrails,
        summary: trend.current,
        trend,
        promotionPolicy,
        governance: {
          owner: governance.owner ?? null,
          review_cadence_days: governance.review_cadence_days,
          promotion_min_success_rate: governance.promotion_min_success_rate,
        },
      });

      if (opts.out) {
        writeFileSync(
          expandHomePath(opts.out),
          JSON.stringify(opts.contract ? promotionContract : rolloutPayload, null, 2),
          'utf-8',
        );
      }

      if (opts.contract) {
        console.log(JSON.stringify(promotionContract, null, 2));
        process.exitCode = promotionContract.gate.exit_code;
        return;
      }

      if (opts.json) {
        console.log(JSON.stringify(rolloutPayload, null, 2));
        return;
      }

      console.log('Shell runner rollout status');
      console.log(`Window: ${parsedDays}d`);
      console.log(`Recommendation: ${recommendation}`);
      console.log(`Governance owner: ${governance.owner ?? '(unset)'}`);
      console.log(`Governance review cadence (days): ${governance.review_cadence_days}`);
      console.log(`Governance promotion min success rate: ${governance.promotion_min_success_rate}`);
      console.log(`Guardrail blockers: ${guardrails.blockers.length}`);
      if (guardrails.blockers.length > 0) {
        for (const blocker of guardrails.blockers) {
          console.log(`- ${blocker}`);
        }
      }
      console.log(`Audited command results: ${trend.current.command_result_total}`);
      console.log(`Audited failures: ${trend.current.command_result_failed}`);
      console.log(`Allowlist blocks: ${trend.current.allowlist_blocked}`);
      console.log(`Execution blocks: ${trend.current.execution_blocked}`);
      console.log(`Hashed command payloads: ${trend.current.hashed_command_count}`);
      console.log(`Unhashed command payloads: ${trend.current.unhashed_command_count}`);
      console.log(`Failure delta vs previous window: ${trend.deltas.failures}`);
      console.log(`Allowlist block delta vs previous window: ${trend.deltas.allowlist_blocks}`);
      console.log(`Hash coverage delta vs previous window (%): ${trend.deltas.hash_coverage_pct.toFixed(2)}`);
      console.log(
        `Promotion policy: ${promotionPolicy.recommendation} (success ${(promotionPolicy.success_rate * 100).toFixed(2)}% / min ${(promotionPolicy.minimum_success_rate * 100).toFixed(2)}%)`,
      );
      if (promotionPolicy.blockers.length > 0) {
        for (const blocker of promotionPolicy.blockers) {
          console.log(`- ${blocker}`);
        }
      }
      if (opts.out) {
        console.log(`Wrote rollout payload: ${expandHomePath(opts.out)}`);
      }
    });

  skills
    .command('execute <name>')
    .description('Preview or execute installer steps for an installed skill')
    .option('--json', 'Output as JSON')
    .option('--confirm', 'Mark installer execution intent as confirmed (required with --execute)')
    .option('--execute', 'Enable installer command execution (requires --confirm and skills.installation_execution=enabled)')
    .option('--runner <mode>', 'Installer runner: noop (default) or shell (requires skills.allow_shell_runner=true and allowlist)')
    .option('-c, --config <path>', 'Config file path')
    .action((name: string, opts: { json?: boolean; confirm?: boolean; execute?: boolean; runner?: string; config?: string }) => {
      const loaded = loadSkillsFromConfig(opts.config);
      if (loaded.error || !loaded.skills) {
        console.error(loaded.error ?? 'Failed to load skills');
        process.exitCode = 1;
        return;
      }

      const configLoaded = loadConfigSafe(opts.config);
      if (configLoaded.error || !configLoaded.config) {
        console.error(configLoaded.error ?? 'Failed to load config');
        process.exitCode = 1;
        return;
      }

      const skill = loaded.skills.find((item) => item.manifest.name === name);
      if (!skill) {
        console.error(`Skill '${name}' not found.`);
        process.exitCode = 1;
        return;
      }

      const runnerResolution = resolveSkillInstallerCommandRunner(
        opts.runner,
        configLoaded.config.skills.shell_runner_allowlist,
      );
      if ('error' in runnerResolution) {
        console.error(runnerResolution.error);
        process.exitCode = 1;
        return;
      }

      if ((opts.execute ?? false) && !(opts.confirm ?? false)) {
        console.error('`--execute` requires `--confirm`. No installer commands were run.');
        process.exitCode = 1;
        return;
      }

      if ((opts.runner ?? 'noop') === 'shell' && !configLoaded.config.skills.allow_shell_runner) {
        console.error("Runner 'shell' is disabled by config. Set skills.allow_shell_runner: true to enable it.");
        process.exitCode = 1;
        return;
      }

      runSkillExecuteAction(skill, {
        asJson: opts.json ?? false,
        confirmed: opts.confirm ?? false,
        executionRequested: opts.execute ?? false,
        commandRunner: runnerResolution.runner,
        configPolicyEnabled: configLoaded.config.skills.installation_execution === 'enabled',
        runnerMode: runnerResolution.mode,
      });
    });
}
