import { createHash } from "node:crypto";
import { execFileSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { coreInternalFetch } from "./microservices";

const DEFAULT_REPO = "pikasTech/unidesk";
const GITHUB_API = (process.env.UNIDESK_GITHUB_API_URL ?? "https://api.github.com").replace(/\/$/u, "");
const USER_AGENT = "unidesk-cli-gh";
const PREVIEW_CHARS = 240;
const REQUEST_TIMEOUT_MS = 20_000;
const MIN_SAFE_ISSUE_BODY_CHARS = 20;
const MAX_INLINE_ISSUE_COMMENT_BODY_CHARS = 1000;
const GITHUB_REST_PAGE_SIZE = 100;
const MAX_ISSUE_LIST_LIMIT = 1000;
const DEFAULT_STALE_CLOSE_INACTIVE_HOURS = 48;
const MAX_STALE_CLOSE_INACTIVE_HOURS = 24 * 365 * 10;
const ISSUE_LIFECYCLE_PREVIEW_LIMIT = 80;
const DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_BASE_URL = "http://backend-core:8080/api/microservices/claudeqq/proxy";
const DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_USER_ID = "645275593";
const CODE_QUEUE_BOARD_TARGET_ISSUE = 20;
const COMMANDER_BRIEF_TARGET_ISSUE = 24;
const MAX_PR_FILES_LIMIT = 3000;
const DEFAULT_BOARD_KNOWN_META_ISSUES = [CODE_QUEUE_BOARD_TARGET_ISSUE, COMMANDER_BRIEF_TARGET_ISSUE] as const;
const BOARD_AUDIT_REQUIRED_COLUMNS = ["branch", "acceptance", "relatedTask", "progress"] as const;
const BOARD_ROW_FIELDS = ["progress", "status", "validation", "branch", "tasks", "focus"] as const;
const BOARD_ROW_UPSERT_TEXT_FIELDS = ["category", "branch", "tasks", "summary", "focus", "validation", "progress"] as const;
const ISSUE_VIEW_JSON_FIELDS = ["body", "title", "state", "closed", "closedAt", "comments", "number", "url", "author", "createdAt", "updatedAt"] as const;
const ISSUE_LIST_JSON_FIELDS = ["number", "title", "state", "closed", "closedAt", "url", "updatedAt", "createdAt", "author", "labels"] as const;
const PR_LIST_JSON_FIELDS = [
  "body",
  "title",
  "state",
  "number",
  "url",
  "author",
  "head",
  "base",
  "draft",
  "createdAt",
  "updatedAt",
  "closedAt",
  "merged",
  "mergedAt",
  "mergeCommit",
  "headRefName",
  "baseRefName"
] as const;
const PR_READ_JSON_FIELDS = ["body", "title", "state", "stateDetail", "number", "url", "author", "head", "base", "draft", "createdAt", "updatedAt", "closed", "closedAt", "merged", "mergedAt", "mergeCommit", "headRefName", "baseRefName", "mergeable", "mergeStateStatus", "statusCheckRollup"] as const;
const PR_CLOSEOUT_JSON_FIELDS = ["mergeable", "mergeStateStatus", "statusCheckRollup"] as const;
const PR_CLOSEOUT_VIEW_JSON = "headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup";
const ISSUE_LIST_STATES = ["open", "closed", "all"] as const;
const BODY_UPDATE_MODES = ["replace", "append"] as const;
const BOARD_MUTATION_SECTIONS = ["open", "closed"] as const;
const BOARD_GITHUB_STATUSES = ["OPEN", "CLOSED"] as const;
const GH_VALUE_OPTIONS = new Set([
  "--repo", "--limit", "--board-issue", "--known-meta-issue", "--ignore-issue", "--title",
  "--body-file", "--body", "--base", "--head", "--json", "--state", "--mode",
  "--expect-updated-at", "--expect-body-sha", "--body-profile", "--label", "--field",
  "--value", "--section", "--to", "--status", "--row-file", "--category", "--branch",
  "--tasks", "--summary", "--focus", "--validation", "--progress", "--number", "--pr",
  "--search", "--inactive-hours", "--comment", "--comment-file",
]);
const GH_FLAG_OPTIONS = new Set(["--dry-run", "--draft", "--notify-claudeqq-brief-diff", "--allow-short-body", "--raw", "--full", "--stat", "--merge", "--squash", "--rebase", "--delete-branch"]);
const MIN_SAFE_BODY_SCAN_CHARS = MIN_SAFE_ISSUE_BODY_CHARS;
const ISSUE_SCAN_MAX_FINDINGS = 60;
const ISSUE_BODY_PROFILES = {
  "code-queue-board": {
    label: "Code Queue long board issue #20",
    issueNumber: CODE_QUEUE_BOARD_TARGET_ISSUE,
    requiredHeadings: ["## 看板（OPEN）"],
  },
  "commander-brief": {
    label: "Commander brief body-only issue (#24 legacy or daily rolling brief)",
    legacyIssueNumber: COMMANDER_BRIEF_TARGET_ISSUE,
    requiredHeadings: ["## 常驻观察与长期建议"],
  },
} as const;
const DAILY_COMMANDER_BRIEF_TITLE_PATTERN = /^\d{4}-\d{2}-\d{2}\s+指挥简报（北京时间）$/u;
const DAILY_COMMANDER_BRIEF_BODY_HEADING_PATTERN = /^#\s+\d{4}-\d{2}-\d{2}\s+指挥简报（北京时间）\s*$/u;
const LEGACY_COMMANDER_BRIEF_BODY_HEADING_PATTERN = /^#\s+指挥简报\s*$/u;
const COMMANDER_BRIEF_UPDATE_HEADING_PATTERNS = [
  /^##\s+更新\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}\s+北京时间\s*$/u,
  /^##\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}\s+北京时间指挥更新\s*$/u,
  /^###\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}\s+CST[:：].*$/u,
] as const;

type IssueViewJsonField = typeof ISSUE_VIEW_JSON_FIELDS[number];
type IssueListJsonField = typeof ISSUE_LIST_JSON_FIELDS[number];
type PrListJsonField = typeof PR_LIST_JSON_FIELDS[number];
type PrReadJsonField = typeof PR_READ_JSON_FIELDS[number];
type IssueListState = typeof ISSUE_LIST_STATES[number];
type PrListState = typeof ISSUE_LIST_STATES[number];
type BodyUpdateMode = typeof BODY_UPDATE_MODES[number];
type PullRequestMergeMethod = "merge" | "squash" | "rebase";
type BoardMutationSection = typeof BOARD_MUTATION_SECTIONS[number];
type BoardGithubStatus = typeof BOARD_GITHUB_STATUSES[number];
type IssueBodyProfileName = keyof typeof ISSUE_BODY_PROFILES;
type IssueBodyProfileOption = "auto" | IssueBodyProfileName;
type EscapeFindingClassification = "suspected-pollution" | "explanatory-mention" | "risk";
type EscapeFindingSeverity = "low" | "medium" | "high";
type EscapeBodyKind = "issue-body" | "comment-body";
type CleanupSuggestionAction = "rewrite-issue-body-with-body-file" | "review-comment-manually" | "review-body-length" | "no-cleanup-needed";
type BoardSectionKind = "open" | "closed";
type BoardRequiredColumn = typeof BOARD_AUDIT_REQUIRED_COLUMNS[number];
type BoardColumnKind = BoardRequiredColumn | "focus" | "githubStatus" | "category" | "summary";
type BoardRowField = typeof BOARD_ROW_FIELDS[number];
type BoardRowUpsertTextField = typeof BOARD_ROW_UPSERT_TEXT_FIELDS[number];
type BoardRowUpsertField = BoardRowUpsertTextField | "status";
type BoardIgnoreReason = "known-meta" | "ignored" | "brief-index-managed";

type GitHubDegradedReason =
  | "missing-binary"
  | "missing-token"
  | "auth-failed"
  | "egress-failed"
  | "github-transient"
  | "network-proxy-failed"
  | "permission-denied"
  | "repo-not-found"
  | "repo-forbidden"
  | "issue-not-found"
  | "pr-not-found"
  | "scope-insufficient"
  | "validation-failed"
  | "invalid-response"
  | "unsupported-command";

type RunnerDisposition = "infra-blocked" | "business-failed";
type ClaudeQqTargetType = "private" | "group";
type CommanderBriefDiffMode = "identical" | "append-only" | "heading-diff" | "unreliable";

interface CommanderBriefDiff {
  ok: boolean;
  mode: CommanderBriefDiffMode;
  message: string;
  chars: number;
  sections: string[];
  sectionCount: number;
  skippedReason?: string;
}

interface ClaudeQqConfig {
  enabled: boolean;
  baseUrl: string;
  targetType: ClaudeQqTargetType;
  userId?: string;
  groupId?: string;
  timeoutMs: number;
}

interface ClaudeQqSendResult {
  ok: boolean;
  attempted: boolean;
  skipped?: boolean;
  skippedReason?: string;
  endpoint?: string;
  status?: number;
  degradedReason?: string;
  message?: string;
  response?: unknown;
  target: Record<string, unknown>;
}

interface ClaudeQqEndpointResult {
  ok: boolean;
  endpoint: string;
  status?: number;
  degradedReason?: string;
  message?: string;
  response?: unknown;
}

interface CommanderBriefSection {
  heading: string;
  text: string;
  startLine: number;
}

interface EscapePatternDefinition {
  kind: string;
  pattern: RegExp;
  description: string;
}

interface EscapeMatchFinding {
  type: "issue" | "comment";
  bodyKind: EscapeBodyKind;
  bodyId: string;
  issueNumber: number;
  issueId: number;
  commentId?: number;
  url: string;
  kind: string;
  classification: EscapeFindingClassification;
  severity: EscapeFindingSeverity;
  reason: string;
  snippet: string;
  lineNumber: number;
  column: number;
  match: string;
  bodyChars: number | null;
  bodyTrimmedChars: number | null;
  cleanupPreview?: {
    before: string;
    after: string;
  };
}

interface EscapeCleanupSuggestion {
  type: EscapeBodyKind;
  bodyId: string;
  issueNumber: number;
  issueId: number;
  commentId?: number;
  url: string;
  classification: EscapeFindingClassification;
  action: CleanupSuggestionAction;
  reason: string;
  findings: Array<Pick<EscapeMatchFinding, "kind" | "classification" | "severity" | "lineNumber" | "column" | "snippet" | "match">>;
  plannedCommand?: string;
  bodyFileHint?: string;
  preview?: {
    before: string;
    after: string;
  };
}

interface EscapeScanErrorFinding {
  type: "comment-scan-error";
  bodyKind: EscapeBodyKind;
  bodyId: string;
  issueNumber: number;
  issueId: number;
  url: string;
  degradedReason: GitHubDegradedReason;
  runnerDisposition: RunnerDisposition;
  details: GitHubErrorPayload;
}

type EscapeScanEntry = EscapeMatchFinding | EscapeScanErrorFinding;

interface BoardTableRow {
  section: BoardSectionKind;
  lineNumber: number;
  raw: string;
  cells: string[];
  issueNumbers: number[];
  issueNumber: number | null;
  title: string | null;
  columns: Partial<Record<BoardRequiredColumn, string>>;
}

interface BoardTableSection {
  kind: BoardSectionKind;
  heading: string;
  headingLine: number;
  headerLine: number;
  headers: string[];
  rows: BoardTableRow[];
}

interface BoardIssueEntry {
  number: number;
  title: string;
  state: string;
  url: string;
}

interface BoardIgnoredIssue {
  number: number;
  reason: BoardIgnoreReason;
  title?: string;
  state?: string;
  url?: string;
}

interface BoardRowValidationWarning {
  issueNumber: number | null;
  section: BoardSectionKind;
  lineNumber: number;
  kind: string;
  message: string;
  missingColumns?: BoardRequiredColumn[];
  columns?: Partial<Record<BoardRequiredColumn, string>>;
  rowPreview: string;
}

interface BoardRowLookup {
  ok: true;
  section: BoardTableSection;
  row: BoardTableRow;
  matches: Array<{ section: BoardTableSection; row: BoardTableRow }>;
}

interface BoardRowUpdatePlanResult {
  ok: true;
  plan: Record<string, unknown>;
  newBody: string;
}

interface BoardRowMutationPlanResult {
  ok: true;
  plan: Record<string, unknown>;
  newBody: string;
}

interface BoardRowUpsertValues {
  category?: string;
  branch?: string;
  tasks?: string;
  summary?: string;
  focus?: string;
  validation?: string;
  progress?: string;
  status?: BoardGithubStatus;
}

interface GitHubCommandResult {
  ok: boolean;
  repo: string;
  command: string;
  degradedReason?: GitHubDegradedReason;
  degraded?: GitHubDegradedReason[];
  runnerDisposition?: RunnerDisposition;
  [key: string]: unknown;
}

type GitHubCommandFailure = GitHubCommandResult & { ok: false };

interface GitHubTokenProbe {
  present: boolean;
  source: "GH_TOKEN" | "GITHUB_TOKEN" | "gh-auth-token" | null;
  ghFallbackAttempted: boolean;
  ghBinaryFound?: boolean;
  ghAuthTokenAvailable?: boolean | null;
}

interface GitHubOptions {
  repo: string;
  dryRun: boolean;
  raw: boolean;
  full: boolean;
  limit: number;
  inactiveHours: number;
  boardIssue: number;
  knownMetaIssues: number[];
  ignoredIssues: number[];
  draft: boolean;
  notifyClaudeQqBriefDiff: boolean;
  allowShortBody: boolean;
  labels: string[];
  search?: string;
  title?: string;
  body?: string;
  bodyFile?: string;
  comment?: string;
  commentFile?: string;
  base?: string;
  head?: string;
  jsonFields?: IssueViewJsonField[];
  issueListJsonFields?: IssueListJsonField[];
  prListJsonFields?: PrListJsonField[];
  prJsonFields?: PrReadJsonField[];
  listState: IssueListState;
  prListState: PrListState;
  mode: BodyUpdateMode;
  expectUpdatedAt?: string;
  expectBodySha?: string;
  bodyProfile: IssueBodyProfileOption;
  boardRowField?: BoardRowField;
  boardRowValue?: string;
  boardRowFile?: string;
  boardSection?: BoardMutationSection;
  boardMoveTo?: BoardMutationSection;
  boardGithubStatus?: BoardGithubStatus;
  boardRowUpsertValues: BoardRowUpsertValues;
  mergeMethod: PullRequestMergeMethod;
  deleteBranch: boolean;
}

interface GitHubShorthandReference {
  input: string;
  repo: string;
  number: number;
  source?: "owner-repo-number" | "github-url" | "number-option";
  urlKind?: "issue" | "pr";
  standardCommand?: string;
}

interface GitHubResolvedNumberReference {
  repo: string;
  number: number;
  shorthand?: GitHubShorthandReference;
}

interface IssueProfileValidationContext {
  issueTitle?: string | null;
  issueBody?: string | null;
  issueMetadataFetched?: boolean;
}

interface GitHubErrorPayload {
  ok: false;
  degradedReason: GitHubDegradedReason;
  runnerDisposition: RunnerDisposition;
  status?: number;
  message: string;
  retryable?: boolean;
  commanderAction?: string;
  details?: unknown;
  scopes?: {
    accepted: string | null;
    token: string | null;
  };
  request?: {
    method: string;
    path: string;
  };
}

interface GitHubIssue {
  id: number;
  number: number;
  title: string;
  body: string | null;
  state: string;
  html_url: string;
  comments: number;
  user?: { login?: string };
  labels?: Array<string | { name?: string; color?: string; description?: string | null }>;
  pull_request?: unknown;
  created_at?: string;
  updated_at?: string;
  closed_at?: string | null;
}

interface GitHubComment {
  id: number;
  body: string | null;
  html_url: string;
  user?: { login?: string };
  created_at?: string;
  updated_at?: string;
}

interface GitHubIssueSearchResponse {
  total_count?: number;
  incomplete_results?: boolean;
  items?: GitHubIssue[];
}

interface GitHubIssueListPage {
  path: string;
  rawCount: number;
  issueCount: number;
}

interface GitHubIssueListResult {
  items: GitHubIssue[];
  rawCount: number;
  fetchedPages: number;
  pageSize: number;
  exhausted: boolean;
  hasMore: boolean;
  pages: GitHubIssueListPage[];
  searchTotalCount?: number;
  searchIncomplete?: boolean;
}

interface GitHubPullRequest {
  id: number;
  number: number;
  title: string;
  body: string | null;
  state: string;
  html_url: string;
  draft?: boolean;
  user?: { login?: string };
  head?: { ref?: string; sha?: string; repo?: { full_name?: string | null } | null };
  base?: { ref?: string; sha?: string };
  additions?: number;
  deletions?: number;
  changed_files?: number;
  commits?: number;
  closed_at?: string | null;
  merged?: boolean | null;
  merged_at?: string | null;
  merge_commit_sha?: string | null;
  mergeable?: string | null;
  merge_state_status?: string | null;
  created_at?: string;
  updated_at?: string;
}

interface GitHubPullRequestFile {
  sha?: string;
  filename: string;
  status?: string;
  additions?: number;
  deletions?: number;
  changes?: number;
  blob_url?: string;
  raw_url?: string;
  contents_url?: string;
  previous_filename?: string;
  patch?: string;
}

interface GitHubPullRequestGraphqlStatusContext {
  __typename?: string;
  name?: string | null;
  status?: string | null;
  conclusion?: string | null;
  context?: string | null;
  state?: string | null;
  targetUrl?: string | null;
  description?: string | null;
}

interface GitHubPullRequestGraphqlStatusCheckRollup {
  state?: string | null;
  contexts?: {
    nodes?: GitHubPullRequestGraphqlStatusContext[] | null;
  } | null;
}

interface GitHubPullRequestGraphqlMetadata {
  mergeable?: string | null;
  mergeStateStatus?: string | null;
  headRefName?: string | null;
  baseRefName?: string | null;
  statusCheckRollup?: GitHubPullRequestGraphqlStatusCheckRollup | null;
}

interface GitHubRepository {
  id?: number;
  full_name?: string;
  private?: boolean;
  default_branch?: string;
  permissions?: Record<string, boolean>;
}

interface GitHubBranch {
  name?: string;
  commit?: { sha?: string };
}

interface GitHubCompare {
  status?: string;
  ahead_by?: number;
  behind_by?: number;
  total_commits?: number;
  html_url?: string;
  base_commit?: { sha?: string };
  merge_base_commit?: { sha?: string };
}

function optionValue(args: string[], name: string): string | undefined {
  const index = args.indexOf(name);
  if (index === -1) return undefined;
  const value = args[index + 1];
  if (value === undefined || value.length === 0 || value.startsWith("--")) throw new Error(`${name} requires a value`);
  return value;
}

function optionValues(args: string[], name: string): string[] {
  const values: string[] = [];
  for (let index = 0; index < args.length; index += 1) {
    if (args[index] !== name) continue;
    const value = args[index + 1];
    if (value === undefined || value.length === 0 || value.startsWith("--")) throw new Error(`${name} requires a value`);
    values.push(value);
    index += 1;
  }
  return values;
}

function hasFlag(args: string[], name: string): boolean {
  return args.includes(name);
}

function optionWasProvided(args: string[], name: string): boolean {
  return args.includes(name);
}

function commaListOption(args: string[], name: string): string[] | undefined {
  const raw = optionValue(args, name);
  if (raw === undefined) return undefined;
  const values = raw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
  if (values.length === 0) throw new Error(`${name} requires at least one comma-separated field`);
  return values;
}

function labelsOption(args: string[]): string[] {
  const rawValues = optionValues(args, "--label");
  const labels: string[] = [];
  const seen = new Set<string>();
  for (const raw of rawValues) {
    const parts = raw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
    if (parts.length === 0) throw new Error("--label requires at least one non-empty label");
    for (const label of parts) {
      if (seen.has(label)) continue;
      labels.push(label);
      seen.add(label);
    }
  }
  return labels;
}

function positiveIntegerValuesOption(args: string[], name: string): number[] {
  const values: number[] = [];
  const seen = new Set<number>();
  for (const raw of optionValues(args, name)) {
    const parts = raw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
    if (parts.length === 0) throw new Error(`${name} requires at least one positive integer`);
    for (const part of parts) {
      const value = Number(part.replace(/^#/u, ""));
      if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} values must be positive issue numbers`);
      if (seen.has(value)) continue;
      values.push(value);
      seen.add(value);
    }
  }
  return values;
}

function positiveIntegerSingleOption(args: string[], name: string, defaultValue: number): number {
  const raw = optionValue(args, name);
  if (raw === undefined) return defaultValue;
  const value = Number(raw.replace(/^#/u, ""));
  if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} must be a positive issue number`);
  return value;
}

function positiveIntegerOption(args: string[], name: string, defaultValue: number, maxValue: number): number {
  const raw = optionValue(args, name);
  if (raw === undefined) return defaultValue;
  const value = Number(raw);
  if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer`);
  return Math.min(value, maxValue);
}

function positiveNumberOption(args: string[], name: string, defaultValue: number, maxValue: number): number {
  const raw = optionValue(args, name);
  if (raw === undefined) return defaultValue;
  const value = Number(raw);
  if (!Number.isFinite(value) || value <= 0) throw new Error(`${name} must be a positive number`);
  return Math.min(value, maxValue);
}

function validateEnumValue<T extends string>(name: string, raw: string, allowedValues: readonly T[]): T {
  if ((allowedValues as readonly string[]).includes(raw)) return raw as T;
  throw new Error(`unsupported ${name} ${raw}; supported values: ${allowedValues.join(",")}`);
}

function validateJsonFields<T extends string>(command: string, requested: string[] | undefined, allowedFields: readonly T[]): T[] | undefined {
  if (requested === undefined) return undefined;
  const allowed = new Set<string>(allowedFields);
  const unsupported = requested.filter((field) => !allowed.has(field));
  if (unsupported.length > 0) {
    throw new Error(`unsupported ${command} --json field(s): ${unsupported.join(", ")}; supported fields: ${allowedFields.join(",")}`);
  }
  return requested as T[];
}

function parseIssueViewJsonFields(requested: string[] | undefined): IssueViewJsonField[] | undefined {
  return validateJsonFields("gh issue read/view", requested, ISSUE_VIEW_JSON_FIELDS);
}

function parseIssueListJsonFields(requested: string[] | undefined): IssueListJsonField[] | undefined {
  return validateJsonFields("gh issue list", requested, ISSUE_LIST_JSON_FIELDS);
}

function parsePrListJsonFields(requested: string[] | undefined): PrListJsonField[] | undefined {
  if (requested !== undefined) {
    const listFields = new Set<string>(PR_LIST_JSON_FIELDS);
    const closeoutFields = requested.filter((field) => (PR_CLOSEOUT_JSON_FIELDS as readonly string[]).includes(field));
    const unsupported = requested.filter((field) => !listFields.has(field));
    if (closeoutFields.length > 0) {
      throw new Error(`unsupported gh pr list --json closeout field(s): ${closeoutFields.join(", ")}; use gh pr view <number> --json ${PR_CLOSEOUT_VIEW_JSON} for mergeability/statusCheckRollup metadata; pr list supported fields: ${PR_LIST_JSON_FIELDS.join(",")}`);
    }
    if (unsupported.length > 0) {
      throw new Error(`unsupported gh pr list --json field(s): ${unsupported.join(", ")}; supported fields: ${PR_LIST_JSON_FIELDS.join(",")}; closeout metadata requires gh pr view <number> --json ${PR_CLOSEOUT_VIEW_JSON}`);
    }
  }
  return validateJsonFields("gh pr list", requested, PR_LIST_JSON_FIELDS);
}

function parsePrReadJsonFields(requested: string[] | undefined): PrReadJsonField[] | undefined {
  return validateJsonFields("gh pr read/view", requested, PR_READ_JSON_FIELDS);
}

function isIssueReadCommand(sub: string | undefined): boolean {
  return sub === "read" || sub === "view";
}

function isPrReadCommand(sub: string | undefined): boolean {
  return sub === "read" || sub === "view";
}

function allowsNumberTargetAlias(top: string | undefined, sub: string | undefined, third: string | undefined): boolean {
  if (top === "preflight") return true;
  if (top === "issue") {
    if (sub === "read" || sub === "view" || sub === "edit" || sub === "update" || sub === "close" || sub === "reopen" || sub === "delete") return true;
    if (sub === "comment") return true;
    if (sub === "board-row" && ["get", "update", "add", "move", "delete", "upsert"].includes(third ?? "")) return true;
    return false;
  }
  if (top === "pr") {
    if (sub === "read" || sub === "view" || sub === "files" || sub === "diff" || sub === "preflight" || sub === "closeout" || sub === "edit" || sub === "update" || sub === "close" || sub === "reopen" || sub === "merge" || sub === "delete") return true;
    if (sub === "comment") return true;
  }
  return false;
}

function parseIssueListState(args: string[]): IssueListState {
  const raw = optionValue(args, "--state") ?? "open";
  if ((ISSUE_LIST_STATES as readonly string[]).includes(raw)) return raw as IssueListState;
  throw new Error(`unsupported gh issue list --state ${raw}; supported states: ${ISSUE_LIST_STATES.join(",")}`);
}

function parsePrListState(args: string[]): PrListState {
  const raw = optionValue(args, "--state") ?? "all";
  if ((ISSUE_LIST_STATES as readonly string[]).includes(raw)) return raw as PrListState;
  throw new Error(`unsupported gh pr list --state ${raw}; supported states: ${ISSUE_LIST_STATES.join(",")}`);
}

function parseBodyUpdateMode(args: string[]): BodyUpdateMode {
  const raw = optionValue(args, "--mode") ?? "replace";
  if ((BODY_UPDATE_MODES as readonly string[]).includes(raw)) return raw as BodyUpdateMode;
  throw new Error(`unsupported --mode ${raw}; supported modes: ${BODY_UPDATE_MODES.join(",")}`);
}

function parsePullRequestMergeMethod(args: string[]): PullRequestMergeMethod {
  const selected = ["merge", "squash", "rebase"].filter((method) => hasFlag(args, `--${method}`));
  if (selected.length > 1) throw new Error("choose only one PR merge method flag: --merge, --squash, or --rebase");
  return (selected[0] ?? "merge") as PullRequestMergeMethod;
}

function parseIssueBodyProfile(args: string[]): IssueBodyProfileOption {
  const raw = optionValue(args, "--body-profile") ?? "auto";
  if (raw === "auto" || raw === "code-queue-board" || raw === "commander-brief") return raw;
  throw new Error(`unsupported --body-profile ${raw}; supported profiles: auto, code-queue-board, commander-brief`);
}

function parseBoardMutationSection(args: string[], name: "--section" | "--to"): BoardMutationSection | undefined {
  const raw = optionValue(args, name);
  if (raw === undefined) return undefined;
  return validateEnumValue(name, raw, BOARD_MUTATION_SECTIONS);
}

function parseBoardGithubStatus(args: string[]): BoardGithubStatus | undefined {
  const raw = optionValue(args, "--status");
  if (raw === undefined) return undefined;
  return validateEnumValue("--status", raw, BOARD_GITHUB_STATUSES);
}

function parseBoardRowField(args: string[]): BoardRowField | undefined {
  const raw = optionValue(args, "--field");
  if (raw === undefined) return undefined;
  return validateEnumValue("--field", raw, BOARD_ROW_FIELDS);
}

function parseBoardRowUpsertValues(args: string[]): BoardRowUpsertValues {
  return {
    category: optionValue(args, "--category"),
    branch: optionValue(args, "--branch"),
    tasks: optionValue(args, "--tasks"),
    summary: optionValue(args, "--summary"),
    focus: optionValue(args, "--focus"),
    validation: optionValue(args, "--validation"),
    progress: optionValue(args, "--progress"),
    status: parseBoardGithubStatus(args),
  };
}

function validateKnownOptions(args: string[]): void {
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    if (!arg.startsWith("--")) continue;
    if (GH_FLAG_OPTIONS.has(arg)) continue;
    if (GH_VALUE_OPTIONS.has(arg)) {
      index += 1;
      continue;
    }
    throw new Error(`unknown gh option: ${arg}`);
  }
}

function positionalArgs(args: string[]): string[] {
  const positionals: string[] = [];
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    if (arg.startsWith("--")) {
      if (GH_VALUE_OPTIONS.has(arg)) index += 1;
      continue;
    }
    positionals.push(arg);
  }
  return positionals;
}

function isOwnerRepo(value: string): boolean {
  return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(value);
}

function resolveRepoOption(args: string[]): string {
  const [top, sub] = args;
  const explicitRepo = optionValue(args, "--repo");
  if ((top === "issue" || top === "pr") && sub === "list") {
    const positionals = positionalArgs(args.slice(2));
    if (positionals.length > 1) {
      throw new Error(`gh ${top} list accepts at most one positional owner/repo; use --repo owner/name`);
    }
    const positionalRepo = positionals[0];
    if (positionalRepo !== undefined) {
      if (!isOwnerRepo(positionalRepo)) {
        throw new Error(`gh ${top} list positional argument must be owner/repo; use --repo owner/name`);
      }
      if (explicitRepo !== undefined && explicitRepo !== positionalRepo) {
        throw new Error(`gh ${top} list received positional repo ${positionalRepo} and --repo ${explicitRepo}; use one repo target, for example --repo ${positionalRepo}`);
      }
      return positionalRepo;
    }
  }
  return explicitRepo ?? DEFAULT_REPO;
}

function parseOptions(args: string[]): GitHubOptions {
  validateKnownOptions(args);
  const [top, sub] = args;
  const requestedJsonFields = commaListOption(args, "--json");
  const limitMax = top === "pr" && (sub === "files" || sub === "diff")
    ? MAX_PR_FILES_LIMIT
    : top === "issue" && (sub === "list" || sub === "stale-close" || sub === "scan-escape" || sub === "cleanup-plan")
      ? MAX_ISSUE_LIST_LIMIT
      : 100;
  return {
    repo: resolveRepoOption(args),
    dryRun: hasFlag(args, "--dry-run"),
    raw: hasFlag(args, "--raw"),
    full: hasFlag(args, "--full"),
    limit: positiveIntegerOption(args, "--limit", top === "issue" && sub === "board-audit" ? 100 : top === "issue" && sub === "stale-close" ? MAX_ISSUE_LIST_LIMIT : 30, limitMax),
    inactiveHours: positiveNumberOption(args, "--inactive-hours", DEFAULT_STALE_CLOSE_INACTIVE_HOURS, MAX_STALE_CLOSE_INACTIVE_HOURS),
    boardIssue: positiveIntegerSingleOption(args, "--board-issue", CODE_QUEUE_BOARD_TARGET_ISSUE),
    knownMetaIssues: positiveIntegerValuesOption(args, "--known-meta-issue"),
    ignoredIssues: positiveIntegerValuesOption(args, "--ignore-issue"),
    draft: hasFlag(args, "--draft"),
    notifyClaudeQqBriefDiff: hasFlag(args, "--notify-claudeqq-brief-diff"),
    allowShortBody: hasFlag(args, "--allow-short-body"),
    labels: labelsOption(args),
    search: optionValue(args, "--search"),
    title: optionValue(args, "--title"),
    body: optionValue(args, "--body"),
    bodyFile: optionValue(args, "--body-file"),
    comment: optionValue(args, "--comment"),
    commentFile: optionValue(args, "--comment-file"),
    base: optionValue(args, "--base"),
    head: optionValue(args, "--head"),
    jsonFields: top === "issue" && isIssueReadCommand(sub) ? parseIssueViewJsonFields(requestedJsonFields) : undefined,
    issueListJsonFields: top === "issue" && sub === "list" ? parseIssueListJsonFields(requestedJsonFields) : undefined,
    prListJsonFields: top === "pr" && sub === "list" ? parsePrListJsonFields(requestedJsonFields) : undefined,
    prJsonFields: top === "pr" && isPrReadCommand(sub) ? parsePrReadJsonFields(requestedJsonFields) : undefined,
    listState: top === "issue" && (sub === "list" || sub === "board-row" && args[2] === "list") ? parseIssueListState(args) : "open",
    prListState: top === "pr" && sub === "list" ? parsePrListState(args) : "all",
    mode: parseBodyUpdateMode(args),
    expectUpdatedAt: optionValue(args, "--expect-updated-at"),
    expectBodySha: optionValue(args, "--expect-body-sha"),
    bodyProfile: parseIssueBodyProfile(args),
    boardRowField: parseBoardRowField(args),
    boardRowValue: optionValue(args, "--value"),
    boardRowFile: optionValue(args, "--row-file"),
    boardSection: parseBoardMutationSection(args, "--section"),
    boardMoveTo: parseBoardMutationSection(args, "--to"),
    boardGithubStatus: parseBoardGithubStatus(args),
    boardRowUpsertValues: parseBoardRowUpsertValues(args),
    mergeMethod: parsePullRequestMergeMethod(args),
    deleteBranch: hasFlag(args, "--delete-branch"),
  };
}

function parseNumber(raw: string | undefined, label: string): number {
  if (raw === undefined) throw new Error(`${label} requires a number`);
  const value = Number(raw);
  if (!Number.isInteger(value) || value <= 0) throw new Error(`${label} must be a positive integer`);
  return value;
}

function parseNumberForCommand(repo: string, raw: string | undefined, label: string): number | GitHubCommandResult {
  try {
    return parseNumber(raw, label);
  } catch (error) {
    return validationError(label, repo, error instanceof Error ? error.message : String(error));
  }
}

function parsePositionalNumberForCommand(repo: string, args: string[], startIndex: number, label: string): number | GitHubCommandResult {
  const targets = positionalArgs(args.slice(startIndex));
  if (targets.length !== 1) {
    return validationError(label, repo, `${label} requires exactly one positive integer positional argument`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${repo}`,
        `bun scripts/cli.ts gh ${label} --repo ${repo} <number>`,
      ],
    });
  }
  return parseNumberForCommand(repo, targets[0], label);
}

function resolvedNumberOptionReference(kind: "issue" | "pr", command: string, repo: string, raw: string): GitHubResolvedNumberReference | GitHubCommandResult {
  const number = parseNumberForCommand(repo, raw, command);
  if (typeof number !== "number") return number;
  return {
    repo,
    number,
    shorthand: {
      input: `--number ${number}`,
      repo,
      number,
      source: "number-option",
      standardCommand: `bun scripts/cli.ts gh ${command} ${number} --repo ${repo}`,
      urlKind: kind,
    },
  };
}

function resolvePositionalNumberReference(kind: "issue" | "pr", args: string[], startIndex: number, label: string, options: GitHubOptions): GitHubResolvedNumberReference | GitHubCommandResult {
  const targets = positionalArgs(args.slice(startIndex));
  const numberOption = optionValue(args, "--number");
  if (targets.length > 0 && numberOption !== undefined) {
    return validationError(label, options.repo, `${label} accepts either a positional numeric target or --number, not both`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  if (numberOption !== undefined) return resolvedNumberOptionReference(kind, label, options.repo, numberOption);
  if (targets.length !== 1) {
    return validationError(label, options.repo, `${label} requires exactly one positive integer or owner/repo#number positional argument`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  const shorthand = parseOwnerRepoNumberShorthand(targets[0]);
  if (shorthand !== null) {
    const explicitRepo = optionValue(args, "--repo");
    if (explicitRepo !== undefined && explicitRepo !== shorthand.repo) {
      return validationError(label, explicitRepo, `${label} target ${shorthand.input} resolves to repo ${shorthand.repo}, but --repo ${explicitRepo} was also provided.`, {
        shorthand,
        explicitRepo,
      });
    }
    return { repo: shorthand.repo, number: shorthand.number, shorthand };
  }
  const number = parseNumberForCommand(options.repo, targets[0], label);
  if (typeof number !== "number") return number;
  return { repo: options.repo, number };
}

function resolvePositionalPrReference(args: string[], startIndex: number, label: string, options: GitHubOptions): GitHubResolvedNumberReference | GitHubCommandResult {
  const targets = positionalArgs(args.slice(startIndex));
  const prOption = optionValue(args, "--pr");
  const numberOption = optionValue(args, "--number");
  if (prOption !== undefined && numberOption !== undefined) {
    return validationError(label, options.repo, `${label} accepts either --pr or --number as a compatibility alias, not both`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  if (targets.length > 0 && (prOption !== undefined || numberOption !== undefined)) {
    return validationError(label, options.repo, `${label} accepts either a positional PR target or a compatibility number option, not both`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --pr <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  if (numberOption !== undefined) return resolvedNumberOptionReference("pr", label, options.repo, numberOption);
  const effectiveTargets = prOption !== undefined ? [prOption] : targets;
  if (effectiveTargets.length !== 1) {
    return validationError(label, options.repo, `${label} requires exactly one positive integer or owner/repo#number positional argument`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --pr <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  const shorthand = parseOwnerRepoNumberShorthand(effectiveTargets[0]);
  if (shorthand !== null) {
    const explicitRepo = optionValue(args, "--repo");
    if (explicitRepo !== undefined && explicitRepo !== shorthand.repo) {
      return validationError(label, explicitRepo, `${label} target ${shorthand.input} resolves to repo ${shorthand.repo}, but --repo ${explicitRepo} was also provided.`, {
        shorthand,
        explicitRepo,
      });
    }
    return { repo: shorthand.repo, number: shorthand.number, shorthand };
  }
  const number = parseNumberForCommand(options.repo, effectiveTargets[0], label);
  if (typeof number !== "number") return number;
  return { repo: options.repo, number };
}

function resolvePositionalIssueReference(args: string[], startIndex: number, label: string, options: GitHubOptions, allowNumberOption = true): GitHubResolvedNumberReference | GitHubCommandResult {
  const targets = positionalArgs(args.slice(startIndex));
  const numberOption = allowNumberOption ? optionValue(args, "--number") : undefined;
  if (targets.length > 0 && numberOption !== undefined) {
    return validationError(label, options.repo, `${label} accepts either a positional issue target or --number, not both`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`,
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  if (numberOption !== undefined) return resolvedNumberOptionReference("issue", label, options.repo, numberOption);
  if (targets.length !== 1) {
    return validationError(label, options.repo, `${label} requires exactly one positive integer or owner/repo#number positional argument`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${label} <number> --repo ${options.repo}`,
        ...(allowNumberOption ? [`bun scripts/cli.ts gh ${label} --number <number> --repo ${options.repo}`] : []),
        `bun scripts/cli.ts gh ${label} ${options.repo}#<number>`,
      ],
    });
  }
  const shorthand = parseOwnerRepoNumberShorthand(targets[0]);
  if (shorthand !== null) {
    const explicitRepo = optionValue(args, "--repo");
    if (explicitRepo !== undefined && explicitRepo !== shorthand.repo) {
      return validationError(label, explicitRepo, `${label} target ${shorthand.input} resolves to repo ${shorthand.repo}, but --repo ${explicitRepo} was also provided.`, {
        shorthand,
        explicitRepo,
      });
    }
    return { repo: shorthand.repo, number: shorthand.number, shorthand };
  }
  const number = parseNumberForCommand(options.repo, targets[0], label);
  if (typeof number !== "number") return number;
  return { repo: options.repo, number };
}

function parseOwnerRepoNumberShorthand(raw: string | undefined): GitHubShorthandReference | null {
  if (raw === undefined) return null;
  const match = /^([^/#\s]+)\/([^/#\s]+)#([1-9]\d*)$/u.exec(raw);
  if (match === null) return null;
  return {
    input: raw,
    repo: `${match[1]}/${match[2]}`,
    number: Number(match[3]),
    source: "owner-repo-number",
  };
}

function parseGitHubIssueOrPrUrl(raw: string | undefined): GitHubShorthandReference | null {
  if (raw === undefined) return null;
  let parsed: URL;
  try {
    parsed = new URL(raw);
  } catch {
    return null;
  }
  if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
  if (parsed.hostname.toLowerCase() !== "github.com" && parsed.hostname.toLowerCase() !== "www.github.com") return null;
  const parts = parsed.pathname.split("/").filter((part) => part.length > 0);
  if (parts.length < 4) return null;
  const [owner, repoName, kind, numberRaw] = parts;
  if (!owner || !repoName || (kind !== "issues" && kind !== "pull")) return null;
  if (!/^[1-9]\d*$/u.test(numberRaw ?? "")) return null;
  return {
    input: raw,
    repo: `${owner}/${repoName}`,
    number: Number(numberRaw),
    source: "github-url",
    urlKind: kind === "issues" ? "issue" : "pr",
  };
}

function parseReadViewTarget(raw: string | undefined): GitHubShorthandReference | null {
  return parseOwnerRepoNumberShorthand(raw) ?? parseGitHubIssueOrPrUrl(raw);
}

function readViewSupportedCommands(kind: "issue" | "pr", repo: string, number: number): string[] {
  return [
    `bun scripts/cli.ts gh ${kind} view ${number} --repo ${repo} --json ${readViewSupportedJsonFields(kind)}`,
    `bun scripts/cli.ts gh ${kind} view ${repo}#${number} --raw`,
    `bun scripts/cli.ts gh ${kind} read ${number} --repo ${repo} --json ${readViewSupportedJsonFields(kind)} [compatibility alias]`,
  ];
}

function readViewSupportedJsonFields(kind: "issue" | "pr"): string {
  return kind === "issue"
    ? ISSUE_VIEW_JSON_FIELDS.join(",")
    : "body,title,state,stateDetail,closed,closedAt,merged,mergedAt,mergeCommit,head,base,draft,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup";
}

function resolveReadViewNumberReference(kind: "issue" | "pr", sub: "read" | "view", _raw: string | undefined, options: GitHubOptions, args: string[]): GitHubResolvedNumberReference | GitHubCommandResult {
  const command = `${kind} ${sub}`;
  const targets = positionalArgs(args.slice(2));
  if (targets.length > 1) {
    return validationError(command, options.repo, `${command} accepts one positional target: number, GitHub ${kind} URL, or owner/repo#number`, {
      supportedCommands: [
        `bun scripts/cli.ts gh ${kind} view <number> --repo owner/name --json ${readViewSupportedJsonFields(kind)}`,
        `bun scripts/cli.ts gh ${kind} view https://github.com/owner/name/${kind === "issue" ? "issues" : "pull"}/<number> --raw`,
        `bun scripts/cli.ts gh ${kind} view owner/name#<number> --raw`,
      ],
    });
  }
  const raw = targets[0];
  if (optionWasProvided(args, "--number")) {
    const numberAliasRaw = optionValue(args, "--number");
    const aliasNumber = parseNumberForCommand(options.repo, numberAliasRaw, command);
    if (typeof aliasNumber !== "number") return aliasNumber;
    if (raw !== undefined) {
      const parsedRaw = parseNumberForCommand(options.repo, raw, command);
      if (typeof parsedRaw !== "number") return parsedRaw;
      if (parsedRaw !== aliasNumber) {
        return validationError(command, options.repo, `${command} positional number ${parsedRaw} conflicts with --number ${aliasNumber}; use one target number.`, {
          positionalNumber: parsedRaw,
          numberAlias: aliasNumber,
          supportedCommands: readViewSupportedCommands(kind, options.repo, aliasNumber),
        });
      }
    }
    return {
      repo: options.repo,
      number: aliasNumber,
      shorthand: {
        input: `--number ${aliasNumber}`,
        repo: options.repo,
        number: aliasNumber,
        source: "number-option",
        standardCommand: `bun scripts/cli.ts gh ${kind} view ${aliasNumber} --repo ${options.repo}`,
      },
    };
  }
  const shorthand = parseReadViewTarget(raw);
  if (shorthand !== null) {
    if (shorthand.urlKind !== undefined && shorthand.urlKind !== kind) {
      return validationError(command, shorthand.repo, `${command} target ${shorthand.input} is a GitHub ${shorthand.urlKind} URL, not a ${kind} URL.`, {
        shorthand,
        supportedCommands: readViewSupportedCommands(shorthand.urlKind, shorthand.repo, shorthand.number),
      });
    }
    const explicitRepo = optionValue(args, "--repo");
    if (explicitRepo !== undefined && explicitRepo !== shorthand.repo) {
      const message = `${command} target ${shorthand.input} resolves to repo ${shorthand.repo}, but --repo ${explicitRepo} was also provided. Use either the shorthand or a matching --repo, not both.`;
      return validationError(command, explicitRepo, message, {
        message,
        shorthand,
        explicitRepo,
        supportedCommands: readViewSupportedCommands(kind, shorthand.repo, shorthand.number),
      });
    }
    return { repo: shorthand.repo, number: shorthand.number, shorthand };
  }
  const parsed = parseNumberForCommand(options.repo, raw, command);
  if (typeof parsed !== "number") {
    return {
      ...parsed,
      supportedCommands: [
        `bun scripts/cli.ts gh ${kind} view <number> --repo owner/name --json ${readViewSupportedJsonFields(kind)}`,
        `bun scripts/cli.ts gh ${kind} view https://github.com/owner/name/${kind === "issue" ? "issues" : "pull"}/<number> --raw`,
        `bun scripts/cli.ts gh ${kind} view owner/name#<number> --raw`,
      ],
    };
  }
  return { repo: options.repo, number: parsed };
}

function issueReadJsonFields(options: GitHubOptions): IssueViewJsonField[] | undefined {
  return options.raw || options.full ? ISSUE_VIEW_JSON_FIELDS.slice() : options.jsonFields;
}

function prReadJsonFields(options: GitHubOptions): PrReadJsonField[] | undefined {
  return options.raw || options.full ? PR_READ_JSON_FIELDS.slice() : options.prJsonFields;
}

function readDisclosureOptions(options: GitHubOptions, shorthand: GitHubShorthandReference | undefined): Record<string, unknown> | null {
  if (!options.raw && !options.full && shorthand === undefined) return null;
  return {
    ...(options.raw ? { raw: true } : {}),
    ...(options.full ? { full: true } : {}),
    fullDisclosure: options.raw || options.full,
    shorthand: shorthand ?? null,
    ...(shorthand?.source === "number-option" ? {
      compatibilityHint: "--number is accepted for low-friction compatibility; standard gh syntax uses a positional number or URL.",
      standardCommand: shorthand.standardCommand,
    } : {}),
  };
}

function numberOptionCompatibilityHint(resolved: GitHubResolvedNumberReference): Record<string, unknown> | null {
  if (resolved.shorthand?.source !== "number-option") return null;
  return {
    acceptedOption: "--number",
    compatibility: true,
    message: "--number is accepted for low-friction compatibility; prefer the standard positional number target shown in standardCommand.",
    standardCommand: resolved.shorthand.standardCommand ?? null,
  };
}

async function withNumberOptionHint(result: GitHubCommandResult | Promise<GitHubCommandResult>, resolved: GitHubResolvedNumberReference): Promise<GitHubCommandResult> {
  const output = await result;
  const hint = numberOptionCompatibilityHint(resolved);
  if (hint === null) return output;
  return {
    ...output,
    standardSyntaxHint: hint,
  };
}

function isGitHubCommandResult(value: GitHubResolvedNumberReference | GitHubCommandResult): value is GitHubCommandResult {
  return typeof (value as { ok?: unknown }).ok === "boolean";
}

function unknownGhOptionDetails(args: string[], option: string): Record<string, unknown> {
  const [top, sub, third] = args;
  const details: Record<string, unknown> = {
    unsupportedOption: option,
    helpCommand: "bun scripts/cli.ts gh help",
  };
  if ((top === "issue" || top === "pr") && (sub === "read" || sub === "view")) {
    const shorthand = parseReadViewTarget(third);
    const repo = shorthand?.repo ?? optionValue(args, "--repo") ?? "owner/name";
    const number = shorthand?.number ?? (third !== undefined && /^\d+$/u.test(third) ? Number(third) : 0);
    details.supportedCommands = number > 0
      ? readViewSupportedCommands(top, repo, number)
      : [
        `bun scripts/cli.ts gh ${top} view <number> --repo owner/name --json ${readViewSupportedJsonFields(top)}`,
        `bun scripts/cli.ts gh ${top} view owner/name#<number> --raw`,
      ];
  }
  return details;
}

function readMarkdownBodyFileOrStdin(path: string): { body: string; bodySource: Record<string, unknown> } {
  if (path === "-") {
    return { body: readFileSync(0, "utf8"), bodySource: { kind: "stdin", path: "-" } };
  }
  if (!existsSync(path)) throw new Error(`body file not found: ${path}`);
  return { body: readFileSync(path, "utf8"), bodySource: { kind: "body-file", path } };
}

function readOptionalMarkdownBody(options: GitHubOptions, command: string): { body: string; bodySource: Record<string, unknown> } | null {
  if (options.bodyFile !== undefined && options.body !== undefined) throw new Error(`${command} accepts only one body source: --body-file or --body`);
  if (options.bodyFile !== undefined) {
    return readMarkdownBodyFileOrStdin(options.bodyFile);
  }
  if (options.body !== undefined) {
    return {
      body: options.body,
      bodySource: {
        kind: "inline",
        warning: options.body.includes("\n") ? "inline body contains real newlines; --body-file is safer for generated Markdown" : "inline body is intended only for short single-line text",
      },
    };
  }
  return null;
}

function readMarkdownBody(options: GitHubOptions, command: string): { body: string; bodySource: Record<string, unknown> } {
  const body = readOptionalMarkdownBody(options, command);
  if (body !== null) return body;
  throw new Error(`${command} requires --body-file <file> or --body <text>`);
}

function secretLikeInlineFindings(body: string): string[] {
  const findings: string[] = [];
  if (/\bgh[pousr]_[A-Za-z0-9_]{6,}\b/u.test(body)) findings.push("github-token-like-value");
  if (/\bgithub_pat_[A-Za-z0-9_]{6,}\b/u.test(body)) findings.push("github-pat-like-value");
  if (/\b(?:token|password|passwd|secret|api[_-]?key)\s*[:=]\s*["']?[^"'\s,;]{6,}/iu.test(body)) findings.push("credential-assignment-like-value");
  if (/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/iu.test(body)) findings.push("bearer-token-like-value");
  return findings;
}

function readIssueCommentBody(options: GitHubOptions): { body: string; bodySource: Record<string, unknown> } {
  if (options.bodyFile !== undefined && options.body !== undefined) {
    throw new Error("issue comment create accepts only one body source: --body-file or --body");
  }
  if (options.bodyFile !== undefined) {
    return readMarkdownBodyFileOrStdin(options.bodyFile);
  }
  if (options.body === undefined) throw new Error("issue comment create requires --body-file <file> or --body <text>");
  const body = options.body;
  const trimmed = body.trim();
  const shellPollution = shellPollutionEvidence(body);
  const secretLike = secretLikeInlineFindings(body);
  if (trimmed.length === 0) {
    throw new Error("issue comment create --body must not be blank; use --body-file for reviewed Markdown");
  }
  if (body.length > MAX_INLINE_ISSUE_COMMENT_BODY_CHARS) {
    throw new Error(`issue comment create --body is limited to ${MAX_INLINE_ISSUE_COMMENT_BODY_CHARS} characters; use --body-file for long Markdown`);
  }
  if (body.includes("\n") || body.includes("\r")) {
    throw new Error("issue comment create --body supports short single-line text only; use --body-file for multiline Markdown");
  }
  if (shellPollution.length > 0) {
    throw new Error(`issue comment create --body contains shell-pollution signals (${shellPollution.join(",")}); use --body-file with reviewed Markdown bytes`);
  }
  if (secretLike.length > 0) {
    throw new Error(`issue comment create --body appears to contain secret-like text (${secretLike.join(",")}); refusing to print or submit it`);
  }
  return {
    body,
    bodySource: {
      kind: "inline",
      maxInlineBodyChars: MAX_INLINE_ISSUE_COMMENT_BODY_CHARS,
      warning: "inline issue comments are intended only for short single-line text; use --body-file for Markdown or generated content",
    },
  };
}

function readIssueLifecycleCommentBody(options: GitHubOptions, command: string): { body: string; bodySource: Record<string, unknown> } | null {
  if (options.comment === undefined && options.commentFile === undefined) return null;
  if (options.comment !== undefined && options.commentFile !== undefined) {
    throw new Error(`${command} --comment and --comment-file are mutually exclusive`);
  }
  if (options.body !== undefined || options.bodyFile !== undefined) {
    throw new Error(`${command} --comment or --comment-file cannot be combined with --body or --body-file`);
  }
  if (options.commentFile !== undefined) {
    return readIssueCommentBody({ ...options, body: undefined, bodyFile: options.commentFile });
  }
  return readIssueCommentBody({ ...options, body: options.comment, bodyFile: undefined });
}

function tokenFromEnvironment(): GitHubTokenProbe {
  if (process.env.GH_TOKEN && process.env.GH_TOKEN.length > 0) {
    return { present: true, source: "GH_TOKEN", ghFallbackAttempted: false };
  }
  if (process.env.GITHUB_TOKEN && process.env.GITHUB_TOKEN.length > 0) {
    return { present: true, source: "GITHUB_TOKEN", ghFallbackAttempted: false };
  }
  return { present: false, source: null, ghFallbackAttempted: false };
}

function ghBinaryPath(): string | null {
  try {
    const output = execFileSync("sh", ["-lc", "command -v gh"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
    return output.length > 0 ? output : null;
  } catch {
    return null;
  }
}

function ghAuthToken(): string | null {
  try {
    const output = execFileSync("gh", ["auth", "token"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }).trim();
    return output.length > 0 ? output : null;
  } catch {
    return null;
  }
}

function resolveToken(allowGhFallback: boolean): { token: string | null; probe: GitHubTokenProbe } {
  const envProbe = tokenFromEnvironment();
  if (envProbe.present) {
    const token = envProbe.source === "GH_TOKEN" ? process.env.GH_TOKEN ?? null : process.env.GITHUB_TOKEN ?? null;
    return { token, probe: envProbe };
  }
  if (!allowGhFallback) return { token: null, probe: envProbe };
  const ghPath = ghBinaryPath();
  if (ghPath === null) return { token: null, probe: { present: false, source: null, ghFallbackAttempted: true, ghBinaryFound: false, ghAuthTokenAvailable: null } };
  const token = ghAuthToken();
  if (token !== null) return { token, probe: { present: true, source: "gh-auth-token", ghFallbackAttempted: true, ghBinaryFound: true, ghAuthTokenAvailable: true } };
  return { token: null, probe: { present: false, source: null, ghFallbackAttempted: true, ghBinaryFound: true, ghAuthTokenAvailable: false } };
}

function repoParts(repo: string): { owner: string; name: string } {
  const [owner, name, extra] = repo.split("/");
  if (!owner || !name || extra !== undefined) throw new Error("--repo must be in owner/name format");
  return { owner, name };
}

function preview(text: string): string {
  return text.length > PREVIEW_CHARS ? `${text.slice(0, PREVIEW_CHARS)}...` : text;
}

function previewLines(text: string, maxLines = 12): string[] {
  return text.split(/\r?\n/).slice(0, maxLines).map((line) => preview(line));
}

function bodySha(text: string): string {
  return createHash("sha256").update(text, "utf8").digest("hex");
}

function normalizeExpectedSha(raw: string): string {
  const value = raw.replace(/^sha256:/iu, "").toLowerCase();
  if (!/^[a-f0-9]{64}$/u.test(value)) throw new Error("--expect-body-sha must be a 64-character SHA-256 hex value, optionally prefixed with sha256:");
  return value;
}

function shellPollutionEvidence(body: string): string[] {
  const evidence: string[] = [];
  if (body.includes("\\n")) evidence.push("literal-backslash-n");
  if (body.includes("\\t")) evidence.push("literal-backslash-t");
  if (body.includes("\\`")) evidence.push("escaped-backtick");
  if (/\\x1b|\\u001b|\u001b\[/u.test(body)) evidence.push("ansi-escape");
  if (/\r(?!\n)/u.test(body)) evidence.push("bare-carriage-return");
  return evidence;
}

function bodySafetySignals(body: string): Record<string, unknown> {
  const evidence = shellPollutionEvidence(body);
  return {
    bodyChars: body.length,
    bodyTrimmedChars: body.trim().length,
    bodyLines: body.length === 0 ? 0 : body.split(/\r?\n/).length,
    bodySha: bodySha(body),
    preservesRawNewlines: body.includes("\n"),
    containsLiteralBackslashN: body.includes("\\n"),
    containsBackticks: body.includes("`"),
    containsMarkdownTable: /^\s*\|.+\|\s*$/m.test(body),
    shellPollution: {
      suspected: evidence.length > 0,
      evidence,
    },
  };
}

function dryRunBody(repo: string, title: string | undefined, body: string): Record<string, unknown> {
  return {
    repo,
    ...(title === undefined ? {} : { title }),
    bodyPreview: preview(body),
    bodyPreviewLines: previewLines(body),
    ...bodySafetySignals(body),
  };
}

function normalizeNewlines(text: string): string {
  return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}

function isTimelineHeading(line: string): boolean {
  return /^##\s+更新\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}\s+北京时间\s*$/u.test(line.trimEnd());
}

function isCommanderBriefUpdateHeading(line: string): boolean {
  const trimmed = line.trimEnd();
  return COMMANDER_BRIEF_UPDATE_HEADING_PATTERNS.some((pattern) => pattern.test(trimmed));
}

function commanderBriefUpdateHeadings(body: string): string[] {
  return normalizeNewlines(body)
    .split("\n")
    .map((line) => line.trimEnd())
    .filter((line) => isCommanderBriefUpdateHeading(line));
}

function codeQueueBoardHwlabProductRoutingFindings(body: string | undefined): Array<Record<string, unknown>> {
  if (body === undefined) return [];
  const findings: Array<Record<string, unknown>> = [];
  const directIssuePatterns = [
    { kind: "hwlab-repo-issue-url", pattern: /(?:https?:\/\/github\.com\/)?pikasTech\/HWLAB\/issues\/\d+/iu },
    { kind: "hwlab-repo-issue-short-ref", pattern: /\bpikasTech\/HWLAB#\d+\b/iu },
    { kind: "hwlab-short-issue-ref", pattern: /\bHWLAB#\d+\b/iu },
  ];
  const hwlabContextPattern = /\bHWLAB\b|pikasTech\/HWLAB|hwlab-/iu;
  const strongProductPattern = /hwlab-cloud-web|Cloud Workbench|DEV-LIVE|res_boxsimu|hwlab-patch-panel|patch panel|M3\s*(?:虚拟硬件|可信闭环)/iu;
  const genericProductPattern = /用户反馈|user feedback/iu;
  const governanceContextPattern = /\b(?:commander|Code Queue|CLI|infra|governance|guard|guardrail|routing|misfile|board)\b|治理|调度|基础设施|守卫|边界|分流|看板/iu;
  const lines = normalizeNewlines(body).split("\n");
  for (let index = 0; index < lines.length; index += 1) {
    const line = lines[index];
    const isTableRow = line.trim().startsWith("|") && !isMarkdownTableSeparator(line);
    const strongProductSignal = strongProductPattern.test(line);
    const genericProductSignal = genericProductPattern.test(line);
    const governanceContext = governanceContextPattern.test(line);
    for (const definition of directIssuePatterns) {
      const match = definition.pattern.exec(line);
      if (match !== null && !governanceContext && (isTableRow || strongProductSignal || (genericProductSignal && hwlabContextPattern.test(line)))) {
        findings.push({
          kind: definition.kind,
          lineNumber: index + 1,
          match: match[0],
          snippet: preview(line.trim()),
        });
      }
    }
    if (isTableRow && !governanceContext && (strongProductSignal || (genericProductSignal && hwlabContextPattern.test(line)))) {
      const match = strongProductSignal ? strongProductPattern.exec(line) : genericProductPattern.exec(line);
      findings.push({
        kind: "hwlab-product-signal",
        lineNumber: index + 1,
        match: match?.[0] ?? "HWLAB product signal",
        snippet: preview(line.trim()),
      });
    }
    if (findings.length >= 12) break;
  }
  return findings;
}

function codeQueueBoardHwlabProductRoutingHint(boardIssueNumber: number, body?: string): Record<string, unknown> | null {
  if (boardIssueNumber !== CODE_QUEUE_BOARD_TARGET_ISSUE) return null;
  const findings = codeQueueBoardHwlabProductRoutingFindings(body);
  return {
    warning: "#20 is only for UniDesk commander/Code Queue/CLI/infra governance; HWLAB user/product issues belong in pikasTech/HWLAB.",
    route: "Create or update the corresponding issue in pikasTech/HWLAB; keep #20 rows limited to UniDesk governance or infrastructure support work.",
    forbiddenPatterns: [
      "pikasTech/HWLAB#<N>",
      "github.com/pikasTech/HWLAB/issues/<N>",
      "HWLAB#<N>",
      "HWLAB product/live validation rows",
    ],
    allowedScope: ["commander governance", "Code Queue supervision", "UniDesk CLI guardrails", "UniDesk infrastructure"],
    detected: findings.length > 0,
    findings,
  };
}

function codeQueueBoardHintHasHwlabProductRouting(hint: Record<string, unknown> | null): boolean {
  if (hint === null) return false;
  const routing = hint.hwlabProductRouting;
  return typeof routing === "object" && routing !== null && (routing as { detected?: unknown }).detected === true;
}

function codeQueueBoardHintHasCommanderBriefUpdates(hint: Record<string, unknown> | null): boolean {
  if (hint === null) return false;
  const commanderBrief = hint.commanderBrief;
  return typeof commanderBrief === "object" && commanderBrief !== null && (commanderBrief as { detected?: unknown }).detected === true;
}

function codeQueueBoardCommanderBriefHint(boardIssueNumber: number, body?: string): Record<string, unknown> | null {
  if (boardIssueNumber !== CODE_QUEUE_BOARD_TARGET_ISSUE) return null;
  const headings = body === undefined ? [] : commanderBriefUpdateHeadings(body);
  const hwlabProductRouting = codeQueueBoardHwlabProductRoutingHint(boardIssueNumber, body);
  const commanderBriefDetected = headings.length > 0;
  const hwlabProductDetected = codeQueueBoardHintHasHwlabProductRouting({ hwlabProductRouting });
  return {
    warning: "#20 is the long-term UniDesk commander/Code Queue/CLI/infra governance board only; do not write daily commander brief updates or HWLAB product/user issue tracking into #20.",
    route: commanderBriefDetected
      ? "Move daily progress notes to the daily rolling commander brief issue referenced in #20's 指挥简报索引, using --body-profile commander-brief."
      : hwlabProductDetected
        ? "Move HWLAB product/user issue tracking to pikasTech/HWLAB; keep #20 limited to UniDesk commander/Code Queue/CLI/infra governance."
        : "Use #20 only for UniDesk commander/Code Queue/CLI/infra governance rows.",
    forbiddenHeadings: ["## 更新 YYYY-MM-DD HH:mm 北京时间", "## YYYY-MM-DD HH:mm 北京时间指挥更新", "### YYYY-MM-DD HH:mm CST：..."],
    suggestedCommand: "bun scripts/cli.ts gh issue update <daily-brief-issue> --mode replace --body-file <file> --body-profile commander-brief --expect-body-sha <sha>",
    detected: commanderBriefDetected || hwlabProductDetected,
    detectedHeadings: headings,
    commanderBrief: {
      detected: commanderBriefDetected,
      detectedHeadings: headings,
    },
    hwlabProductRouting,
  };
}

function extractTimelineSections(markdown: string): CommanderBriefSection[] {
  const normalized = normalizeNewlines(markdown);
  const lines = normalized.split("\n");
  const sections: CommanderBriefSection[] = [];
  let currentStart = -1;
  for (let index = 0; index < lines.length; index += 1) {
    if (!isTimelineHeading(lines[index])) continue;
    if (currentStart !== -1) {
      sections.push({
        heading: lines[currentStart].trimEnd(),
        text: lines.slice(currentStart, index).join("\n").trimEnd(),
        startLine: currentStart + 1,
      });
    }
    currentStart = index;
  }
  if (currentStart !== -1) {
    sections.push({
      heading: lines[currentStart].trimEnd(),
      text: lines.slice(currentStart).join("\n").trimEnd(),
      startLine: currentStart + 1,
    });
  }
  return sections.filter((section) => section.text.length > 0);
}

export function commanderBriefDiff(oldBodyRaw: string, newBodyRaw: string): CommanderBriefDiff {
  const oldBody = normalizeNewlines(oldBodyRaw);
  const newBody = normalizeNewlines(newBodyRaw);
  if (oldBody === newBody) {
    return {
      ok: false,
      mode: "identical",
      message: "",
      chars: 0,
      sections: [],
      sectionCount: 0,
      skippedReason: "issue body is unchanged",
    };
  }

  if (newBody.startsWith(oldBody)) {
    const suffix = newBody.slice(oldBody.length);
    const sections = extractTimelineSections(suffix).map((section) => section.text);
    const message = sections.join("\n\n").trim();
    if (message.length === 0) {
      return {
        ok: false,
        mode: "append-only",
        message: "",
        chars: 0,
        sections: [],
        sectionCount: 0,
        skippedReason: "append-only suffix contains no new commander brief update section",
      };
    }
    return { ok: true, mode: "append-only", message, chars: message.length, sections, sectionCount: sections.length };
  }

  const oldSections = new Set(extractTimelineSections(oldBody).map((section) => section.text));
  const allNewSections = extractTimelineSections(newBody);
  const newSections = allNewSections.map((section) => section.text).filter((section) => !oldSections.has(section));
  if (newSections.length === 0) {
    return {
      ok: false,
      mode: "heading-diff",
      message: "",
      chars: 0,
      sections: [],
      sectionCount: 0,
      skippedReason: "no new commander brief update section found; non-timeline edits are not notified",
    };
  }
  const duplicateNewHeadings = allNewSections.some((section, index) => allNewSections.findIndex((candidate) => candidate.heading === section.heading) !== index);
  const oldHeadings = new Set(extractTimelineSections(oldBody).map((section) => section.heading));
  const ambiguousChangedHeadings = allNewSections.some((section) => oldHeadings.has(section.heading) && !oldSections.has(section.text));
  if (duplicateNewHeadings || ambiguousChangedHeadings) {
    return {
      ok: false,
      mode: "unreliable",
      message: "",
      chars: 0,
      sections: [],
      sectionCount: 0,
      skippedReason: duplicateNewHeadings
        ? "duplicate update headings make commander brief diff unreliable"
        : "an existing update section changed; cannot reliably isolate only new timeline content",
    };
  }
  const message = newSections.join("\n\n").trim();
  return { ok: true, mode: "heading-diff", message, chars: message.length, sections: newSections, sectionCount: newSections.length };
}

function envEnabled(name: string, defaultValue: boolean): boolean {
  const raw = process.env[name];
  if (raw === undefined || raw.length === 0) return defaultValue;
  return !["0", "false", "no", "off", "disabled"].includes(raw.toLowerCase());
}

function positiveEnvInteger(name: string, defaultValue: number): number {
  const raw = process.env[name];
  if (raw === undefined || raw.length === 0) return defaultValue;
  const value = Number(raw);
  return Number.isInteger(value) && value > 0 ? value : defaultValue;
}

function commanderBriefClaudeQqConfig(): ClaudeQqConfig {
  const targetTypeRaw = (process.env.UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_TARGET_TYPE ?? "private").toLowerCase();
  const targetType: ClaudeQqTargetType = targetTypeRaw === "group" ? "group" : "private";
  return {
    enabled: envEnabled("UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_ENABLED", true),
    baseUrl: process.env.UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_BASE_URL ?? DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_BASE_URL,
    targetType,
    userId: process.env.UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_USER_ID ?? DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_USER_ID,
    groupId: process.env.UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_GROUP_ID,
    timeoutMs: positiveEnvInteger("UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_TIMEOUT_MS", 15_000),
  };
}

function maskedTarget(config: ClaudeQqConfig): Record<string, unknown> {
  if (config.targetType === "group") return { targetType: "group", groupId: config.groupId === undefined ? null : maskId(config.groupId) };
  return { targetType: "private", userId: config.userId === undefined ? null : maskId(config.userId) };
}

function maskId(value: string): string {
  if (value.length <= 4) return "*".repeat(value.length);
  return `${value.slice(0, 2)}***${value.slice(-2)}`;
}

function sanitizeUrlForOutput(value: string): string {
  try {
    const parsed = new URL(value);
    parsed.username = parsed.username.length > 0 ? "***" : "";
    parsed.password = parsed.password.length > 0 ? "***" : "";
    parsed.search = parsed.search.length > 0 ? "?..." : "";
    parsed.hash = "";
    return parsed.toString();
  } catch {
    return value.includes("?") ? `${value.split("?")[0]}?...` : value;
  }
}

function claudeQqPayload(config: ClaudeQqConfig, message: string): Record<string, unknown> {
  if (config.targetType === "group") return { targetType: "group", groupId: config.groupId ?? "", message };
  return { targetType: "private", userId: config.userId ?? DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_USER_ID, message };
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function proxiedClaudeQqBasePath(baseUrl: string): string | null {
  try {
    const parsed = new URL(baseUrl);
    const path = parsed.pathname.replace(/\/$/u, "");
    if (parsed.hostname === "backend-core" && path === "/api/microservices/claudeqq/proxy") return path;
    if ((parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost") && parsed.port === "8080" && path === "/api/microservices/claudeqq/proxy") return path;
    return null;
  } catch {
    return baseUrl === "/api/microservices/claudeqq/proxy" ? baseUrl : null;
  }
}

function normalizeClaudeQqEndpoint(basePath: string, endpoint: string): string {
  return `${basePath.replace(/\/$/u, "")}${endpoint}`;
}

function claudeQqResponseOk(response: unknown): boolean {
  if (!isRecord(response)) return false;
  if (response.ok === false) return false;
  if (typeof response.status === "number" && (response.status < 200 || response.status >= 300)) return false;
  const body = response.body;
  if (isRecord(body) && (body.ok === false || body.success === false)) return false;
  return response.ok === true || typeof response.status === "number";
}

function summarizeClaudeQqProxyFailure(response: unknown, endpoint: string): ClaudeQqEndpointResult {
  if (!isRecord(response)) return { ok: false, endpoint, degradedReason: "invalid-response", message: "ClaudeQQ proxy returned a non-object response", response };
  const status = typeof response.status === "number" ? response.status : undefined;
  const stdoutTail = typeof response.stdoutTail === "string" ? response.stdoutTail : "";
  let parsedTail: unknown = null;
  if (stdoutTail.length > 0) {
    try {
      parsedTail = JSON.parse(stdoutTail.trim()) as unknown;
    } catch {
      parsedTail = null;
    }
  }
  return {
    ok: false,
    endpoint,
    status,
    degradedReason: response.ok === false ? "microservice-proxy-failed" : "invalid-response",
    message: typeof response.error === "string"
      ? response.error
      : typeof response.stderrTail === "string" && response.stderrTail.length > 0
        ? response.stderrTail.slice(-500)
        : "ClaudeQQ proxy request failed",
    response: parsedTail ?? response,
  };
}

async function sendClaudeQqEndpoint(config: ClaudeQqConfig, endpoint: string, payload: Record<string, unknown>): Promise<ClaudeQqEndpointResult> {
  const basePath = proxiedClaudeQqBasePath(config.baseUrl);
  if (basePath !== null) {
    const response = coreInternalFetch(normalizeClaudeQqEndpoint(basePath, endpoint), {
      method: "POST",
      body: payload,
      maxResponseBytes: 240_000,
      timeoutMs: config.timeoutMs,
    });
    if (claudeQqResponseOk(response)) return { ok: true, endpoint, status: isRecord(response) && typeof response.status === "number" ? response.status : 200, response };
    return summarizeClaudeQqProxyFailure(response, endpoint);
  }

  return {
    ok: false,
    endpoint,
    degradedReason: "notification-path-unavailable",
    message: "ClaudeQQ notifications must use backend-core /api/microservices/claudeqq/proxy; local skill, powershell, direct host, and ad-hoc ClaudeQQ URLs are not supported.",
    response: {
      baseUrl: sanitizeUrlForOutput(config.baseUrl),
      requiredBaseUrl: DEFAULT_COMMANDER_BRIEF_CLAUDEQQ_BASE_URL,
      recommendedCommand: "bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw",
    },
  };
}

async function sendCommanderBriefClaudeQq(config: ClaudeQqConfig, message: string): Promise<ClaudeQqSendResult> {
  const target = maskedTarget(config);
  if (!config.enabled) {
    return { ok: true, attempted: false, skipped: true, skippedReason: "UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_ENABLED disabled", target };
  }
  if (config.targetType === "group" && (config.groupId === undefined || config.groupId.length === 0)) {
    return { ok: false, attempted: false, skipped: true, skippedReason: "group target requires UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_GROUP_ID", target };
  }
  const payload = claudeQqPayload(config, message);
  const first = await sendClaudeQqEndpoint(config, "/api/push/text", payload);
  if (first.ok) return { ok: true, attempted: true, endpoint: first.endpoint, status: first.status, response: first.response, target };
  const second = await sendClaudeQqEndpoint(config, "/api/send/text", payload);
  if (second.ok) return { ok: true, attempted: true, endpoint: second.endpoint, status: second.status, response: second.response, target };
  return {
    ok: false,
    attempted: true,
    endpoint: second.endpoint,
    status: second.status ?? first.status,
    degradedReason: second.degradedReason ?? first.degradedReason ?? "claudeqq-send-failed",
    message: second.message ?? first.message ?? "ClaudeQQ send failed",
    response: { attempts: [first, second] },
    target,
  };
}

function commanderBriefNotificationPlan(issueNumber: number, body: string, diff: CommanderBriefDiff, config: ClaudeQqConfig): Record<string, unknown> {
  const proxyBasePath = proxiedClaudeQqBasePath(config.baseUrl);
  return {
    enabled: config.enabled,
    issueNumber,
    bodyChars: body.length,
    diff: {
      ok: diff.ok,
      mode: diff.mode,
      chars: diff.chars,
      sectionCount: diff.sectionCount,
      skippedReason: diff.skippedReason ?? null,
      preview: diff.ok ? preview(diff.message) : "",
      previewLines: diff.ok ? previewLines(diff.message) : [],
      containsLiteralBackslashN: diff.message.includes("\\n"),
      containsBackticks: diff.message.includes("`"),
      containsMarkdownTable: /^\s*\|.+\|\s*$/m.test(diff.message),
    },
    claudeqq: {
      dryRun: true,
      wouldSend: config.enabled && diff.ok && proxyBasePath !== null,
      baseUrl: sanitizeUrlForOutput(config.baseUrl),
      proxyBasePath,
      servicePath: proxyBasePath === null ? null : normalizeClaudeQqEndpoint(proxyBasePath, "/api/push/text"),
      target: maskedTarget(config),
      timeoutMs: config.timeoutMs,
      ...(proxyBasePath === null
        ? {
          blockedReason: "notification-path-unavailable",
          recommendedCommand: "bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw",
        }
        : {}),
    },
  };
}

function writeBodyPlan(command: "issue create" | "issue comment create" | "pr create" | "pr comment", repo: string, body: string, bodySource: Record<string, unknown>, extra: Record<string, unknown> = {}): Record<string, unknown> {
  const isIssueWrite = command === "issue create" || command === "issue comment create";
  const source = String(bodySource.kind ?? "unknown");
  const requestBody: Record<string, unknown> = {
    bodyChars: body.length,
    bodySource,
  };
  if (command === "issue create" && typeof extra.title === "string") requestBody.title = extra.title;
  if (command === "issue create" && Array.isArray(extra.labels)) requestBody.labels = extra.labels;
  return {
    repo,
    bodySource,
    source,
    bodyPreview: preview(body),
    bodyPreviewLines: previewLines(body),
    ...bodySafetySignals(body),
    request: {
      method: "POST",
      path: isIssueWrite
        ? (command === "issue create" ? "/repos/{owner}/{repo}/issues" : "/repos/{owner}/{repo}/issues/{issue_number}/comments")
        : (command === "pr create" ? "/repos/{owner}/{repo}/pulls" : "/repos/{owner}/{repo}/issues/{issue_number}/comments"),
      body: requestBody,
    },
    validation: {
      source,
      rawText: source === "body-file"
        ? "read from file bytes without shell interpolation"
        : source === "stdin"
          ? "read from stdin bytes"
          : "short inline argument accepted only by command-specific safety rules",
    },
    ...extra,
  };
}

function runnerDisposition(reason: GitHubDegradedReason): RunnerDisposition {
  if (reason === "unsupported-command" || reason === "validation-failed" || reason === "issue-not-found" || reason === "pr-not-found") return "business-failed";
  return "infra-blocked";
}

function sanitizedErrorDetails(parsed: unknown): unknown {
  if (typeof parsed !== "object" || parsed === null) return parsed;
  const source = parsed as Record<string, unknown>;
  const details: Record<string, unknown> = {};
  if ("message" in source) details.message = source.message;
  if ("documentation_url" in source) details.documentationUrl = source.documentation_url;
  if ("status" in source) details.status = source.status;
  if (Array.isArray(source.errors)) details.errors = source.errors.slice(0, 5);
  if ("raw" in source) details.raw = source.raw;
  return details;
}

function errorProperty(value: unknown, key: string): string | null {
  if (!isRecord(value)) return null;
  const item = value[key];
  return typeof item === "string" && item.length > 0 ? item : null;
}

function errorChainText(error: unknown): string {
  const parts: string[] = [];
  let current: unknown = error;
  const seen = new Set<unknown>();
  while (current !== null && current !== undefined && !seen.has(current)) {
    seen.add(current);
    if (current instanceof Error) {
      parts.push(current.name, current.message);
      current = (current as Error & { cause?: unknown }).cause;
      continue;
    }
    if (isRecord(current)) {
      for (const key of ["name", "message", "code", "errno", "syscall", "hostname", "host"]) {
        const value = errorProperty(current, key);
        if (value !== null) parts.push(value);
      }
      current = current.cause;
      continue;
    }
    parts.push(String(current));
    break;
  }
  return parts.join(" ").replace(/\s+/gu, " ").trim();
}

function firstErrorProperty(error: unknown, key: string): string | null {
  let current: unknown = error;
  const seen = new Set<unknown>();
  while (current !== null && current !== undefined && !seen.has(current)) {
    seen.add(current);
    const value = errorProperty(current, key);
    if (value !== null) return value;
    current = isRecord(current) ? current.cause : current instanceof Error ? (current as Error & { cause?: unknown }).cause : null;
  }
  return null;
}

function classifyGitHubFetchFailure(error: unknown): {
  reason: GitHubDegradedReason;
  message: string;
  retryable?: boolean;
  commanderAction?: string;
  details: Record<string, unknown>;
} {
  const text = errorChainText(error);
  const lower = text.toLowerCase();
  const isProxyFailure = /\bproxy\b/u.test(lower) && /could not resolve|connect|tunnel|proxy/i.test(text);
  const isGitHubTransient = !isProxyFailure && (
    /temporary failure in name resolution/iu.test(text)
    || /\b(eai_again|enotfound|etimedout|econnreset|econnrefused|ehostunreach|enetunreach)\b/iu.test(text)
    || /getaddrinfo|could not resolve host|name or service not known|failed to connect|connection timed out|connection reset|connection closed|other side closed|network is unreachable|socket connection/iu.test(text)
  );
  if (!isGitHubTransient) {
    return {
      reason: "network-proxy-failed",
      message: text.length > 0 ? text : "GitHub request failed before receiving an HTTP response",
      details: {
        scope: "github-api",
        transient: false,
        errorName: error instanceof Error ? error.name : firstErrorProperty(error, "name"),
        errorCode: firstErrorProperty(error, "code"),
        host: firstErrorProperty(error, "hostname") ?? firstErrorProperty(error, "host"),
      },
    };
  }
  return {
    reason: "github-transient",
    message: text.length > 0 ? text : "GitHub DNS/API connection failed before receiving an HTTP response",
    retryable: true,
    commanderAction: "retry-backoff-or-keep-running-if-heartbeat-fresh",
    details: {
      scope: "github-api",
      transient: true,
      retryable: true,
      authFailure: false,
      semanticFailure: false,
      errorName: error instanceof Error ? error.name : firstErrorProperty(error, "name"),
      errorCode: firstErrorProperty(error, "code"),
      host: firstErrorProperty(error, "hostname") ?? firstErrorProperty(error, "host"),
      commanderAction: "retry/backoff; if the Code Queue task heartbeat or trace is fresh, keep supervising instead of closing or requeueing business work",
      valuesPrinted: false,
    },
  };
}

function errorPayload(reason: GitHubDegradedReason, message: string, extra: Omit<GitHubErrorPayload, "ok" | "degradedReason" | "runnerDisposition" | "message"> = {}): GitHubErrorPayload {
  return {
    ok: false,
    degradedReason: reason,
    runnerDisposition: runnerDisposition(reason),
    message,
    ...extra,
  };
}

function commandError(command: string, repo: string, error: GitHubErrorPayload, extra: Record<string, unknown> = {}): GitHubCommandResult {
  return {
    ok: false,
    command,
    repo,
    degradedReason: error.degradedReason,
    runnerDisposition: error.runnerDisposition,
    details: error,
    ...extra,
  };
}

function validationError(command: string, repo: string, message: string, extra: Record<string, unknown> = {}): GitHubCommandResult {
  const error = errorPayload("validation-failed", message, { details: extra });
  return commandError(command, repo, error, extra);
}

function unsupportedCommand(command: string, repo: string, message: string, extra: Record<string, unknown> = {}): GitHubCommandResult {
  const error = errorPayload("unsupported-command", message, { details: extra });
  return commandError(command, repo, error, { message, ...extra });
}

async function parseGitHubResponse(response: Response): Promise<unknown> {
  const text = await response.text();
  if (text.length === 0) return null;
  try {
    return JSON.parse(text) as unknown;
  } catch {
    return { raw: preview(text) };
  }
}

function classifyHttpStatus(status: number, message: string, path: string, response: Response): GitHubDegradedReason {
  const lower = message.toLowerCase();
  const acceptedScopes = response.headers.get("x-accepted-oauth-scopes");
  if (status === 401) return "auth-failed";
  if (status === 403) {
    if (lower.includes("resource not accessible") || lower.includes("insufficient") || lower.includes("scope") || (acceptedScopes !== null && acceptedScopes.length > 0)) return "scope-insufficient";
    if (path.startsWith("/repos/")) return "repo-forbidden";
    return "permission-denied";
  }
  if (status === 404) {
    if (/\/pulls\/\d+/u.test(path)) return "pr-not-found";
    if (/\/issues\/\d+/u.test(path)) return "issue-not-found";
    if (path.includes("/branches/") || path.includes("/compare/")) return "validation-failed";
    return "repo-not-found";
  }
  if (status === 422) return "validation-failed";
  return "invalid-response";
}

async function githubRequest<T>(
  token: string,
  method: string,
  path: string,
  body?: Record<string, unknown>,
): Promise<T | GitHubErrorPayload> {
  let response: Response;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
  try {
    response = await fetch(`${GITHUB_API}${path}`, {
      method,
      signal: controller.signal,
      headers: {
        Accept: "application/vnd.github+json",
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        "User-Agent": USER_AGENT,
        "X-GitHub-Api-Version": "2022-11-28",
      },
      body: body === undefined ? undefined : JSON.stringify(body),
    });
  } catch (error) {
    const classified = classifyGitHubFetchFailure(error);
    return errorPayload(classified.reason, classified.message, {
      request: { method, path },
      details: classified.details,
      retryable: classified.retryable,
      commanderAction: classified.commanderAction,
    });
  } finally {
    clearTimeout(timeout);
  }
  const parsed = await parseGitHubResponse(response);
  if (!response.ok) {
    const message = typeof parsed === "object" && parsed !== null && "message" in parsed ? String((parsed as { message?: unknown }).message) : response.statusText;
    return errorPayload(classifyHttpStatus(response.status, message, path, response), message, {
      status: response.status,
      details: sanitizedErrorDetails(parsed),
      scopes: {
        accepted: response.headers.get("x-accepted-oauth-scopes"),
        token: response.headers.get("x-oauth-scopes"),
      },
      request: { method, path },
    });
  }
  return parsed as T;
}

async function githubGraphqlRequest<T>(
  token: string,
  query: string,
  variables: Record<string, unknown>,
): Promise<T | GitHubErrorPayload> {
  let response: Response;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
  try {
    response = await fetch(`${GITHUB_API}/graphql`, {
      method: "POST",
      signal: controller.signal,
      headers: {
        Accept: "application/vnd.github+json",
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        "User-Agent": USER_AGENT,
        "X-GitHub-Api-Version": "2022-11-28",
      },
      body: JSON.stringify({ query, variables }),
    });
  } catch (error) {
    const classified = classifyGitHubFetchFailure(error);
    return errorPayload(classified.reason, classified.message, {
      request: { method: "POST", path: "/graphql" },
      details: classified.details,
      retryable: classified.retryable,
      commanderAction: classified.commanderAction,
    });
  } finally {
    clearTimeout(timeout);
  }
  const parsed = await parseGitHubResponse(response);
  if (!response.ok) {
    const message = typeof parsed === "object" && parsed !== null && "message" in parsed ? String((parsed as { message?: unknown }).message) : response.statusText;
    return errorPayload(classifyHttpStatus(response.status, message, "/graphql", response), message, {
      status: response.status,
      details: sanitizedErrorDetails(parsed),
      scopes: {
        accepted: response.headers.get("x-accepted-oauth-scopes"),
        token: response.headers.get("x-oauth-scopes"),
      },
      request: { method: "POST", path: "/graphql" },
    });
  }
  if (!isRecord(parsed)) {
    return errorPayload("invalid-response", "GitHub GraphQL response was not an object", { details: parsed });
  }
  if (Array.isArray(parsed.errors) && parsed.errors.length > 0) {
    const firstError = parsed.errors[0];
    const message = isRecord(firstError) && typeof firstError.message === "string" ? firstError.message : "GitHub GraphQL query failed";
    return errorPayload("validation-failed", message, {
      details: sanitizedErrorDetails(parsed),
      request: { method: "POST", path: "/graphql" },
    });
  }
  const data = parsed.data;
  if (!isRecord(data)) {
    return errorPayload("invalid-response", "GitHub GraphQL response did not include an object data payload", { details: sanitizedErrorDetails(parsed) });
  }
  return data as T;
}

function authRequired(repo: string, command: string, tokenProbe: GitHubTokenProbe): GitHubCommandResult | null {
  if (!tokenProbe.present) {
    if (tokenProbe.ghFallbackAttempted && tokenProbe.ghBinaryFound === false) {
      return commandError(command, repo, errorPayload("missing-binary", "gh binary is missing and no GH_TOKEN/GITHUB_TOKEN is available", { details: tokenProbe }));
    }
    if (tokenProbe.ghFallbackAttempted && tokenProbe.ghBinaryFound === true && tokenProbe.ghAuthTokenAvailable === false) {
      return commandError(command, repo, errorPayload("auth-failed", "gh auth token failed and no GH_TOKEN/GITHUB_TOKEN is available", { details: tokenProbe }));
    }
    return commandError(command, repo, errorPayload("missing-token", "GH_TOKEN or GITHUB_TOKEN is required", { details: tokenProbe }), {
      degraded: ["missing-token"],
      token: tokenProbe,
    });
  }
  return null;
}

function isGitHubError(value: unknown): value is GitHubErrorPayload {
  return typeof value === "object" && value !== null && (value as { ok?: unknown }).ok === false && "degradedReason" in value;
}

function issueBodyReadCommands(repo: string, issueNumber: number): Record<string, string> {
  return {
    body: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --json body`,
    full: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --full`,
    raw: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --raw`,
  };
}

function issueCommentReadCommands(repo: string, issueNumber: number): Record<string, string> {
  return {
    comments: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --json comments`,
    full: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --full`,
    raw: `bun scripts/cli.ts gh issue view ${issueNumber} --repo ${repo} --raw`,
  };
}

function issueWriteDisclosure(options: GitHubOptions, repo: string, issueNumber: number, dryRun: boolean): Record<string, unknown> {
  const explicitFullDisclosure = options.raw || options.full;
  return {
    defaultCompact: dryRun || !explicitFullDisclosure,
    explicitFullDisclosure,
    fullBodyIncluded: explicitFullDisclosure && !dryRun,
    bodyOmitted: dryRun || !explicitFullDisclosure,
    dryRunBoundedPreview: dryRun,
    note: explicitFullDisclosure && !dryRun
      ? "The returned issue object includes the full body because --full or --raw was explicitly requested."
      : "Default issue write output omits full issue.body; use readCommands.full/raw or gh issue view --json body when full text is needed.",
    readCommands: issueBodyReadCommands(repo, issueNumber),
  };
}

function issueSummary(issue: GitHubIssue, options: { includeBody?: boolean; previewLineCount?: number } = {}): Record<string, unknown> {
  const body = issue.body ?? "";
  const includeBody = options.includeBody ?? true;
  const summary: Record<string, unknown> = {
    id: issue.id,
    number: issue.number,
    title: issue.title,
    state: issue.state,
    closed: issue.state === "closed",
    closedAt: issue.closed_at ?? null,
    url: issue.html_url,
    author: issue.user?.login ?? null,
    createdAt: issue.created_at ?? null,
    updatedAt: issue.updated_at ?? null,
    commentCount: issue.comments ?? null,
    bodyChars: body.length,
    bodySha: bodySha(body),
  };
  if (includeBody) {
    summary.body = body;
  } else {
    summary.bodyOmitted = true;
    summary.fullBodyIncluded = false;
    summary.bodyPreview = preview(body);
    summary.bodyPreviewLines = previewLines(body, options.previewLineCount ?? 8);
  }
  return summary;
}

function issueLifecycleSummary(issue: GitHubIssue): Record<string, unknown> {
  return {
    id: issue.id,
    number: issue.number,
    title: issue.title,
    state: issue.state,
    closed: issue.state === "closed",
    closedAt: issue.closed_at ?? null,
    url: issue.html_url,
    author: issue.user?.login ?? null,
    createdAt: issue.created_at ?? null,
    updatedAt: issue.updated_at ?? null,
    commentCount: issue.comments ?? null,
    labels: issueLabelNames(issue),
    bodyChars: (issue.body ?? "").length,
    bodySha: bodySha(issue.body ?? ""),
    bodyOmitted: true,
    fullBodyIncluded: false,
  };
}

function issueLifecycleDisclosure(repo: string, issueNumber: number, dryRun: boolean): Record<string, unknown> {
  return {
    defaultCompact: true,
    explicitFullDisclosure: false,
    fullBodyIncluded: false,
    bodyOmitted: true,
    dryRunBoundedPreview: dryRun,
    note: "Issue lifecycle write output omits full issue.body; use readCommands.full/raw or gh issue view --json body when full text is needed.",
    readCommands: issueBodyReadCommands(repo, issueNumber),
  };
}

function issueLifecycleBatchSummary(issues: GitHubIssue[]): Record<string, unknown> {
  const visible = issues.slice(0, ISSUE_LIFECYCLE_PREVIEW_LIMIT);
  return {
    count: issues.length,
    returned: visible.length,
    omitted: Math.max(0, issues.length - visible.length),
    numbers: visible.map((issue) => issue.number),
    issues: visible.map(issueLifecycleSummary),
  };
}

function labelSummary(label: string | { name?: string; color?: string; description?: string | null }): Record<string, unknown> {
  if (typeof label === "string") return { name: label, color: null, description: null };
  return {
    name: label.name ?? "",
    color: label.color ?? null,
    description: label.description ?? null,
  };
}

function issueLabelNames(issue: GitHubIssue): string[] {
  return (issue.labels ?? []).map((label) => typeof label === "string" ? label : label.name ?? "").filter((label) => label.length > 0);
}

function issueListSummary(issue: GitHubIssue, fields: IssueListJsonField[]): Record<string, unknown> {
  const summary: Record<IssueListJsonField, unknown> = {
    number: issue.number,
    title: issue.title,
    state: issue.state,
    closed: issue.state === "closed",
    closedAt: issue.closed_at ?? null,
    url: issue.html_url,
    updatedAt: issue.updated_at ?? null,
    createdAt: issue.created_at ?? null,
    author: issue.user?.login ?? null,
    labels: (issue.labels ?? []).map(labelSummary),
  };
  const selected: Record<string, unknown> = {};
  for (const field of fields) selected[field] = summary[field];
  return selected;
}

function boardIssueEntry(issue: GitHubIssue): BoardIssueEntry {
  return {
    number: issue.number,
    title: issue.title,
    state: issue.state,
    url: issue.html_url,
  };
}

function sortedIssueEntries(issues: BoardIssueEntry[]): BoardIssueEntry[] {
  return issues.slice().sort((a, b) => a.number - b.number);
}

function mergedKnownMetaIssues(options: GitHubOptions): number[] {
  const seen = new Set<number>();
  const values: number[] = [];
  for (const issueNumber of [...DEFAULT_BOARD_KNOWN_META_ISSUES, ...options.knownMetaIssues]) {
    if (seen.has(issueNumber)) continue;
    seen.add(issueNumber);
    values.push(issueNumber);
  }
  return values;
}

function boardIgnoreMap(options: GitHubOptions, issues: BoardIssueEntry[] = []): Map<number, BoardIgnoreReason> {
  const ignored = new Map<number, BoardIgnoreReason>();
  for (const issueNumber of mergedKnownMetaIssues(options)) ignored.set(issueNumber, "known-meta");
  for (const issueNumber of options.ignoredIssues) ignored.set(issueNumber, "ignored");
  if (options.boardIssue === CODE_QUEUE_BOARD_TARGET_ISSUE) {
    for (const issue of issues) {
      if (!ignored.has(issue.number) && isDailyCommanderBriefTitle(issue.title)) ignored.set(issue.number, "brief-index-managed");
    }
  }
  return ignored;
}

function stripMarkdownInline(text: string): string {
  return text
    .replace(/\[[^\]]*]\(([^)]*)\)/g, "$1")
    .replace(/<[^>]+>/g, "")
    .replace(/[*_`~]/g, "")
    .trim();
}

function normalizeBoardHeader(header: string): string {
  return stripMarkdownInline(header).toLowerCase().replace(/\s+/g, "");
}

function boardHeaderColumnKind(header: string): BoardColumnKind | null {
  const normalized = normalizeBoardHeader(header);
  if (["category", "类别", "分类", "类型", "任务类型"].includes(normalized)) return "category";
  if (["branch", "分支", "目标分支", "工作分支"].includes(normalized)) return "branch";
  if (["acceptance", "验收", "验收状态", "验收结果", "验收标准"].includes(normalized)) return "acceptance";
  if (["relatedtask", "task", "codequeue", "codequeuetask", "相关任务", "任务", "codequeue任务", "cq任务", "相关codequeue任务", "相关codequeuetask"].includes(normalized)) return "relatedTask";
  if (["githubstatus", "githubstate", "github状态", "githubissue状态", "issuestate", "issue状态", "issuegithub状态", "开闭状态", "openclosed", "openclosed状态"].includes(normalized)) return "githubStatus";
  if (["summary", "摘要", "概述", "任务摘要", "任务概述", "标题"].includes(normalized)) return "summary";
  if (["focus", "currentfocus", "关注点", "当前关注点", "当前focus"].includes(normalized)) return "focus";
  if (["progress", "进度", "状态", "当前进度"].includes(normalized)) return "progress";
  return null;
}

function boardColumnIndexMap(headers: string[]): Map<BoardColumnKind, number> {
  const map = new Map<BoardColumnKind, number>();
  headers.forEach((header, headerIndex) => {
    const kind = boardHeaderColumnKind(header);
    if (kind !== null && !map.has(kind)) map.set(kind, headerIndex);
  });
  return map;
}

function boardRequiredColumnIndexMap(headers: string[]): Map<BoardRequiredColumn, number> {
  const map = new Map<BoardRequiredColumn, number>();
  const allColumns = boardColumnIndexMap(headers);
  for (const column of BOARD_AUDIT_REQUIRED_COLUMNS) {
    const index = allColumns.get(column);
    if (index !== undefined) map.set(column, index);
  }
  return map;
}

function boardColumnKindForField(field: BoardRowField): BoardColumnKind {
  if (field === "branch") return "branch";
  if (field === "tasks") return "relatedTask";
  if (field === "focus") return "focus";
  if (field === "progress") return "progress";
  return "acceptance";
}

function boardIssueColumnIndex(headers: string[]): number | null {
  for (let index = 0; index < headers.length; index += 1) {
    const normalized = normalizeBoardHeader(headers[index]);
    if (["issue", "issue#", "issuenumber", "issueid", "issue编号", "issue号", "编号"].includes(normalized)) return index;
  }
  return null;
}

function markdownCells(line: string): string[] {
  const trimmed = line.trim();
  const withoutOuterPipes = trimmed.replace(/^\|/u, "").replace(/\|$/u, "");
  const cells: string[] = [];
  let current = "";
  for (let index = 0; index < withoutOuterPipes.length; index += 1) {
    const char = withoutOuterPipes[index];
    const previous = withoutOuterPipes[index - 1];
    if (char === "|" && previous !== "\\") {
      cells.push(current.trim().replace(/\\\|/g, "|"));
      current = "";
      continue;
    }
    current += char;
  }
  cells.push(current.trim().replace(/\\\|/g, "|"));
  return cells;
}

function isMarkdownTableSeparator(line: string): boolean {
  const cells = markdownCells(line);
  return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/u.test(cell.trim()));
}

function boardSectionKindFromHeading(line: string): BoardSectionKind | null {
  const normalized = line.toLowerCase();
  if (!line.trimStart().startsWith("#")) return null;
  if (normalized.includes("open") || normalized.includes("看板（open）") || normalized.includes("看板(open)") || normalized.includes("开放")) return "open";
  if (normalized.includes("closed") || normalized.includes("看板（closed）") || normalized.includes("看板(closed)") || normalized.includes("关闭") || normalized.includes("已关闭")) return "closed";
  return null;
}

function extractIssueNumbers(text: string): number[] {
  const numbers: number[] = [];
  const seen = new Set<number>();
  const patterns = [/#(\d+)/g, /\/issues\/(\d+)/g];
  for (const pattern of patterns) {
    pattern.lastIndex = 0;
    let match = pattern.exec(text);
    while (match !== null) {
      const value = Number(match[1]);
      if (Number.isInteger(value) && value > 0 && !seen.has(value)) {
        seen.add(value);
        numbers.push(value);
      }
      match = pattern.exec(text);
    }
  }
  return numbers;
}

function primaryBoardIssueNumberFromIssueCell(issueCell: string | undefined): number | null {
  if (issueCell === undefined) return null;
  const markdownLinkPattern = /\[[^\]]*]\(([^)]*)\)/g;
  let match = markdownLinkPattern.exec(issueCell);
  while (match !== null) {
    const issueNumber = /\/issues\/(\d+)/u.exec(match[1] ?? "")?.[1];
    if (issueNumber !== undefined) {
      const value = Number(issueNumber);
      if (Number.isInteger(value) && value > 0) return value;
    }
    match = markdownLinkPattern.exec(issueCell);
  }
  const leadingIssueReference = /^\s*#(\d+)\b/u.exec(issueCell)?.[1];
  if (leadingIssueReference !== undefined) {
    const value = Number(leadingIssueReference);
    if (Number.isInteger(value) && value > 0) return value;
  }
  return null;
}

function boardRowFromCells(section: BoardTableSection, raw: string, cells: string[], lineNumber: number): BoardTableRow {
  const columnMap = boardRequiredColumnIndexMap(section.headers);
  const issueColumnIndex = boardIssueColumnIndex(section.headers);
  const issueCell = issueColumnIndex === null ? undefined : cells[issueColumnIndex];
  const primaryIssueNumber = issueColumnIndex === null ? null : primaryBoardIssueNumberFromIssueCell(issueCell);
  const issueNumbers = issueColumnIndex === null
    ? extractIssueNumbers(raw)
    : (primaryIssueNumber === null ? extractIssueNumbers(issueCell ?? "") : [primaryIssueNumber]);
  const columns: Partial<Record<BoardRequiredColumn, string>> = {};
  for (const column of BOARD_AUDIT_REQUIRED_COLUMNS) {
    const cellIndex = columnMap.get(column);
    if (cellIndex !== undefined) columns[column] = cells[cellIndex] ?? "";
  }
  return {
    section: section.kind,
    lineNumber,
    raw,
    cells,
    issueNumbers,
    issueNumber: issueNumbers[0] ?? null,
    title: issueColumnIndex === null
      ? (cells.find((cell) => extractIssueNumbers(cell).length > 0) ?? cells[0] ?? null)
      : (issueCell ?? cells[0] ?? null),
    columns,
  };
}

function normalizedBoardCellPlaceholder(value: string): { spaced: string; compact: string } {
  const spaced = stripMarkdownInline(value).toLowerCase().replace(/\s+/g, " ").trim();
  return { spaced, compact: spaced.replace(/\s+/g, "") };
}

function isRelatedTaskNoTaskPlaceholder(value: string): boolean {
  const normalized = normalizedBoardCellPlaceholder(value);
  return [
    "-",
    "—",
    "–",
    "n/a",
    "na",
    "none",
    "no task",
    "no-task",
    "no code queue task",
    "not applicable",
  ].includes(normalized.spaced) || [
    "无",
    "无任务",
    "无关联任务",
    "无相关任务",
    "无codequeue任务",
    "无codequeuetask",
    "不适用",
  ].includes(normalized.compact);
}

function cellHasMeaningfulValue(value: string | undefined, column: BoardRequiredColumn): boolean {
  if (value === undefined) return false;
  const stripped = stripMarkdownInline(value);
  if (stripped.length === 0) return false;
  if (column === "relatedTask" && isRelatedTaskNoTaskPlaceholder(value)) return true;
  return !["-", "—", "n/a", "na", "todo", "tbd", "待补", "未填", "无"].includes(stripped.toLowerCase());
}

function parseBoardTables(body: string): { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] } {
  const lines = normalizeNewlines(body).split("\n");
  const sections: BoardTableSection[] = [];
  const warnings: BoardRowValidationWarning[] = [];
  let currentKind: BoardSectionKind | null = null;
  let currentHeading = "";
  let currentHeadingLine = 0;
  for (let index = 0; index < lines.length; index += 1) {
    const headingKind = boardSectionKindFromHeading(lines[index]);
    if (headingKind !== null) {
      currentKind = headingKind;
      currentHeading = lines[index].trim();
      currentHeadingLine = index + 1;
      continue;
    }
    if (currentKind === null) continue;
    if (!lines[index].trim().startsWith("|")) continue;
    const separator = lines[index + 1];
    if (separator === undefined || !separator.trim().startsWith("|") || !isMarkdownTableSeparator(separator)) continue;
    const headers = markdownCells(lines[index]);
    const rows: BoardTableRow[] = [];
    let rowIndex = index + 2;
    while (rowIndex < lines.length && lines[rowIndex].trim().startsWith("|")) {
      if (!isMarkdownTableSeparator(lines[rowIndex])) {
        const cells = markdownCells(lines[rowIndex]);
        const row = boardRowFromCells(
          { kind: currentKind, heading: currentHeading, headingLine: currentHeadingLine, headerLine: index + 1, headers, rows: [] },
          lines[rowIndex],
          cells,
          rowIndex + 1,
        );
        rows.push(row);
        if (row.issueNumbers.length === 0) {
          warnings.push({
            issueNumber: null,
            section: currentKind,
            lineNumber: row.lineNumber,
            kind: "missing-issue-reference",
            message: "Board table row does not contain a GitHub issue reference such as #36.",
            rowPreview: preview(row.raw),
          });
        }
        if (row.issueNumbers.length > 1) {
          warnings.push({
            issueNumber: row.issueNumber,
            section: currentKind,
            lineNumber: row.lineNumber,
            kind: "multiple-issue-references",
            message: boardIssueColumnIndex(headers) === null
              ? "Board table row contains multiple issue references; audit uses the first number as the row key."
              : "Board table row contains multiple issue references in the Issue column; audit uses the first number as the row key.",
            rowPreview: preview(row.raw),
          });
        }
        const missingColumns = BOARD_AUDIT_REQUIRED_COLUMNS.filter((column) => !cellHasMeaningfulValue(row.columns[column], column));
        if (missingColumns.length > 0) {
          warnings.push({
            issueNumber: row.issueNumber,
            section: currentKind,
            lineNumber: row.lineNumber,
            kind: "missing-required-columns",
            message: "Board table row is missing branch, acceptance, related task, or progress information.",
            missingColumns,
            columns: row.columns,
            rowPreview: preview(row.raw),
          });
        }
      }
      rowIndex += 1;
    }
    sections.push({
      kind: currentKind,
      heading: currentHeading,
      headingLine: currentHeadingLine,
      headerLine: index + 1,
      headers,
      rows,
    });
    index = rowIndex - 1;
  }
  return { sections, warnings };
}

function boardRowsByIssue(rows: BoardTableRow[]): Map<number, BoardTableRow[]> {
  const map = new Map<number, BoardTableRow[]>();
  for (const row of rows) {
    if (row.issueNumber === null) continue;
    const list = map.get(row.issueNumber);
    if (list === undefined) map.set(row.issueNumber, [row]);
    else list.push(row);
  }
  return map;
}

function boardRowFieldValues(section: BoardTableSection, row: BoardTableRow): Record<BoardRowField, string | null> {
  const columnMap = boardColumnIndexMap(section.headers);
  const valueFor = (kind: BoardColumnKind): string | null => {
    const index = columnMap.get(kind);
    return index === undefined ? null : row.cells[index] ?? "";
  };
  const acceptance = valueFor("acceptance");
  return {
    progress: valueFor("progress"),
    status: acceptance,
    validation: acceptance,
    branch: valueFor("branch"),
    tasks: valueFor("relatedTask"),
    focus: valueFor("focus"),
  };
}

function boardRowUpsertFieldValues(section: BoardTableSection, row: BoardTableRow): Record<BoardRowUpsertField, string | null> {
  const columnMap = boardColumnIndexMap(section.headers);
  const valueFor = (kind: BoardColumnKind): string | null => {
    const index = columnMap.get(kind);
    return index === undefined ? null : row.cells[index] ?? "";
  };
  const acceptance = valueFor("acceptance");
  return {
    category: valueFor("category"),
    branch: valueFor("branch"),
    tasks: valueFor("relatedTask"),
    summary: valueFor("summary"),
    focus: valueFor("focus"),
    validation: acceptance,
    progress: valueFor("progress"),
    status: valueFor("githubStatus"),
  };
}

function boardRowSummary(section: BoardTableSection, row: BoardTableRow): Record<string, unknown> {
  return {
    issueNumber: row.issueNumber,
    section: row.section,
    lineNumber: row.lineNumber,
    heading: section.heading,
    headers: section.headers,
    title: row.title,
    cells: row.cells,
    fields: boardRowFieldValues(section, row),
    raw: row.raw,
    rowPreview: preview(row.raw),
  };
}

function boardRowsExactMatch(
  sections: BoardTableSection[],
  issueNumber: number,
): Array<{ section: BoardTableSection; row: BoardTableRow }> {
  return boardRowsForState(sections, "all").filter(({ row }) => row.issueNumber === issueNumber);
}

function boardRowsForState(sections: BoardTableSection[], state: IssueListState): Array<{ section: BoardTableSection; row: BoardTableRow }> {
  return sections
    .filter((section) => state === "all" || section.kind === state)
    .flatMap((section) => section.rows.map((row) => ({ section, row })));
}

function boardDuplicateRows(
  sections: BoardTableSection[],
  issueNumber: number,
): Array<{ section: BoardTableSection; row: BoardTableRow }> {
  return boardRowsForState(sections, "all").filter(({ row }) => row.issueNumber === issueNumber);
}

function targetBoardSection(
  repo: string,
  sections: BoardTableSection[],
  kind: BoardMutationSection,
  commandName: string,
): { ok: true; section: BoardTableSection } | GitHubCommandFailure {
  const matches = sections.filter((section) => section.kind === kind);
  if (matches.length === 0) {
    return validationError(commandName, repo, `board ${kind.toUpperCase()} table was not found`, {
      section: kind,
      availableSections: sections.map((section) => ({ kind: section.kind, heading: section.heading, headerLine: section.headerLine })),
    }) as GitHubCommandFailure;
  }
  if (matches.length > 1) {
    return validationError(commandName, repo, `board ${kind.toUpperCase()} table is ambiguous`, {
      section: kind,
      matches: matches.map((section) => ({ heading: section.heading, headingLine: section.headingLine, headerLine: section.headerLine })),
    }) as GitHubCommandFailure;
  }
  return { ok: true, section: matches[0] };
}

function findBoardRow(
  repo: string,
  sections: BoardTableSection[],
  issueNumber: number,
  state: IssueListState = "all",
): BoardRowLookup | GitHubCommandFailure {
  const matches = boardRowsForState(sections, state).filter(({ row }) => row.issueNumber === issueNumber);
  if (matches.length === 0) {
    return validationError("issue board-row", repo, `board row for issue #${issueNumber} was not found`, {
      issueNumber,
      state,
      availableIssueNumbers: boardRowsForState(sections, state)
        .map(({ row }) => row.issueNumber)
        .filter((value): value is number => value !== null)
        .sort((a, b) => a - b),
    }) as GitHubCommandFailure;
  }
  if (matches.length > 1) {
    return validationError("issue board-row", repo, `board row for issue #${issueNumber} is ambiguous`, {
      issueNumber,
      state,
      matches: matches.map(({ section, row }) => ({ section: section.kind, lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
    }) as GitHubCommandFailure;
  }
  return { ok: true, ...matches[0], matches };
}

function escapeMarkdownTableCell(value: string): string {
  return value
    .replace(/\r\n/g, " ")
    .replace(/[\r\n]+/g, " ")
    .replace(/\|/g, "\\|")
    .trim();
}

function boardRowValuePollutionEvidence(value: string): string[] {
  return shellPollutionEvidence(value).filter((item) => item !== "bare-carriage-return");
}

function renderMarkdownTableRow(cells: string[]): string {
  return `| ${cells.map(escapeMarkdownTableCell).join(" | ")} |`;
}

function validateMarkdownTableRowLine(line: string): string | null {
  if (!line.trim().startsWith("|") || !line.trim().endsWith("|")) return "row-file must contain a Markdown table row starting and ending with |";
  if (isMarkdownTableSeparator(line)) return "row-file must contain a data row, not a Markdown table separator";
  return null;
}

function readBoardRowFile(path: string | undefined, command: string): { path: string; raw: string; cells: string[] } {
  if (path === undefined) throw new Error(`${command} requires --row-file <markdown-row-file>`);
  if (!existsSync(path)) throw new Error(`row file not found: ${path}`);
  const normalized = normalizeNewlines(readFileSync(path, "utf8")).trim();
  if (normalized.length === 0) throw new Error("row-file must contain exactly one non-empty Markdown table row");
  const lines = normalized.split("\n");
  if (lines.length !== 1) throw new Error("row-file must contain exactly one Markdown table row");
  const line = lines[0];
  const lineError = validateMarkdownTableRowLine(line);
  if (lineError !== null) throw new Error(lineError);
  return { path, raw: line, cells: markdownCells(line) };
}

function replaceBoardBodyLine(body: string, lineNumber: number, newLine: string): string {
  const normalized = normalizeNewlines(body);
  const lines = normalized.split("\n");
  if (lineNumber <= 0 || lineNumber > lines.length) throw new Error(`line ${lineNumber} is outside the board body`);
  lines[lineNumber - 1] = newLine;
  return lines.join("\n");
}

function removeBoardBodyLine(body: string, lineNumber: number): string {
  const normalized = normalizeNewlines(body);
  const lines = normalized.split("\n");
  if (lineNumber <= 0 || lineNumber > lines.length) throw new Error(`line ${lineNumber} is outside the board body`);
  lines.splice(lineNumber - 1, 1);
  return lines.join("\n");
}

function insertBoardBodyLineAfter(body: string, lineNumber: number, newLine: string): string {
  const normalized = normalizeNewlines(body);
  const lines = normalized.split("\n");
  if (lineNumber < 0 || lineNumber > lines.length) throw new Error(`insert line ${lineNumber} is outside the board body`);
  lines.splice(lineNumber, 0, newLine);
  return lines.join("\n");
}

function boardSectionInsertAfterLine(section: BoardTableSection): number {
  if (section.rows.length === 0) return section.headerLine + 1;
  return section.rows[section.rows.length - 1].lineNumber;
}

function boardBodySafetyIssue(repo: string, boardIssueNumber: number, body: string, options: GitHubOptions): GitHubCommandResult | null {
  const guardOptions: GitHubOptions = { ...options, bodyProfile: boardIssueNumber === CODE_QUEUE_BOARD_TARGET_ISSUE ? "code-queue-board" : options.bodyProfile };
  return validateIssueBodyGuard(repo, boardIssueNumber, body, guardOptions);
}

function boardBodyGuardSummary(boardIssueNumber: number, body: string, options: GitHubOptions): Record<string, unknown> {
  const guardOptions: GitHubOptions = { ...options, bodyProfile: boardIssueNumber === CODE_QUEUE_BOARD_TARGET_ISSUE ? "code-queue-board" : options.bodyProfile };
  return issueEditGuardSummary(boardIssueNumber, body, guardOptions);
}

function boardRowUpdatePlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  field: BoardRowField,
  value: string,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowUpdatePlanResult | GitHubCommandFailure {
  const found = findBoardRow(repo, parsed.sections, issueNumber);
  if (found.ok === false) return found;
  const { section, row } = found;
  const targetColumn = boardColumnKindForField(field);
  const columnMap = boardColumnIndexMap(section.headers);
  const columnIndex = columnMap.get(targetColumn);
  if (columnIndex === undefined) {
    return validationError("issue board-row update", repo, `board table does not contain a column for --field ${field}`, {
      issueNumber,
      field,
      targetColumn,
      section: section.kind,
      headers: section.headers,
      supportedFields: BOARD_ROW_FIELDS.slice(),
    }) as GitHubCommandFailure;
  }
  const oldCells = row.cells.slice();
  const newCells = oldCells.slice();
  while (newCells.length < section.headers.length) newCells.push("");
  newCells[columnIndex] = value;
  const newRow = renderMarkdownTableRow(newCells);
  const oldBody = boardIssue.body ?? "";
  let newBody: string;
  try {
    newBody = replaceBoardBodyLine(oldBody, row.lineNumber, newRow);
  } catch (error) {
    return validationError("issue board-row update", repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      field,
      lineNumber: row.lineNumber,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      field,
      targetColumn,
      targetColumnIndex: columnIndex,
      section: section.kind,
      lineNumber: row.lineNumber,
      oldRow: row.raw,
      newRow,
      oldCells,
      newCells,
      oldValue: oldCells[columnIndex] ?? "",
      newValue: value,
      row: {
        old: boardRowSummary(section, row),
        new: {
          ...boardRowSummary(section, { ...row, raw: newRow, cells: newCells }),
          updatedField: field,
        },
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

function expectedBoardGithubStatus(section: BoardMutationSection): BoardGithubStatus {
  return section === "open" ? "OPEN" : "CLOSED";
}

function normalizeBoardGithubStatusCell(value: string | undefined): BoardGithubStatus | null {
  const normalized = stripMarkdownInline(value ?? "").toUpperCase().replace(/\s+/g, "");
  if (normalized === "OPEN") return "OPEN";
  if (normalized === "CLOSED") return "CLOSED";
  return null;
}

function boardHeaderShape(headers: string[]): string[] {
  return headers.map((header) => normalizeBoardHeader(header));
}

function sameBoardHeaderShape(a: string[], b: string[]): boolean {
  const left = boardHeaderShape(a);
  const right = boardHeaderShape(b);
  return left.length === right.length && left.every((value, index) => value === right[index]);
}

function boardRowDuplicateFailure(
  repo: string,
  commandName: string,
  sections: BoardTableSection[],
  issueNumber: number,
): GitHubCommandFailure | null {
  const duplicates = boardDuplicateRows(sections, issueNumber);
  if (duplicates.length === 0) return null;
  return validationError(commandName, repo, `board row for issue #${issueNumber} already exists; refusing duplicate row`, {
    issueNumber,
    matches: duplicates.map(({ section, row }) => ({ section: section.kind, lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
  }) as GitHubCommandFailure;
}

function boardStatusOptionFailure(
  repo: string,
  commandName: string,
  targetSection: BoardMutationSection,
  status: BoardGithubStatus | undefined,
): GitHubCommandFailure | null {
  if (status === undefined) return null;
  const expected = expectedBoardGithubStatus(targetSection);
  if (status === expected) return null;
  return validationError(commandName, repo, `--status ${status} conflicts with --to ${targetSection}; expected ${expected}`, {
    targetSection,
    status,
    expectedStatus: expected,
  }) as GitHubCommandFailure;
}

function boardRowStatusConsistency(
  repo: string,
  commandName: string,
  section: BoardTableSection,
  row: BoardTableRow,
  role: "source" | "target",
): { ok: true; githubStatusColumnIndex: number; actualStatus: BoardGithubStatus | null; expectedStatus: BoardGithubStatus } | GitHubCommandFailure {
  const columnMap = boardColumnIndexMap(section.headers);
  const githubStatusColumnIndex = columnMap.get("githubStatus");
  if (githubStatusColumnIndex === undefined) {
    return validationError(commandName, repo, `${role} board table does not contain a GitHub 状态 column`, {
      section: section.kind,
      headers: section.headers,
      lineNumber: row.lineNumber,
      rowPreview: preview(row.raw),
    }) as GitHubCommandFailure;
  }
  const expectedStatus = expectedBoardGithubStatus(section.kind);
  const actualStatus = normalizeBoardGithubStatusCell(row.cells[githubStatusColumnIndex]);
  if (actualStatus !== expectedStatus) {
    return validationError(commandName, repo, `${role} row GitHub 状态 ${actualStatus ?? "missing"} conflicts with ${section.kind.toUpperCase()} section; expected ${expectedStatus}`, {
      section: section.kind,
      lineNumber: row.lineNumber,
      rowPreview: preview(row.raw),
      actualStatus,
      expectedStatus,
      githubStatusColumnIndex,
      headers: section.headers,
    }) as GitHubCommandFailure;
  }
  return { ok: true, githubStatusColumnIndex, actualStatus, expectedStatus };
}

function boardRowUpsertValueEntries(values: BoardRowUpsertValues): Array<{ field: BoardRowUpsertField; value: string }> {
  const entries: Array<{ field: BoardRowUpsertField; value: string }> = [];
  for (const field of BOARD_ROW_UPSERT_TEXT_FIELDS) {
    const value = values[field];
    if (value !== undefined) entries.push({ field, value });
  }
  if (values.status !== undefined) entries.push({ field: "status", value: values.status });
  return entries;
}

function boardColumnKindForUpsertField(field: BoardRowUpsertField): BoardColumnKind {
  if (field === "category") return "category";
  if (field === "branch") return "branch";
  if (field === "tasks") return "relatedTask";
  if (field === "summary") return "summary";
  if (field === "focus") return "focus";
  if (field === "validation") return "acceptance";
  if (field === "status") return "githubStatus";
  return "progress";
}

function renderBoardIssueCell(repo: string, issueNumber: number, summary: string | undefined): string {
  const trimmed = (summary ?? "").trim();
  if (trimmed.length === 0) return `#${issueNumber}`;
  const targetHref = `https://github.com/${repo}/issues/${issueNumber}`;
  const firstMarkdownIssue = /^\s*\[[^\]]*\]\(([^)]*)\)/u.exec(trimmed)?.[1] ?? "";
  if (new RegExp(`/issues/${issueNumber}(?:\\b|$)`, "u").test(firstMarkdownIssue)) return trimmed;
  return `[#${issueNumber}](${targetHref}) ${trimmed}`;
}

function boardRowUpsertTargetColumn(
  repo: string,
  commandName: string,
  section: BoardTableSection,
  issueNumber: number,
  field: BoardRowUpsertField,
): { ok: true; columnIndex: number; targetColumn: BoardColumnKind | "issue"; usesIssueCell: boolean } | GitHubCommandFailure {
  const columnMap = boardColumnIndexMap(section.headers);
  if (field === "summary") {
    const summaryIndex = columnMap.get("summary");
    if (summaryIndex !== undefined) return { ok: true, columnIndex: summaryIndex, targetColumn: "summary", usesIssueCell: false };
    const issueColumnIndex = boardIssueColumnIndex(section.headers);
    if (issueColumnIndex !== null) return { ok: true, columnIndex: issueColumnIndex, targetColumn: "issue", usesIssueCell: true };
  }
  const targetColumn = boardColumnKindForUpsertField(field);
  const columnIndex = columnMap.get(targetColumn);
  if (columnIndex === undefined) {
    return validationError(commandName, repo, `board table does not contain a column for --${field}`, {
      issueNumber,
      field,
      targetColumn,
      section: section.kind,
      headers: section.headers,
    }) as GitHubCommandFailure;
  }
  return { ok: true, columnIndex, targetColumn, usesIssueCell: false };
}

function boardRowUpsertRenderedValue(
  repo: string,
  issueNumber: number,
  field: BoardRowUpsertField,
  value: string,
  targetColumn: BoardColumnKind | "issue",
): string {
  if (field === "summary" && targetColumn === "issue") return renderBoardIssueCell(repo, issueNumber, value);
  return value;
}

function boardRowUpsertPollutionFailure(
  repo: string,
  commandName: string,
  issueNumber: number,
  values: BoardRowUpsertValues,
): GitHubCommandFailure | null {
  const findings = BOARD_ROW_UPSERT_TEXT_FIELDS
    .map((field) => {
      const value = values[field];
      if (value === undefined) return null;
      const evidence = boardRowValuePollutionEvidence(value);
      return evidence.length === 0 ? null : { field, valuePreview: preview(value), evidence };
    })
    .filter((item): item is { field: BoardRowUpsertTextField; valuePreview: string; evidence: string[] } => item !== null);
  if (findings.length === 0) return null;
  return validationError(commandName, repo, "issue board-row upsert field value contains literal shell escape text that would pollute a board cell; pass real newlines or plain text instead", {
    issueNumber,
    shellPollution: { suspected: true, findings },
  }) as GitHubCommandFailure;
}

function boardRowUpsertUpdatePlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  found: { section: BoardTableSection; row: BoardTableRow },
  values: BoardRowUpsertValues,
  requestedSection: BoardMutationSection | undefined,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row upsert";
  const { section, row } = found;
  if (requestedSection !== undefined && requestedSection !== section.kind) {
    return validationError(commandName, repo, `--section ${requestedSection} conflicts with existing row in ${section.kind}; use gh issue board-row move for section migration`, {
      issueNumber,
      requestedSection,
      existingSection: section.kind,
      moveCommand: `bun scripts/cli.ts gh issue board-row move ${issueNumber} --board-issue ${boardIssue.number} --to ${requestedSection}`,
    }) as GitHubCommandFailure;
  }
  if (values.status !== undefined) {
    const expectedStatus = expectedBoardGithubStatus(section.kind);
    if (values.status !== expectedStatus) {
      return validationError(commandName, repo, `--status ${values.status} conflicts with existing ${section.kind.toUpperCase()} section; use gh issue board-row move for section migration`, {
        issueNumber,
        existingSection: section.kind,
        requestedStatus: values.status,
        expectedStatus,
        moveCommand: `bun scripts/cli.ts gh issue board-row move ${issueNumber} --board-issue ${boardIssue.number} --to ${values.status === "OPEN" ? "open" : "closed"} --status ${values.status}`,
      }) as GitHubCommandFailure;
    }
  }
  const entries = boardRowUpsertValueEntries(values);
  if (entries.length === 0) {
    return validationError(commandName, repo, "issue board-row upsert found an existing row but no upsert fields were provided", {
      issueNumber,
      existingSection: section.kind,
      supportedFields: ["--category", "--branch", "--tasks", "--summary", "--focus", "--validation", "--progress", "--status"],
    }) as GitHubCommandFailure;
  }
  if (row.cells.length !== section.headers.length) {
    return validationError(commandName, repo, "existing row column count does not match the board table header", {
      issueNumber,
      section: section.kind,
      expectedColumns: section.headers.length,
      actualColumns: row.cells.length,
      headers: section.headers,
      lineNumber: row.lineNumber,
      rowPreview: preview(row.raw),
    }) as GitHubCommandFailure;
  }
  const oldCells = row.cells.slice();
  const newCells = oldCells.slice();
  const changedFields: Record<string, unknown>[] = [];
  for (const entry of entries) {
    const target = boardRowUpsertTargetColumn(repo, commandName, section, issueNumber, entry.field);
    if (target.ok === false) return target;
    const newValue = boardRowUpsertRenderedValue(repo, issueNumber, entry.field, entry.value, target.targetColumn);
    const oldValue = oldCells[target.columnIndex] ?? "";
    newCells[target.columnIndex] = newValue;
    changedFields.push({
      field: entry.field,
      targetColumn: target.targetColumn,
      targetColumnIndex: target.columnIndex,
      requestedValue: entry.value,
      oldValue,
      newValue,
      changed: oldValue !== newValue,
    });
  }
  const newRow = renderMarkdownTableRow(newCells);
  const oldBody = boardIssue.body ?? "";
  let newBody: string;
  try {
    newBody = replaceBoardBodyLine(oldBody, row.lineNumber, newRow);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      lineNumber: row.lineNumber,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      operation: "update",
      action: "update",
      section: section.kind,
      lineNumber: row.lineNumber,
      requestedSection: requestedSection ?? null,
      providedFields: entries.map((entry) => entry.field),
      changedFields,
      oldRow: row.raw,
      newRow,
      oldCells,
      newCells,
      row: {
        old: boardRowSummary(section, row),
        new: {
          ...boardRowSummary(section, { ...row, raw: newRow, cells: newCells }),
          upsertFields: boardRowUpsertFieldValues(section, { ...row, raw: newRow, cells: newCells }),
        },
      },
      linePlan: {
        action: "replace",
        section: section.kind,
        lineNumber: row.lineNumber,
      },
      sectionPlan: {
        kind: section.kind,
        heading: section.heading,
        headerLine: section.headerLine,
        rowCount: section.rows.length,
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

function boardRowUpsertAddPlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  sectionKind: BoardMutationSection,
  values: BoardRowUpsertValues,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row upsert";
  const targetLookup = targetBoardSection(repo, parsed.sections, sectionKind, commandName);
  if (targetLookup.ok === false) return targetLookup;
  const target = targetLookup.section;
  const issueColumnIndex = boardIssueColumnIndex(target.headers);
  if (issueColumnIndex === null) {
    return validationError(commandName, repo, "board table does not contain an Issue column required for generated upsert rows", {
      boardIssue: boardIssue.number,
      section: sectionKind,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const columnMap = boardColumnIndexMap(target.headers);
  const githubStatusColumnIndex = columnMap.get("githubStatus");
  if (githubStatusColumnIndex === undefined) {
    return validationError(commandName, repo, "board table does not contain a GitHub 状态 column required for generated upsert rows", {
      boardIssue: boardIssue.number,
      section: sectionKind,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const expectedStatus = expectedBoardGithubStatus(sectionKind);
  const requestedStatus = values.status ?? expectedStatus;
  if (requestedStatus !== expectedStatus) {
    return validationError(commandName, repo, `--status ${requestedStatus} conflicts with --section ${sectionKind}; expected ${expectedStatus}`, {
      issueNumber,
      section: sectionKind,
      requestedStatus,
      expectedStatus,
    }) as GitHubCommandFailure;
  }
  const appliedFields = new Set<BoardRowUpsertField>();
  const missingFields: BoardRowUpsertTextField[] = [];
  const unsupportedColumns: Array<{ index: number; header: string }> = [];
  const summaryColumnIndex = columnMap.get("summary");
  const cells = target.headers.map((header, index) => {
    if (index === issueColumnIndex) {
      if (summaryColumnIndex === undefined && values.summary !== undefined) appliedFields.add("summary");
      return summaryColumnIndex === undefined ? renderBoardIssueCell(repo, issueNumber, values.summary) : `#${issueNumber}`;
    }
    const kind = boardHeaderColumnKind(header);
    if (kind === "githubStatus") {
      if (values.status !== undefined) appliedFields.add("status");
      return requestedStatus;
    }
    const valueFor = (field: BoardRowUpsertTextField, value: string | undefined): string => {
      if (value === undefined) {
        missingFields.push(field);
        return "";
      }
      appliedFields.add(field);
      return value;
    };
    if (kind === "category") return valueFor("category", values.category);
    if (kind === "branch") return valueFor("branch", values.branch);
    if (kind === "relatedTask") return valueFor("tasks", values.tasks);
    if (kind === "summary") return valueFor("summary", values.summary);
    if (kind === "focus") return valueFor("focus", values.focus);
    if (kind === "acceptance") return valueFor("validation", values.validation);
    if (kind === "progress") return valueFor("progress", values.progress);
    unsupportedColumns.push({ index, header });
    return "";
  });
  if (unsupportedColumns.length > 0) {
    return validationError(commandName, repo, "target board table contains unsupported column(s) for generated upsert rows", {
      issueNumber,
      section: sectionKind,
      unsupportedColumns,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const unappliedFields = boardRowUpsertValueEntries(values)
    .map((entry) => entry.field)
    .filter((field) => !appliedFields.has(field));
  if (unappliedFields.length > 0) {
    return validationError(commandName, repo, "target board table does not contain column(s) for supplied upsert field(s)", {
      issueNumber,
      section: sectionKind,
      unappliedFields,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const uniqueMissingFields = Array.from(new Set(missingFields));
  if (uniqueMissingFields.length > 0) {
    return validationError(commandName, repo, "issue board-row upsert add requires values for every generated row column", {
      issueNumber,
      section: sectionKind,
      missingFields: uniqueMissingFields,
      requiredOptions: uniqueMissingFields.map((field) => `--${field}`),
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const newRow = renderMarkdownTableRow(cells);
  const row = boardRowFromCells(target, newRow, cells, 0);
  if (row.issueNumber !== issueNumber) {
    return validationError(commandName, repo, "generated Issue column does not match the requested issue number", {
      issueNumber,
      issueColumnIndex,
      issueCell: cells[issueColumnIndex] ?? "",
      detectedIssueNumber: row.issueNumber,
      rowPreview: preview(newRow),
    }) as GitHubCommandFailure;
  }
  const actualStatus = normalizeBoardGithubStatusCell(cells[githubStatusColumnIndex]);
  if (actualStatus !== expectedStatus) {
    return validationError(commandName, repo, "generated GitHub 状态 column does not match the target section", {
      issueNumber,
      section: sectionKind,
      expectedStatus,
      actualStatus,
      statusColumnIndex: githubStatusColumnIndex,
      statusCell: cells[githubStatusColumnIndex] ?? "",
      rowPreview: preview(newRow),
    }) as GitHubCommandFailure;
  }
  const oldBody = boardIssue.body ?? "";
  const insertAfterLine = boardSectionInsertAfterLine(target);
  let newBody: string;
  try {
    newBody = insertBoardBodyLineAfter(oldBody, insertAfterLine, newRow);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      section: sectionKind,
      insertAfterLine,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      operation: "add",
      action: "add",
      section: sectionKind,
      insertAfterLine,
      providedFields: boardRowUpsertValueEntries(values).map((entry) => entry.field),
      newRow,
      newCells: cells,
      row: {
        old: null,
        new: {
          ...boardRowSummary(target, { ...row, lineNumber: insertAfterLine + 1 }),
          upsertFields: boardRowUpsertFieldValues(target, { ...row, lineNumber: insertAfterLine + 1 }),
        },
      },
      linePlan: {
        action: "insert",
        section: sectionKind,
        insertAfterLine,
        newLineNumber: insertAfterLine + 1,
      },
      sectionPlan: {
        kind: sectionKind,
        heading: target.heading,
        headerLine: target.headerLine,
        rowCount: target.rows.length,
      },
      validation: {
        issueColumnIndex,
        githubStatusColumnIndex,
        expectedStatus,
        actualStatus,
        columnCount: cells.length,
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

function boardRowUpsertPlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  options: GitHubOptions,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row upsert";
  const pollutionFailure = boardRowUpsertPollutionFailure(repo, commandName, issueNumber, options.boardRowUpsertValues);
  if (pollutionFailure !== null) return pollutionFailure;
  const matches = boardRowsExactMatch(parsed.sections, issueNumber);
  if (matches.length > 1) {
    return validationError(commandName, repo, `board row for issue #${issueNumber} is ambiguous; refusing upsert`, {
      issueNumber,
      matches: matches.map(({ section, row }) => ({ section: section.kind, lineNumber: row.lineNumber, rowPreview: preview(row.raw) })),
    }) as GitHubCommandFailure;
  }
  if (matches.length === 1) {
    return boardRowUpsertUpdatePlan(repo, boardIssue, issueNumber, matches[0], options.boardRowUpsertValues, options.boardSection, parsed);
  }
  if (options.boardSection === undefined) {
    return validationError(commandName, repo, "issue board-row upsert add requires --section open|closed when the row does not already exist", {
      issueNumber,
      supportedSections: BOARD_MUTATION_SECTIONS.slice(),
    }) as GitHubCommandFailure;
  }
  return boardRowUpsertAddPlan(repo, boardIssue, issueNumber, options.boardSection, options.boardRowUpsertValues, parsed);
}

function boardRowAddPlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  sectionKind: BoardMutationSection,
  rowFile: { path: string; raw: string; cells: string[] },
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row add";
  const duplicate = boardRowDuplicateFailure(repo, commandName, parsed.sections, issueNumber);
  if (duplicate !== null) return duplicate;
  const targetLookup = targetBoardSection(repo, parsed.sections, sectionKind, commandName);
  if (targetLookup.ok === false) return targetLookup;
  const target = targetLookup.section;
  const issueColumnIndex = boardIssueColumnIndex(target.headers);
  if (issueColumnIndex === null) {
    return validationError(commandName, repo, "board table does not contain an Issue column required for row-file validation", {
      boardIssue: boardIssue.number,
      section: sectionKind,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const columnMap = boardColumnIndexMap(target.headers);
  const githubStatusColumnIndex = columnMap.get("githubStatus");
  if (githubStatusColumnIndex === undefined) {
    return validationError(commandName, repo, "board table does not contain a GitHub 状态 column required for add section validation", {
      boardIssue: boardIssue.number,
      section: sectionKind,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  if (rowFile.cells.length !== target.headers.length) {
    return validationError(commandName, repo, "row-file column count does not match the target board table header", {
      boardIssue: boardIssue.number,
      section: sectionKind,
      expectedColumns: target.headers.length,
      actualColumns: rowFile.cells.length,
      headers: target.headers,
      rowFile: rowFile.path,
      rowPreview: preview(rowFile.raw),
    }) as GitHubCommandFailure;
  }
  const row = boardRowFromCells(target, rowFile.raw, rowFile.cells, 0);
  if (row.issueNumber !== issueNumber) {
    return validationError(commandName, repo, "row-file Issue column does not match the requested issue number", {
      issueNumber,
      issueColumnIndex,
      issueCell: rowFile.cells[issueColumnIndex] ?? "",
      detectedIssueNumber: row.issueNumber,
      rowPreview: preview(rowFile.raw),
    }) as GitHubCommandFailure;
  }
  const expectedStatus = expectedBoardGithubStatus(sectionKind);
  const actualStatus = normalizeBoardGithubStatusCell(rowFile.cells[githubStatusColumnIndex]);
  if (actualStatus !== expectedStatus) {
    return validationError(commandName, repo, "row-file GitHub 状态 column does not match the target section", {
      issueNumber,
      section: sectionKind,
      expectedStatus,
      actualStatus,
      statusColumnIndex: githubStatusColumnIndex,
      statusCell: rowFile.cells[githubStatusColumnIndex] ?? "",
      rowPreview: preview(rowFile.raw),
    }) as GitHubCommandFailure;
  }
  const oldBody = boardIssue.body ?? "";
  const insertAfterLine = boardSectionInsertAfterLine(target);
  let newBody: string;
  try {
    newBody = insertBoardBodyLineAfter(oldBody, insertAfterLine, rowFile.raw);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      section: sectionKind,
      insertAfterLine,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      action: "add",
      section: sectionKind,
      insertAfterLine,
      rowFile: rowFile.path,
      newRow: rowFile.raw,
      newCells: rowFile.cells,
      row: {
        old: null,
        new: boardRowSummary(target, { ...row, lineNumber: insertAfterLine + 1 }),
      },
      linePlan: {
        action: "insert",
        section: sectionKind,
        insertAfterLine,
        newLineNumber: insertAfterLine + 1,
      },
      sectionPlan: {
        kind: sectionKind,
        heading: target.heading,
        headerLine: target.headerLine,
        rowCount: target.rows.length,
      },
      validation: {
        issueColumnIndex,
        githubStatusColumnIndex,
        expectedStatus,
        actualStatus,
        columnCount: rowFile.cells.length,
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

function moveBoardBodyRow(body: string, sourceLineNumber: number, insertAfterLine: number, newLine: string): string {
  const normalized = normalizeNewlines(body);
  const lines = normalized.split("\n");
  if (sourceLineNumber <= 0 || sourceLineNumber > lines.length) throw new Error(`line ${sourceLineNumber} is outside the board body`);
  if (insertAfterLine < 0 || insertAfterLine > lines.length) throw new Error(`insert line ${insertAfterLine} is outside the board body`);
  lines.splice(sourceLineNumber - 1, 1);
  const adjustedInsertAfterLine = sourceLineNumber <= insertAfterLine ? insertAfterLine - 1 : insertAfterLine;
  lines.splice(adjustedInsertAfterLine, 0, newLine);
  return lines.join("\n");
}

function boardRowMovePlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  targetKind: BoardMutationSection,
  status: BoardGithubStatus | undefined,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row move";
  const statusFailure = boardStatusOptionFailure(repo, commandName, targetKind, status);
  if (statusFailure !== null) return statusFailure;
  const found = findBoardRow(repo, parsed.sections, issueNumber);
  if (found.ok === false) return { ...found, command: commandName, repo } as GitHubCommandFailure;
  const sourceConsistency = boardRowStatusConsistency(repo, commandName, found.section, found.row, "source");
  if (sourceConsistency.ok === false) return sourceConsistency;
  const targetLookup = targetBoardSection(repo, parsed.sections, targetKind, commandName);
  if (targetLookup.ok === false) return targetLookup;
  const target = targetLookup.section;
  const { section: source, row } = found;
  if (!sameBoardHeaderShape(source.headers, target.headers)) {
    return validationError(commandName, repo, "source and target board tables have different headers; refusing to move a raw row across mismatched columns", {
      issueNumber,
      source: { section: source.kind, headers: source.headers, headerLine: source.headerLine },
      target: { section: target.kind, headers: target.headers, headerLine: target.headerLine },
    }) as GitHubCommandFailure;
  }
  if (row.cells.length !== target.headers.length) {
    return validationError(commandName, repo, "source row column count does not match the target board table header", {
      issueNumber,
      source: { section: source.kind, lineNumber: row.lineNumber, rowPreview: preview(row.raw), cells: row.cells.length },
      target: { section: target.kind, headers: target.headers, columns: target.headers.length },
    }) as GitHubCommandFailure;
  }
  const columnMap = boardColumnIndexMap(target.headers);
  const githubStatusColumnIndex = columnMap.get("githubStatus");
  if (githubStatusColumnIndex === undefined) {
    return validationError(commandName, repo, "target board table does not contain a GitHub 状态 column required for section migration", {
      issueNumber,
      targetSection: targetKind,
      headers: target.headers,
    }) as GitHubCommandFailure;
  }
  const oldCells = row.cells.slice();
  const newCells = oldCells.slice();
  const expectedStatus = expectedBoardGithubStatus(targetKind);
  if (githubStatusColumnIndex !== undefined) newCells[githubStatusColumnIndex] = status ?? expectedStatus;
  const newRow = renderMarkdownTableRow(newCells);
  const oldBody = boardIssue.body ?? "";
  const insertAfterLine = boardSectionInsertAfterLine(target);
  let newBody: string;
  try {
    newBody = source.kind === target.kind
      ? replaceBoardBodyLine(oldBody, row.lineNumber, newRow)
      : moveBoardBodyRow(oldBody, row.lineNumber, insertAfterLine, newRow);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      sourceLineNumber: row.lineNumber,
      insertAfterLine,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      action: source.kind === target.kind ? "move-within-section" : "move",
      from: source.kind,
      to: target.kind,
      sourceLineNumber: row.lineNumber,
      insertAfterLine,
      oldRow: row.raw,
      newRow,
      oldCells,
      newCells,
      row: {
        old: boardRowSummary(source, row),
        new: boardRowSummary(target, { ...row, section: target.kind, raw: newRow, cells: newCells, lineNumber: source.kind === target.kind ? row.lineNumber : insertAfterLine + 1 }),
      },
      linePlan: {
        action: source.kind === target.kind ? "replace" : "move",
        sourceLineNumber: row.lineNumber,
        insertAfterLine,
        newLineNumber: source.kind === target.kind ? row.lineNumber : insertAfterLine + 1,
      },
      sectionPlan: {
        from: source.kind,
        to: target.kind,
        sourceHeading: source.heading,
        targetHeading: target.heading,
      },
      status: {
        requested: status ?? null,
        targetDefault: expectedStatus,
        githubStatusColumnIndex: githubStatusColumnIndex ?? null,
        old: oldCells[githubStatusColumnIndex] ?? "",
        new: newCells[githubStatusColumnIndex] ?? "",
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

function boardRowDeletePlan(
  repo: string,
  boardIssue: GitHubIssue,
  issueNumber: number,
  parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] },
): BoardRowMutationPlanResult | GitHubCommandFailure {
  const commandName = "issue board-row delete";
  const found = findBoardRow(repo, parsed.sections, issueNumber);
  if (found.ok === false) return { ...found, command: commandName, repo } as GitHubCommandFailure;
  const { section, row } = found;
  const sourceConsistency = boardRowStatusConsistency(repo, commandName, section, row, "source");
  if (sourceConsistency.ok === false) return sourceConsistency;
  const oldBody = boardIssue.body ?? "";
  let newBody: string;
  try {
    newBody = removeBoardBodyLine(oldBody, row.lineNumber);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      lineNumber: row.lineNumber,
    }) as GitHubCommandFailure;
  }
  return {
    ok: true,
    newBody,
    plan: {
      repo,
      boardIssue: boardIssue.number,
      issueNumber,
      action: "delete",
      section: section.kind,
      lineNumber: row.lineNumber,
      oldRow: row.raw,
      oldCells: row.cells,
      row: {
        old: boardRowSummary(section, row),
        new: null,
      },
      linePlan: {
        action: "remove",
        section: section.kind,
        lineNumber: row.lineNumber,
      },
      sectionPlan: {
        kind: section.kind,
        heading: section.heading,
        headerLine: section.headerLine,
        rowCount: section.rows.length,
      },
      body: {
        oldBodyChars: oldBody.length,
        oldBodySha: bodySha(oldBody),
        newBodyChars: newBody.length,
        newBodySha: bodySha(newBody),
        changed: oldBody !== newBody,
      },
      tableParser: {
        reused: "parseBoardTables",
        rowValidationWarnings: parsed.warnings.length,
      },
      request: {
        method: "PATCH",
        path: `/repos/{owner}/{repo}/issues/${boardIssue.number}`,
        body: { bodyChars: newBody.length },
      },
    },
  };
}

async function issueBoardRowMutation(
  repo: string,
  token: string,
  issueNumber: number,
  options: GitHubOptions,
  commandName: "issue board-row add" | "issue board-row move" | "issue board-row delete" | "issue board-row upsert",
  mutationKey: "add" | "move" | "delete" | "upsert",
  buildPlan: (boardIssue: GitHubIssue, parsed: { sections: BoardTableSection[]; warnings: BoardRowValidationWarning[] }) => BoardRowMutationPlanResult | GitHubCommandFailure,
): Promise<GitHubCommandResult> {
  const concurrencyOptionError = assertConcurrencyOptions(options, commandName);
  if (concurrencyOptionError !== null) return { ...concurrencyOptionError, command: commandName, repo };
  const boardIssue = await getIssue(token, repo, options.boardIssue);
  if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue, issueNumber });
  const parsed = parseBoardTables(boardIssue.body ?? "");
  const planned = buildPlan(boardIssue, parsed);
  if (planned.ok === false) return planned;
  const guardError = boardBodySafetyIssue(repo, options.boardIssue, planned.newBody, options);
  const guard = boardBodyGuardSummary(options.boardIssue, planned.newBody, options);
  const hasConcurrencyGuard = options.expectBodySha !== undefined || options.expectUpdatedAt !== undefined;
  const effectiveDryRun = options.dryRun || !hasConcurrencyGuard;
  const base = {
    ok: true,
    command: commandName,
    repo,
    boardIssue: {
      number: boardIssue.number,
      title: boardIssue.title,
      state: boardIssue.state,
      url: boardIssue.html_url,
      bodyChars: (boardIssue.body ?? "").length,
      bodySha: bodySha(boardIssue.body ?? ""),
      updatedAt: boardIssue.updated_at ?? null,
    },
    codeQueueBoardHint: codeQueueBoardCommanderBriefHint(options.boardIssue, planned.newBody),
    issueNumber,
    operation: typeof planned.plan.operation === "string" ? planned.plan.operation : mutationKey,
    dryRun: effectiveDryRun,
    planned: true,
    [mutationKey]: planned.plan,
    guard,
    bodyOnlySafety: {
      oldBody: {
        fetched: true,
        bodyChars: (boardIssue.body ?? "").length,
        bodySha: bodySha(boardIssue.body ?? ""),
        updatedAt: boardIssue.updated_at ?? null,
      },
      newBody: {
        bodyChars: planned.newBody.length,
        bodySha: bodySha(planned.newBody),
        ...bodySafetySignals(planned.newBody),
      },
    },
    concurrency: {
      expectUpdatedAt: options.expectUpdatedAt ?? null,
      expectBodySha: options.expectBodySha ?? null,
      note: `non-dry-run board-row ${mutationKey} requires --expect-body-sha or --expect-updated-at and checks the current board issue before PATCH`,
    },
  };
  if (guardError !== null) return { ...guardError, command: commandName, [mutationKey]: planned.plan, guard };
  if (effectiveDryRun) {
    return {
      ...base,
      dryRun: true,
      disclosure: issueWriteDisclosure(options, repo, options.boardIssue, true),
      readCommands: issueBodyReadCommands(repo, options.boardIssue),
      wouldPatch: { issueNumber: options.boardIssue, bodySha: bodySha(planned.newBody), bodyChars: planned.newBody.length },
      note: !hasConcurrencyGuard
        ? "Default dry-run because no concurrency expectation was supplied; no GitHub issue body was modified."
        : "Dry-run only; no GitHub issue body was modified.",
    };
  }
  const concurrencyError = validateIssueConcurrency(repo, options.boardIssue, boardIssue, options);
  if (concurrencyError !== null) return { ...base, ...concurrencyError, command: commandName };
  const { owner, name } = repoParts(repo);
  const issue = await githubRequest<GitHubIssue>(token, "PATCH", `/repos/${owner}/${name}/issues/${options.boardIssue}`, { body: planned.newBody });
  if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber, [mutationKey]: planned.plan });
  return {
    ...base,
    dryRun: false,
    planned: false,
    issue: issueSummary(issue, { includeBody: options.raw || options.full }),
    disclosure: issueWriteDisclosure(options, repo, options.boardIssue, false),
    readCommands: issueBodyReadCommands(repo, options.boardIssue),
    concurrency: {
      checked: true,
      oldIssueUpdatedAt: boardIssue.updated_at ?? null,
      oldBodySha: bodySha(boardIssue.body ?? ""),
      expectUpdatedAt: options.expectUpdatedAt ?? null,
      expectBodySha: options.expectBodySha ?? null,
    },
    rest: true,
  };
}

async function issueBoardRowList(repo: string, token: string, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row list";
  const boardIssue = await getIssue(token, repo, options.boardIssue);
  if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue });
  const parsed = parseBoardTables(boardIssue.body ?? "");
  const rows = boardRowsForState(parsed.sections, options.listState);
  return {
    ok: true,
    command: commandName,
    repo,
    dryRun: true,
    readOnly: true,
    codeQueueBoardHint: codeQueueBoardCommanderBriefHint(options.boardIssue, boardIssue.body ?? ""),
    boardIssue: {
      number: boardIssue.number,
      title: boardIssue.title,
      state: boardIssue.state,
      url: boardIssue.html_url,
      bodyChars: (boardIssue.body ?? "").length,
      bodySha: bodySha(boardIssue.body ?? ""),
      updatedAt: boardIssue.updated_at ?? null,
    },
    state: options.listState,
    count: rows.length,
    rows: rows.map(({ section, row }) => boardRowSummary(section, row)),
    sections: parsed.sections.map((section) => ({ kind: section.kind, heading: section.heading, headingLine: section.headingLine, headerLine: section.headerLine, headers: section.headers, rows: section.rows.length })),
    rowValidationWarnings: parsed.warnings,
    note: "Read-only board row list; no issue body was edited.",
  };
}

async function issueBoardRowGet(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row get";
  const boardIssue = await getIssue(token, repo, options.boardIssue);
  if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue, issueNumber });
  const parsed = parseBoardTables(boardIssue.body ?? "");
  const found = findBoardRow(repo, parsed.sections, issueNumber);
  if (found.ok === false) return { ...found, command: commandName, repo, boardIssue: options.boardIssue };
  return {
    ok: true,
    command: commandName,
    repo,
    dryRun: true,
    readOnly: true,
    codeQueueBoardHint: codeQueueBoardCommanderBriefHint(options.boardIssue, boardIssue.body ?? ""),
    boardIssue: {
      number: boardIssue.number,
      title: boardIssue.title,
      state: boardIssue.state,
      url: boardIssue.html_url,
      bodyChars: (boardIssue.body ?? "").length,
      bodySha: bodySha(boardIssue.body ?? ""),
      updatedAt: boardIssue.updated_at ?? null,
    },
    issueNumber,
    row: boardRowSummary(found.section, found.row),
    rowValidationWarnings: parsed.warnings.filter((warning) => warning.issueNumber === issueNumber),
    note: "Read-only board row get; no issue body was edited.",
  };
}

async function issueBoardRowUpdate(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row update";
  if (options.boardRowField === undefined) return validationError(commandName, repo, "issue board-row update requires --field progress|status|validation|branch|tasks|focus", { supportedFields: BOARD_ROW_FIELDS.slice() });
  if (options.boardRowValue === undefined) return validationError(commandName, repo, "issue board-row update requires --value <text>", { issueNumber, field: options.boardRowField });
  const valuePollutionEvidence = boardRowValuePollutionEvidence(options.boardRowValue);
  if (valuePollutionEvidence.length > 0) {
    return validationError(commandName, repo, "issue board-row update --value contains literal shell escape text that would pollute the board cell; pass real newlines or plain text instead", {
      issueNumber,
      field: options.boardRowField,
      valuePreview: preview(options.boardRowValue),
      shellPollution: { suspected: true, evidence: valuePollutionEvidence },
    });
  }
  const concurrencyOptionError = assertConcurrencyOptions(options, commandName);
  if (concurrencyOptionError !== null) return { ...concurrencyOptionError, command: commandName, repo };

  const boardIssue = await getIssue(token, repo, options.boardIssue);
  if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue, issueNumber });
  const parsed = parseBoardTables(boardIssue.body ?? "");
  const planned = boardRowUpdatePlan(repo, boardIssue, issueNumber, options.boardRowField, options.boardRowValue, parsed);
  if (planned.ok === false) return planned;
  const guardError = boardBodySafetyIssue(repo, options.boardIssue, planned.newBody, options);
  const guard = boardBodyGuardSummary(options.boardIssue, planned.newBody, options);
  const effectiveDryRun = options.dryRun || (options.expectBodySha === undefined && options.expectUpdatedAt === undefined);
  const base = {
    ok: true,
    command: commandName,
    repo,
    boardIssue: {
      number: boardIssue.number,
      title: boardIssue.title,
      state: boardIssue.state,
      url: boardIssue.html_url,
      bodyChars: (boardIssue.body ?? "").length,
      bodySha: bodySha(boardIssue.body ?? ""),
      updatedAt: boardIssue.updated_at ?? null,
    },
    codeQueueBoardHint: codeQueueBoardCommanderBriefHint(options.boardIssue, planned.newBody),
    issueNumber,
    dryRun: effectiveDryRun,
    planned: true,
    update: planned.plan,
    guard,
    bodyOnlySafety: {
      oldBody: {
        fetched: true,
        bodyChars: (boardIssue.body ?? "").length,
        bodySha: bodySha(boardIssue.body ?? ""),
        updatedAt: boardIssue.updated_at ?? null,
      },
      newBody: {
        bodyChars: planned.newBody.length,
        bodySha: bodySha(planned.newBody),
        ...bodySafetySignals(planned.newBody),
      },
    },
    concurrency: {
      expectUpdatedAt: options.expectUpdatedAt ?? null,
      expectBodySha: options.expectBodySha ?? null,
      note: "non-dry-run board-row update requires --expect-body-sha or --expect-updated-at and checks the current board issue before PATCH",
    },
  };
  if (guardError !== null) return { ...guardError, command: commandName, update: planned.plan, guard };
  if (effectiveDryRun) {
    return {
      ...base,
      dryRun: true,
      disclosure: issueWriteDisclosure(options, repo, options.boardIssue, true),
      readCommands: issueBodyReadCommands(repo, options.boardIssue),
      wouldPatch: { issueNumber: options.boardIssue, bodySha: bodySha(planned.newBody), bodyChars: planned.newBody.length },
      note: options.expectBodySha === undefined && options.expectUpdatedAt === undefined
        ? "Default dry-run because no concurrency expectation was supplied; no GitHub issue body was modified."
        : "Dry-run only; no GitHub issue body was modified.",
    };
  }
  const concurrencyError = validateIssueConcurrency(repo, options.boardIssue, boardIssue, options);
  if (concurrencyError !== null) return { ...base, ...concurrencyError, command: commandName };
  const { owner, name } = repoParts(repo);
  const issue = await githubRequest<GitHubIssue>(token, "PATCH", `/repos/${owner}/${name}/issues/${options.boardIssue}`, { body: planned.newBody });
  if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber, update: planned.plan });
  return {
    ...base,
    dryRun: false,
    planned: false,
    issue: issueSummary(issue, { includeBody: options.raw || options.full }),
    disclosure: issueWriteDisclosure(options, repo, options.boardIssue, false),
    readCommands: issueBodyReadCommands(repo, options.boardIssue),
    concurrency: {
      checked: true,
      oldIssueUpdatedAt: boardIssue.updated_at ?? null,
      oldBodySha: bodySha(boardIssue.body ?? ""),
      expectUpdatedAt: options.expectUpdatedAt ?? null,
      expectBodySha: options.expectBodySha ?? null,
    },
    rest: true,
  };
}

async function issueBoardRowAdd(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row add";
  if (options.boardSection === undefined) return validationError(commandName, repo, "issue board-row add requires --section open|closed", { issueNumber, supportedSections: BOARD_MUTATION_SECTIONS.slice() });
  let rowFile: { path: string; raw: string; cells: string[] };
  try {
    rowFile = readBoardRowFile(options.boardRowFile, commandName);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), {
      issueNumber,
      rowFile: options.boardRowFile ?? null,
    });
  }
  return issueBoardRowMutation(
    repo,
    token,
    issueNumber,
    options,
    commandName,
    "add",
    (boardIssue, parsed) => boardRowAddPlan(repo, boardIssue, issueNumber, options.boardSection as BoardMutationSection, rowFile, parsed),
  );
}

async function issueBoardRowUpsert(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row upsert";
  return issueBoardRowMutation(
    repo,
    token,
    issueNumber,
    options,
    commandName,
    "upsert",
    (boardIssue, parsed) => boardRowUpsertPlan(repo, boardIssue, issueNumber, options, parsed),
  );
}

async function issueBoardRowMove(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row move";
  if (options.boardMoveTo === undefined) return validationError(commandName, repo, "issue board-row move requires --to open|closed", { issueNumber, supportedTargets: BOARD_MUTATION_SECTIONS.slice() });
  return issueBoardRowMutation(
    repo,
    token,
    issueNumber,
    options,
    commandName,
    "move",
    (boardIssue, parsed) => boardRowMovePlan(repo, boardIssue, issueNumber, options.boardMoveTo as BoardMutationSection, options.boardGithubStatus, parsed),
  );
}

async function issueBoardRowDelete(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-row delete";
  return issueBoardRowMutation(
    repo,
    token,
    issueNumber,
    options,
    commandName,
    "delete",
    (boardIssue, parsed) => boardRowDeletePlan(repo, boardIssue, issueNumber, parsed),
  );
}

function ignoredIssueList(issues: BoardIssueEntry[], ignoreMap: Map<number, BoardIgnoreReason>): BoardIgnoredIssue[] {
  return sortedIssueEntries(issues.filter((issue) => ignoreMap.has(issue.number))).map((issue) => ({
    number: issue.number,
    reason: ignoreMap.get(issue.number) ?? "ignored",
    title: issue.title,
    state: issue.state,
    url: issue.url,
  }));
}

function ignoredBoardOnlyIssues(issueNumbers: number[], issueMap: Map<number, BoardIssueEntry>, ignoreMap: Map<number, BoardIgnoreReason>): BoardIgnoredIssue[] {
  return issueNumbers
    .filter((issueNumber) => ignoreMap.has(issueNumber) && !issueMap.has(issueNumber))
    .sort((a, b) => a - b)
    .map((issueNumber) => ({ number: issueNumber, reason: ignoreMap.get(issueNumber) ?? "ignored" }));
}

function issueProfileFor(issueNumber: number, requested: IssueBodyProfileOption): IssueBodyProfileName | null {
  if (requested !== "auto") return requested;
  if (issueNumber === CODE_QUEUE_BOARD_TARGET_ISSUE) return "code-queue-board";
  if (issueNumber === COMMANDER_BRIEF_TARGET_ISSUE) return "commander-brief";
  return null;
}

function firstMarkdownLine(body: string | null | undefined): string {
  return normalizeNewlines(body ?? "").split("\n").find((line) => line.trim().length > 0)?.trim() ?? "";
}

function isDailyCommanderBriefTitle(title: string | null | undefined): boolean {
  return DAILY_COMMANDER_BRIEF_TITLE_PATTERN.test((title ?? "").trim());
}

function isDailyCommanderBriefBodyHeading(line: string): boolean {
  return DAILY_COMMANDER_BRIEF_BODY_HEADING_PATTERN.test(line.trim());
}

function isLegacyCommanderBriefBodyHeading(line: string): boolean {
  return LEGACY_COMMANDER_BRIEF_BODY_HEADING_PATTERN.test(line.trim());
}

function currentBodyLooksLikeCommanderBrief(body: string | null | undefined): boolean {
  const normalized = normalizeNewlines(body ?? "");
  const firstLine = firstMarkdownLine(normalized);
  if (isDailyCommanderBriefBodyHeading(firstLine)) return true;
  return isLegacyCommanderBriefBodyHeading(firstLine)
    && normalized.includes("## 常驻观察与长期建议")
    && extractTimelineSections(normalized).length > 0;
}

function commanderBriefIssueProfileMatch(issueNumber: number, context: IssueProfileValidationContext): { ok: boolean; reason: string } {
  if (issueNumber === COMMANDER_BRIEF_TARGET_ISSUE) return { ok: true, reason: "legacy-issue-number" };
  if (isDailyCommanderBriefTitle(context.issueTitle)) return { ok: true, reason: "daily-title" };
  if (currentBodyLooksLikeCommanderBrief(context.issueBody)) return { ok: true, reason: "current-body-heading" };
  return {
    ok: false,
    reason: context.issueMetadataFetched === true ? "not-commander-brief" : "issue-metadata-unavailable",
  };
}

function issueProfileNeedsMetadata(issueNumber: number, requested: IssueBodyProfileOption): boolean {
  return requested === "commander-brief" && issueNumber !== COMMANDER_BRIEF_TARGET_ISSUE;
}

function issueProfileValidation(issueNumber: number, body: string, requested: IssueBodyProfileOption, context: IssueProfileValidationContext = {}): Record<string, unknown> {
  const profileName = issueProfileFor(issueNumber, requested);
  if (profileName === null) return { profile: null, applied: false, requiredHeadings: [], missingHeadings: [], ok: true };
  const profile = ISSUE_BODY_PROFILES[profileName];
  const match = profileName === "commander-brief"
    ? commanderBriefIssueProfileMatch(issueNumber, context)
    : { ok: issueNumber === ISSUE_BODY_PROFILES["code-queue-board"].issueNumber, reason: issueNumber === ISSUE_BODY_PROFILES["code-queue-board"].issueNumber ? "issue-number" : "wrong-issue-number" };
  const missingHeadings = profile.requiredHeadings.filter((heading) => !body.includes(heading));
  return {
    profile: profileName,
    label: profile.label,
    applied: true,
    ...(profileName === "code-queue-board"
      ? { expectedIssueNumber: ISSUE_BODY_PROFILES["code-queue-board"].issueNumber }
      : { legacyIssueNumber: COMMANDER_BRIEF_TARGET_ISSUE }),
    issueMatchesProfile: match.ok,
    issueMatchReason: match.reason,
    issueMetadataFetched: context.issueMetadataFetched === true,
    issueTitle: context.issueTitle ?? null,
    requiredHeadings: profile.requiredHeadings,
    missingHeadings,
    ok: match.ok && missingHeadings.length === 0,
  };
}

function validationBoolean(record: Record<string, unknown>, key: string): boolean {
  return record[key] === true;
}

function validationStringArray(record: Record<string, unknown>, key: string): string[] {
  const value = record[key];
  return Array.isArray(value) ? value.map((item) => String(item)) : [];
}

function validateIssueBodyGuard(repo: string, issueNumber: number, body: string, options: GitHubOptions, profileContext: IssueProfileValidationContext = {}): GitHubCommandResult | null {
  const trimmed = body.trim();
  const isLiteralNull = trimmed.toLowerCase() === "null";
  const isBlank = trimmed.length === 0;
  const isShort = trimmed.length > 0 && trimmed.length < MIN_SAFE_ISSUE_BODY_CHARS;
  const profileValidation = issueProfileValidation(issueNumber, body, options.bodyProfile, profileContext);
  const boardBriefHint = codeQueueBoardCommanderBriefHint(issueNumber, body);
  const boardContainsCommanderBriefUpdates = codeQueueBoardHintHasCommanderBriefUpdates(boardBriefHint);
  const profileOk = validationBoolean(profileValidation, "ok");
  const failures: string[] = [];
  if (isLiteralNull) failures.push("literal-null-body");
  if (isBlank) failures.push("blank-body");
  if (isShort && !options.allowShortBody) failures.push("short-body");
  if (boardContainsCommanderBriefUpdates) failures.push("code-queue-board-contains-commander-brief-updates");
  if (!profileOk) {
    if (profileValidation.issueMatchesProfile === false) failures.push("profile-issue-mismatch");
    const missingHeadings = validationStringArray(profileValidation, "missingHeadings");
    if (missingHeadings.length > 0) failures.push("profile-heading-missing");
  }
  if (failures.length === 0) return null;
  const overrideAvailable = failures.every((failure) => failure === "short-body");
  const message = overrideAvailable
    ? "issue edit body is shorter than the safe default; pass --allow-short-body only for intentional short writes"
    : "issue edit body failed safety guard; refusing to write possible body-only issue corruption";
  return validationError("issue edit", repo, message, {
    issueNumber,
    guard: {
      ok: false,
      failures,
      minSafeBodyChars: MIN_SAFE_ISSUE_BODY_CHARS,
      allowShortBody: options.allowShortBody,
      shortBodyOverrideAvailable: overrideAvailable,
      bodySource: { kind: "body-file", path: options.bodyFile ?? null },
      bodyPreview: preview(body),
      bodyPreviewLines: previewLines(body),
      codeQueueBoardHint: boardBriefHint,
      ...bodySafetySignals(body),
      isLiteralNull,
      isBlank,
      isShort,
      profile: profileValidation,
    },
  });
}

function issueEditGuardSummary(issueNumber: number, body: string, options: GitHubOptions, profileContext: IssueProfileValidationContext = {}): Record<string, unknown> {
  const trimmed = body.trim();
  const profile = issueProfileValidation(issueNumber, body, options.bodyProfile, profileContext);
  const boardBriefHint = codeQueueBoardCommanderBriefHint(issueNumber, body);
  const warnings: string[] = [];
  if (trimmed.length > 0 && trimmed.length < MIN_SAFE_ISSUE_BODY_CHARS) {
    warnings.push(options.allowShortBody ? "short-body-allowed" : "short-body-would-fail");
  }
  if (options.allowShortBody) warnings.push("allow-short-body enabled; caller accepted short-body corruption risk");
  if (profile.ok === false) warnings.push("profile guard would fail");
  if (codeQueueBoardHintHasCommanderBriefUpdates(boardBriefHint)) warnings.push("code-queue-board-contains-commander-brief-updates");
  if (codeQueueBoardHintHasHwlabProductRouting(boardBriefHint)) warnings.push("code-queue-board-contains-hwlab-product-work");
  const signals = bodySafetySignals(body);
  const shellPollution = signals.shellPollution as Record<string, unknown>;
  if (shellPollution.suspected === true) warnings.push("shell-pollution-suspected");
  return {
    ok: trimmed.length > 0
      && trimmed.toLowerCase() !== "null"
      && (trimmed.length >= MIN_SAFE_ISSUE_BODY_CHARS || options.allowShortBody)
      && profile.ok === true
      && !codeQueueBoardHintHasCommanderBriefUpdates(boardBriefHint),
    minSafeBodyChars: MIN_SAFE_ISSUE_BODY_CHARS,
    allowShortBody: options.allowShortBody,
    warnings,
    profile,
    codeQueueBoardHint: boardBriefHint,
    ...signals,
  };
}

function assertConcurrencyOptions(options: GitHubOptions, commandName: string): GitHubCommandResult | null {
  if (options.expectBodySha === undefined) return null;
  try {
    normalizeExpectedSha(options.expectBodySha);
    return null;
  } catch (error) {
    return validationError(commandName, options.repo, error instanceof Error ? error.message : String(error), {
      expectBodySha: options.expectBodySha,
    });
  }
}

function validateIssueConcurrency(repo: string, issueNumber: number, oldIssue: GitHubIssue, options: GitHubOptions): GitHubCommandResult | null {
  const checks: Record<string, unknown> = {
    issueNumber,
    expectUpdatedAt: options.expectUpdatedAt ?? null,
    actualUpdatedAt: oldIssue.updated_at ?? null,
    expectBodySha: options.expectBodySha ?? null,
    actualBodySha: bodySha(oldIssue.body ?? ""),
  };
  if (options.expectUpdatedAt !== undefined && oldIssue.updated_at !== options.expectUpdatedAt) {
    return validationError("issue edit", repo, "--expect-updated-at did not match current GitHub issue updated_at; refusing stale body overwrite", checks);
  }
  if (options.expectBodySha !== undefined) {
    const expected = normalizeExpectedSha(options.expectBodySha);
    if (bodySha(oldIssue.body ?? "") !== expected) {
      return validationError("issue edit", repo, "--expect-body-sha did not match current GitHub issue body; refusing stale body overwrite", checks);
    }
  }
  return null;
}

function commentSummary(comment: GitHubComment): Record<string, unknown> {
  return {
    id: comment.id,
    body: comment.body ?? "",
    url: comment.html_url,
    author: comment.user?.login ?? null,
    createdAt: comment.created_at ?? null,
    updatedAt: comment.updated_at ?? null,
  };
}

function compactCommentSummary(comment: GitHubComment): Record<string, unknown> {
  const body = comment.body ?? "";
  return {
    id: comment.id,
    url: comment.html_url,
    author: comment.user?.login ?? null,
    createdAt: comment.created_at ?? null,
    updatedAt: comment.updated_at ?? null,
    bodyChars: body.length,
    bodySha: bodySha(body),
    bodyPreview: preview(body),
    bodyPreviewLines: previewLines(body, 4),
    bodyOmitted: true,
    fullBodyIncluded: false,
  };
}

function prStateDetail(pr: GitHubPullRequest): "open" | "closed" | "merged" {
  if (pr.merged === true || pr.merged_at !== null && pr.merged_at !== undefined) return "merged";
  return pr.state === "closed" ? "closed" : "open";
}

function prSummary(pr: GitHubPullRequest): Record<string, unknown> {
  const stateDetail = prStateDetail(pr);
  const closedAt = pr.closed_at ?? pr.merged_at ?? null;
  const mergedAt = pr.merged_at ?? null;
  const mergeCommitSha = pr.merge_commit_sha ?? null;
  return {
    id: pr.id,
    number: pr.number,
    title: pr.title,
    body: pr.body ?? "",
    state: pr.state,
    stateDetail,
    draft: pr.draft ?? false,
    url: pr.html_url,
    author: pr.user?.login ?? null,
    head: { ref: pr.head?.ref ?? null, sha: pr.head?.sha ?? null },
    base: { ref: pr.base?.ref ?? null, sha: pr.base?.sha ?? null },
    headRefName: pr.head?.ref ?? null,
    baseRefName: pr.base?.ref ?? null,
    createdAt: pr.created_at ?? null,
    updatedAt: pr.updated_at ?? null,
    closed: stateDetail !== "open",
    closedAt,
    merged: stateDetail === "merged",
    mergedAt,
    mergeCommit: stateDetail === "merged" && mergeCommitSha !== null ? { oid: mergeCommitSha } : null,
  };
}

function numberOrNull(value: number | undefined): number | null {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}

function prCompactSummary(pr: GitHubPullRequest): Record<string, unknown> {
  return {
    id: pr.id,
    number: pr.number,
    title: pr.title,
    state: pr.state,
    draft: pr.draft ?? false,
    url: pr.html_url,
    author: pr.user?.login ?? null,
    head: { ref: pr.head?.ref ?? null, sha: pr.head?.sha ?? null },
    base: { ref: pr.base?.ref ?? null, sha: pr.base?.sha ?? null },
    createdAt: pr.created_at ?? null,
    updatedAt: pr.updated_at ?? null,
  };
}

function prFileSummary(file: GitHubPullRequestFile): Record<string, unknown> {
  return {
    filename: file.filename,
    status: file.status ?? null,
    additions: numberOrNull(file.additions),
    deletions: numberOrNull(file.deletions),
    changes: numberOrNull(file.changes),
    previousFilename: file.previous_filename ?? null,
    sha: file.sha ?? null,
    blobUrl: file.blob_url ?? null,
    contentsUrl: file.contents_url ?? null,
  };
}

function sumPrFileStats(files: GitHubPullRequestFile[]): { additions: number; deletions: number; changes: number } {
  return files.reduce((accumulator, file) => {
    accumulator.additions += file.additions ?? 0;
    accumulator.deletions += file.deletions ?? 0;
    accumulator.changes += file.changes ?? ((file.additions ?? 0) + (file.deletions ?? 0));
    return accumulator;
  }, { additions: 0, deletions: 0, changes: 0 });
}

function selectedPrJson(summary: Record<string, unknown>, fields: readonly string[]): Record<string, unknown> {
  const selected: Record<string, unknown> = {};
  for (const field of fields) selected[field] = summary[field];
  return selected;
}

function prMetadataSummary(metadata: GitHubPullRequestGraphqlMetadata): Record<string, unknown> {
  return {
    mergeable: metadata.mergeable ?? null,
    mergeStateStatus: metadata.mergeStateStatus ?? null,
    statusCheckRollup: metadata.statusCheckRollup ?? null,
    headRefName: metadata.headRefName ?? null,
    baseRefName: metadata.baseRefName ?? null,
  };
}

function prMergeBoundary(): Record<string, unknown> {
  return {
    runnerAllowed: ["pr create", "pr update/edit", "pr comment", "pr read/view", "pr close", "pr merge after explicit command authorization and preflight success"],
    ordinaryRunnerFinalActionAllowed: true,
    commanderRequiredWhen: ["conflicts", "failed required checks", "production/runtime/release/security/database scope", "ambiguous task boundary"],
    hostAllowedToolsAfterReview: ["bun scripts/cli.ts gh pr merge", "GitHub UI merge/close"],
    unideskCliMergeSupported: true,
  };
}

function prCloseoutMetadata(metadata: GitHubPullRequestGraphqlMetadata): Record<string, unknown> {
  const summary = prMetadataSummary(metadata);
  const missingOrUnknownFields = PR_CLOSEOUT_JSON_FIELDS.filter((field) => {
    const value = summary[field];
    if (value === null || value === undefined) return true;
    return typeof value === "string" && value.toUpperCase() === "UNKNOWN";
  });
  return {
    ok: missingOrUnknownFields.length === 0,
    source: "github-graphql",
    missingOrUnknownFields,
    advice: missingOrUnknownFields.length === 0
      ? "Closeout GraphQL metadata is present; still review checks, branch scope, and task boundary before final action."
      : "Some closeout GraphQL metadata is missing or unknown; retry or cross-check with system gh/GitHub UI before treating the PR as merge-ready.",
    mergeBoundary: prMergeBoundary(),
  };
}

function prCloseoutMetadataError(error: GitHubErrorPayload): Record<string, unknown> {
  return {
    ok: false,
    source: "github-graphql",
    degradedReason: error.degradedReason,
    runnerDisposition: error.runnerDisposition,
    message: error.message,
    advice: "Closeout GraphQL metadata was not available; retry or use a read-only system gh/GitHub UI cross-check before deciding merge readiness.",
    mergeBoundary: prMergeBoundary(),
  };
}

function needsPrGraphqlMetadata(fields: readonly string[] | undefined): boolean {
  if (fields === undefined) return false;
  return fields.some((field) => field === "mergeable" || field === "mergeStateStatus" || field === "statusCheckRollup");
}

async function prGraphqlMetadata(repo: string, token: string, number: number): Promise<GitHubPullRequestGraphqlMetadata | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  const query = `
    query PullRequestCloseoutMetadata($owner: String!, $name: String!, $number: Int!) {
      repository(owner: $owner, name: $name) {
        pullRequest(number: $number) {
          mergeable
          mergeStateStatus
          headRefName
          baseRefName
          statusCheckRollup {
            state
            contexts(first: 100) {
              nodes {
                __typename
                ... on CheckRun {
                  name
                  status
                  conclusion
                }
                ... on StatusContext {
                  context
                  state
                  targetUrl
                  description
                }
              }
            }
          }
        }
      }
    }
  `;
  const response = await githubGraphqlRequest<{ repository?: { pullRequest?: GitHubPullRequestGraphqlMetadata | null } }>(token, query, { owner, name, number });
  if (isGitHubError(response)) return response;
  const pullRequest = response.repository?.pullRequest;
  if (pullRequest === null || pullRequest === undefined) {
    return errorPayload("invalid-response", "GitHub GraphQL PR metadata response was missing repository.pullRequest", { details: response });
  }
  return pullRequest;
}

function statusContextName(context: GitHubPullRequestGraphqlStatusContext): string {
  return context.name ?? context.context ?? "(unnamed status check)";
}

function statusContextBucket(context: GitHubPullRequestGraphqlStatusContext): string {
  const type = context.__typename ?? "StatusContext";
  const status = (context.status ?? "").toUpperCase();
  const conclusion = (context.conclusion ?? "").toUpperCase();
  const state = (context.state ?? "").toUpperCase();
  if (type === "CheckRun") {
    if (status.length > 0 && status !== "COMPLETED") return "pending";
    if (conclusion === "SUCCESS") return "success";
    if (conclusion === "NEUTRAL") return "neutral";
    if (conclusion === "SKIPPED") return "skipped";
    if (conclusion === "CANCELLED") return "cancelled";
    if (conclusion === "TIMED_OUT") return "timed-out";
    if (["FAILURE", "ACTION_REQUIRED", "STARTUP_FAILURE", "STALE"].includes(conclusion)) return "failure";
    return "unknown";
  }
  if (state === "SUCCESS") return "success";
  if (state === "PENDING" || state === "EXPECTED") return "pending";
  if (state === "FAILURE" || state === "ERROR") return "failure";
  return "unknown";
}

function compactStatusContext(context: GitHubPullRequestGraphqlStatusContext): Record<string, unknown> {
  const bucket = statusContextBucket(context);
  return {
    name: statusContextName(context),
    type: context.__typename ?? "StatusContext",
    bucket,
    status: context.status ?? null,
    conclusion: context.conclusion ?? null,
    state: context.state ?? null,
    targetUrl: context.targetUrl ?? null,
    description: context.description === undefined || context.description === null ? null : preview(context.description),
  };
}

function statusRollupSummary(
  repo: string,
  number: number,
  rollup: GitHubPullRequestGraphqlStatusCheckRollup | null | undefined,
  includeRaw: boolean,
): Record<string, unknown> {
  const contexts = rollup?.contexts?.nodes ?? [];
  const compactContexts = contexts.map(compactStatusContext);
  const counts: Record<string, number> = {
    success: 0,
    failure: 0,
    pending: 0,
    neutral: 0,
    skipped: 0,
    cancelled: 0,
    "timed-out": 0,
    unknown: 0,
  };
  for (const context of compactContexts) {
    const bucket = String(context.bucket ?? "unknown");
    counts[bucket] = (counts[bucket] ?? 0) + 1;
  }
  const nonSuccess = compactContexts.filter((context) => context.bucket !== "success");
  const failing = compactContexts.filter((context) => context.bucket === "failure" || context.bucket === "cancelled" || context.bucket === "timed-out");
  const pending = compactContexts.filter((context) => context.bucket === "pending" || context.bucket === "unknown");
  const neutral = compactContexts.filter((context) => context.bucket === "neutral" || context.bucket === "skipped");
  return {
    state: rollup?.state ?? null,
    totalContexts: compactContexts.length,
    counts,
    failingContexts: failing.slice(0, 10),
    pendingContexts: pending.slice(0, 10),
    neutralContexts: neutral.slice(0, 10),
    nonSuccessPreview: nonSuccess.slice(0, 12),
    omittedNonSuccessContexts: Math.max(0, nonSuccess.length - 12),
    rawOmitted: !includeRaw,
    ...(includeRaw
      ? { contexts: compactContexts, raw: rollup ?? null }
      : { fullHint: `bun scripts/cli.ts gh pr preflight ${number} --repo ${repo} --full` }),
  };
}

function compactProbeOk(value: unknown): boolean | null {
  if (value === "ok") return true;
  if (value === "skipped") return null;
  if (isRecord(value) && value.ok === true) return true;
  if (isRecord(value) && value.ok === false) return false;
  return null;
}

function compactAuthCapability(auth: GitHubCommandResult): Record<string, unknown> {
  const token = isRecord(auth.token) ? auth.token : {};
  const gh = isRecord(auth.gh) ? auth.gh : {};
  const probes = isRecord(auth.probes) ? auth.probes : {};
  return {
    ok: auth.ok === true,
    degradedReason: auth.ok === false ? auth.degradedReason ?? null : null,
    runnerDisposition: auth.ok === false ? auth.runnerDisposition ?? null : null,
    tokenPresent: token.present === true,
    tokenSource: typeof token.source === "string" ? token.source : null,
    ghFallbackAttempted: token.ghFallbackAttempted === true,
    ghBinaryFound: gh.binaryFound === true,
    restApi: compactProbeOk(probes.restApi),
    repoVisible: compactProbeOk(probes.repo),
    issueRead: compactProbeOk(probes.issueRead),
    valuesPrinted: false,
  };
}

function prPreflightPolicy(repo: string, number: number): Record<string, unknown> {
  return {
    readOnly: true,
    writesRemote: false,
    createsPr: false,
    comments: false,
    mergesPr: false,
    mergeCommandSupported: true,
    mergeCommand: `bun scripts/cli.ts gh pr merge ${number} --repo ${repo} --merge`,
    unideskCliMergeSupported: true,
    ordinaryRunnerFinalActionAllowed: true,
    note: "This preflight only reads GitHub auth, PR metadata, mergeability, and status checks; use gh pr merge only after this command reports ready and the task boundary allows merge.",
  };
}

function preflightPullRequestSummary(summary: Record<string, unknown>): Record<string, unknown> {
  return {
    number: summary.number ?? null,
    title: summary.title ?? null,
    state: summary.state ?? null,
    stateDetail: summary.stateDetail ?? null,
    draft: summary.draft ?? null,
    url: summary.url ?? null,
    author: summary.author ?? null,
    head: summary.head ?? null,
    base: summary.base ?? null,
    headRefName: summary.headRefName ?? null,
    baseRefName: summary.baseRefName ?? null,
    closed: summary.closed ?? null,
    merged: summary.merged ?? null,
    mergedAt: summary.mergedAt ?? null,
    updatedAt: summary.updatedAt ?? null,
    bodyOmitted: true,
  };
}

function prCloseoutSummary(
  pr: Record<string, unknown>,
  metadata: Record<string, unknown>,
  statusChecks: Record<string, unknown>,
): Record<string, unknown> {
  const blockers: string[] = [];
  const pending: string[] = [];
  const prState = typeof pr.state === "string" ? pr.state.toLowerCase() : "";
  if (prState !== "open") blockers.push(`state:${pr.state ?? "unknown"}`);
  if (pr.draft === true) blockers.push("draft:true");
  const mergeable = typeof metadata.mergeable === "string" ? metadata.mergeable.toUpperCase() : null;
  if (mergeable === "MERGEABLE") {
    // Ready signal, combined with mergeStateStatus and checks below.
  } else if (mergeable === "CONFLICTING" || mergeable === "NOT_MERGEABLE") {
    blockers.push(`mergeable:${mergeable}`);
  } else {
    pending.push(`mergeable:${mergeable ?? "unknown"}`);
  }
  const mergeStateStatus = typeof metadata.mergeStateStatus === "string" ? metadata.mergeStateStatus.toUpperCase() : null;
  if (mergeStateStatus === "CLEAN") {
    // Ready signal.
  } else if (mergeStateStatus === "UNKNOWN" || mergeStateStatus === null) {
    pending.push(`mergeStateStatus:${mergeStateStatus ?? "unknown"}`);
  } else {
    blockers.push(`mergeStateStatus:${mergeStateStatus}`);
  }
  const rollupState = typeof statusChecks.state === "string" ? statusChecks.state.toUpperCase() : null;
  const totalContexts = typeof statusChecks.totalContexts === "number" ? statusChecks.totalContexts : 0;
  const counts = isRecord(statusChecks.counts) ? statusChecks.counts : {};
  const failureCount = Number(counts.failure ?? 0) + Number(counts.cancelled ?? 0) + Number(counts["timed-out"] ?? 0);
  const pendingCount = Number(counts.pending ?? 0) + Number(counts.unknown ?? 0);
  if (failureCount > 0) blockers.push(`statusChecks:failure:${failureCount}`);
  if (pendingCount > 0) pending.push(`statusChecks:pending:${pendingCount}`);
  if (rollupState === "SUCCESS" || rollupState === null && totalContexts === 0) {
    // Ready signal.
  } else if (rollupState === "PENDING" || rollupState === "EXPECTED" || rollupState === null) {
    pending.push(`statusCheckRollup:${rollupState ?? "unknown"}`);
  } else if (rollupState !== null) {
    blockers.push(`statusCheckRollup:${rollupState}`);
  }
  const readyForCommanderMerge = blockers.length === 0 && pending.length === 0;
  return {
    readyForCommanderMerge,
    conclusion: readyForCommanderMerge ? "ready" : blockers.length > 0 ? "blocked" : "pending",
    blockers,
    pending,
    commanderAction: readyForCommanderMerge
      ? "review and merge through bun scripts/cli.ts gh pr merge when task boundaries allow"
      : "resolve blockers or rerun after GitHub finishes computing mergeability/status checks",
  };
}

async function deleteHeadBranchAfterMerge(repo: string, token: string, pr: GitHubPullRequest): Promise<Record<string, unknown>> {
  const { owner, name } = repoParts(repo);
  const headRepo = pr.head?.repo?.full_name ?? null;
  const headRef = pr.head?.ref ?? null;
  if (headRepo !== repo || headRef === null || headRef.length === 0) {
    return {
      attempted: false,
      skippedReason: "head-repo-differs-or-ref-missing",
      headRepo,
      headRef,
    };
  }
  const encodedRef = encodeURIComponent(`heads/${headRef}`);
  const deleted = await githubRequest<unknown>(token, "DELETE", `/repos/${owner}/${name}/git/refs/${encodedRef}`);
  if (isGitHubError(deleted)) {
    return {
      attempted: true,
      ok: false,
      headRepo,
      headRef,
      error: deleted,
    };
  }
  return {
    attempted: true,
    ok: true,
    headRepo,
    headRef,
  };
}

async function prMerge(repo: string, token: string, number: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  const { owner, name } = repoParts(repo);
  const pr = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
  if (isGitHubError(pr)) return commandError("pr merge", repo, pr, { number, phase: "fetch-pr" });
  const summary = prSummary(pr);
  const metadata = await prGraphqlMetadata(repo, token, number);
  if (isGitHubError(metadata)) return commandError("pr merge", repo, metadata, { number, phase: "fetch-pr-closeout-metadata", pullRequest: summary });
  const statusChecks = statusRollupSummary(repo, number, metadata.statusCheckRollup, false);
  const mergeability = prCloseoutSummary(summary, prMetadataSummary(metadata), statusChecks);
  if (mergeability.readyForCommanderMerge !== true) {
    return validationError("pr merge", repo, "PR is not ready for merge; preflight blockers or pending states remain", {
      number,
      pullRequest: preflightPullRequestSummary(summary),
      mergeability,
      statusChecks,
      closeoutMetadata: prCloseoutMetadata(metadata),
    });
  }
  if (options.dryRun) {
    return {
      ok: true,
      command: "pr merge",
      repo,
      number,
      dryRun: true,
      wouldMerge: true,
      method: options.mergeMethod,
      deleteBranch: options.deleteBranch,
      pullRequest: preflightPullRequestSummary(summary),
      mergeability,
      statusChecks,
    };
  }
  const merged = await githubRequest<Record<string, unknown>>(token, "PUT", `/repos/${owner}/${name}/pulls/${number}/merge`, {
    merge_method: options.mergeMethod,
  });
  if (isGitHubError(merged)) return commandError("pr merge", repo, merged, { number, phase: "merge", method: options.mergeMethod, pullRequest: summary });
  const after = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
  if (isGitHubError(after)) return commandError("pr merge", repo, after, { number, phase: "fetch-after-merge", mergeResult: merged });
  const branchDeletion = options.deleteBranch ? await deleteHeadBranchAfterMerge(repo, token, after) : { attempted: false, skippedReason: "delete-branch-not-requested" };
  return {
    ok: true,
    command: "pr merge",
    repo,
    number,
    method: options.mergeMethod,
    mergeResult: merged,
    pullRequest: prSummary(after),
    branchDeletion,
    rest: true,
  };
}

async function prPreflight(repo: string, number: number, commandName: "preflight" | "pr preflight" | "pr closeout", includeRaw: boolean): Promise<GitHubCommandResult> {
  const auth = await authStatus(repo);
  const authCapability = compactAuthCapability(auth);
  const policy = prPreflightPolicy(repo, number);
  if (auth.ok === false) {
    return {
      ok: false,
      command: commandName,
      repo,
      number,
      degradedReason: auth.degradedReason,
      runnerDisposition: auth.runnerDisposition,
      authCapability,
      policy,
      details: auth,
    };
  }
  const { token, probe } = resolveToken(true);
  const missing = authRequired(repo, commandName, probe);
  if (missing !== null || token === null) {
    const missingResult = missing ?? authRequired(repo, commandName, { present: false, source: null, ghFallbackAttempted: true });
    return {
      ...(missingResult ?? commandError(commandName, repo, errorPayload("missing-token", "GH_TOKEN or GITHUB_TOKEN is required"))),
      command: commandName,
      number,
      authCapability,
      policy,
    } as GitHubCommandResult;
  }
  const { owner, name } = repoParts(repo);
  const pr = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
  if (isGitHubError(pr)) return commandError(commandName, repo, pr, { number, authCapability, policy });
  const metadata = await prGraphqlMetadata(repo, token, number);
  const summary = prSummary(pr);
  const pullRequest = preflightPullRequestSummary(summary);
  if (isGitHubError(metadata)) return commandError(commandName, repo, metadata, { number, phase: "fetch-pr-closeout-metadata", pullRequest, authCapability, policy });
  const metadataSummary = prMetadataSummary(metadata);
  const statusChecks = statusRollupSummary(repo, number, metadata.statusCheckRollup, includeRaw);
  const closeout = prCloseoutSummary(summary, metadataSummary, statusChecks);
  return {
    ok: true,
    command: commandName,
    canonicalCommand: `bun scripts/cli.ts gh pr preflight ${number} --repo ${repo}`,
    aliases: ["bun scripts/cli.ts gh preflight <number>", "bun scripts/cli.ts gh pr closeout <number>"],
    repo,
    number,
    readOnly: true,
    writesRemote: false,
    valuesPrinted: false,
    authCapability,
    pullRequest,
    mergeability: {
      mergeable: metadataSummary.mergeable,
      mergeStateStatus: metadataSummary.mergeStateStatus,
      ...closeout,
    },
    statusChecks,
    policy,
    ...(includeRaw ? { raw: { authStatus: auth, pullRequest, closeoutMetadata: metadataSummary } } : {}),
  };
}

function repoSummary(repo: GitHubRepository): Record<string, unknown> {
  return {
    id: repo.id ?? null,
    fullName: repo.full_name ?? null,
    private: repo.private ?? null,
    defaultBranch: repo.default_branch ?? null,
    permissions: repo.permissions ?? null,
  };
}

function branchSummary(branch: GitHubBranch): Record<string, unknown> {
  return {
    name: branch.name ?? null,
    sha: branch.commit?.sha ?? null,
  };
}

function compareSummary(compare: GitHubCompare): Record<string, unknown> {
  return {
    status: compare.status ?? null,
    aheadBy: compare.ahead_by ?? null,
    behindBy: compare.behind_by ?? null,
    totalCommits: compare.total_commits ?? null,
    htmlUrl: compare.html_url ?? null,
    baseSha: compare.base_commit?.sha ?? null,
    mergeBaseSha: compare.merge_base_commit?.sha ?? null,
  };
}

function prUrl(repo: string, number: number): string {
  return `https://github.com/${repo}/pull/${number}`;
}

function encodePathPart(value: string): string {
  return encodeURIComponent(value);
}

async function repoInfo(token: string, repo: string): Promise<GitHubRepository | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubRepository>(token, "GET", `/repos/${owner}/${name}`);
}

async function branchInfo(token: string, repo: string, branch: string): Promise<GitHubBranch | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubBranch>(token, "GET", `/repos/${owner}/${name}/branches/${encodePathPart(branch)}`);
}

async function compareBranches(token: string, repo: string, base: string, head: string): Promise<GitHubCompare | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubCompare>(token, "GET", `/repos/${owner}/${name}/compare/${encodePathPart(base)}...${encodePathPart(head)}`);
}

function prCreatePlannedOperation(repo: string, options: GitHubOptions, body: string, bodySource: Record<string, unknown>): Record<string, unknown> {
  return {
    repo,
    title: options.title,
    base: options.base,
    head: options.head,
    draft: options.draft,
    bodyChars: body.length,
    bodyPreview: preview(body),
    bodyPreviewLines: body.split(/\r?\n/).slice(0, 12),
    bodySource,
    preservesRawNewlines: body.includes("\n"),
    containsLiteralBackslashN: body.includes("\\n"),
    containsBackticks: body.includes("`"),
    request: {
      method: "POST",
      path: "/repos/{owner}/{repo}/pulls",
      body: {
        title: options.title,
        base: options.base,
        head: options.head,
        draft: options.draft,
        bodyChars: body.length,
      },
    },
    validation: {
      repo: "required",
      base: "required",
      head: "required",
      title: "required",
      body: body.length > 0 ? "optional" : "empty",
    },
  };
}

function prCommentPlannedOperation(repo: string, issueNumber: number, body: string, bodySource: Record<string, unknown>): Record<string, unknown> {
  return {
    repo,
    issueNumber,
    bodyChars: body.length,
    bodyPreview: preview(body),
    bodyPreviewLines: body.split(/\r?\n/).slice(0, 12),
    bodySource,
    preservesRawNewlines: body.includes("\n"),
    containsLiteralBackslashN: body.includes("\\n"),
    containsBackticks: body.includes("`"),
    request: {
      method: "POST",
      path: `/repos/{owner}/{repo}/issues/${issueNumber}/comments`,
      body: { bodyChars: body.length },
    },
    validation: {
      repo: "required",
      issueNumber: "required",
      body: "required",
    },
  };
}

function bodyUpdatePlan(command: string, repo: string, number: number, mode: BodyUpdateMode, body: string, bodySource: Record<string, unknown>, existingBody: string | null): Record<string, unknown> {
  const finalBody = mode === "append" ? `${existingBody ?? ""}${body}` : body;
  return {
    command,
    repo,
    number,
    mode,
    bodySource,
    input: bodySafetySignals(body),
    existingBody: existingBody === null ? { fetched: false } : { fetched: true, bodyChars: existingBody.length, bodySha: bodySha(existingBody) },
    finalBody: {
      bodyChars: finalBody.length,
      bodyPreview: preview(finalBody),
      bodyPreviewLines: previewLines(finalBody),
      ...bodySafetySignals(finalBody),
    },
  };
}

function prEditBodyPlan(mode: BodyUpdateMode, input: { body: string; bodySource: Record<string, unknown> } | null, finalBody: string | undefined, existingBody: string | null): Record<string, unknown> | null {
  if (input === null || finalBody === undefined) return null;
  return {
    mode,
    bodySource: input.bodySource,
    input: bodySafetySignals(input.body),
    existingBody: existingBody === null ? { fetched: false } : { fetched: true, bodyChars: existingBody.length, bodySha: bodySha(existingBody) },
    finalBody: bodySafetySignals(finalBody),
  };
}

async function prCreate(repo: string, token: string, options: GitHubOptions): Promise<GitHubCommandResult> {
  if (options.title === undefined) return validationError("pr create", repo, "pr create requires --title <title>");
  if (options.base === undefined) return validationError("pr create", repo, "pr create requires --base <branch>");
  if (options.head === undefined) return validationError("pr create", repo, "pr create requires --head <branch>");
  let bodySource: { body: string; bodySource: Record<string, unknown> };
  try {
    bodySource = readMarkdownBody(options, "pr create");
  } catch (error) {
    return validationError("pr create", repo, error instanceof Error ? error.message : String(error));
  }
  const body = bodySource.body;
  const planned = prCreatePlannedOperation(repo, options, body, bodySource.bodySource);
  if (options.dryRun) {
    return {
      ok: true,
      command: "pr create",
      repo,
      dryRun: true,
      planned: true,
      draft: options.draft,
      ...planned,
    };
  }

  const repoResult = await repoInfo(token, repo);
  if (isGitHubError(repoResult)) return commandError("pr create", repo, repoResult, { planned });
  const baseBranch = await branchInfo(token, repo, options.base);
  if (isGitHubError(baseBranch)) return commandError("pr create", repo, baseBranch, { base: options.base, planned });
  const headBranch = await branchInfo(token, repo, options.head);
  if (isGitHubError(headBranch)) return commandError("pr create", repo, headBranch, { head: options.head, planned });
  const compare = await compareBranches(token, repo, options.base, options.head);
  if (isGitHubError(compare)) return commandError("pr create", repo, compare, { base: options.base, head: options.head, planned });
  const aheadBy = typeof compare.ahead_by === "number" ? compare.ahead_by : null;
  if (aheadBy === null || aheadBy <= 0) {
    return validationError("pr create", repo, "head branch must be ahead of base branch before creating a PR", {
      base: options.base,
      head: options.head,
      compare: compareSummary(compare),
    });
  }

  const { owner, name } = repoParts(repo);
  const payload: Record<string, unknown> = {
    title: options.title,
    base: options.base,
    head: options.head,
    draft: options.draft,
    body,
  };
  const pr = await githubRequest<GitHubPullRequest>(token, "POST", `/repos/${owner}/${name}/pulls`, payload);
  if (isGitHubError(pr)) return commandError("pr create", repo, pr, { base: options.base, head: options.head, planned });
  return {
    ok: true,
    command: "pr create",
    repo,
    pr: prSummary(pr),
    validation: {
      repo: repoSummary(repoResult),
      base: branchSummary(baseBranch),
      head: branchSummary(headBranch),
      compare: compareSummary(compare),
      bodySource: bodySource.bodySource,
    },
    request: {
      method: "POST",
      path: `/repos/${owner}/${name}/pulls`,
      title: options.title,
      base: options.base,
      head: options.head,
      draft: options.draft,
      bodyChars: body.length,
    },
    rest: true,
  };
}

async function prComment(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  let bodySource: { body: string; bodySource: Record<string, unknown> };
  try {
    bodySource = readMarkdownBody(options, "pr comment");
  } catch (error) {
    return validationError("pr comment", repo, error instanceof Error ? error.message : String(error), { issueNumber });
  }
  const body = bodySource.body;
  const planned = prCommentPlannedOperation(repo, issueNumber, body, bodySource.bodySource);
  if (options.dryRun) {
    return {
      ok: true,
      command: "pr comment",
      repo,
      dryRun: true,
      planned: true,
      issueNumber,
      ...planned,
    };
  }

  const repoResult = await repoInfo(token, repo);
  if (isGitHubError(repoResult)) return commandError("pr comment", repo, repoResult, { issueNumber, planned });
  const { owner, name } = repoParts(repo);
  const prResult = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${issueNumber}`);
  if (isGitHubError(prResult)) return commandError("pr comment", repo, prResult, { issueNumber, planned });
    const comment = await githubRequest<GitHubComment>(token, "POST", `/repos/${owner}/${name}/issues/${issueNumber}/comments`, { body });
    if (isGitHubError(comment)) return commandError("pr comment", repo, comment, { issueNumber, planned });
    return {
      ok: true,
      command: "pr comment create",
    repo,
    issueNumber,
    pr: prSummary(prResult),
    comment: commentSummary(comment),
    request: {
      method: "POST",
      path: `/repos/${owner}/${name}/issues/${issueNumber}/comments`,
      bodyChars: body.length,
    },
    validation: {
      repo: repoSummary(repoResult),
      pr: prSummary(prResult),
      bodySource: bodySource.bodySource,
    },
    rest: true,
  };
}

async function prUpdate(repo: string, token: string, number: number, options: GitHubOptions, commandName = "pr update"): Promise<GitHubCommandResult> {
  let bodySource: { body: string; bodySource: Record<string, unknown> } | null;
  try {
    bodySource = readOptionalMarkdownBody(options, commandName);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), { number });
  }
  if (bodySource === null && options.title === undefined) {
    return validationError(commandName, repo, `${commandName} requires --title <title>, --body-file <file|->, or --body <text>`, { number });
  }
  if (options.mode === "append" && bodySource === null) {
    return validationError(commandName, repo, `${commandName} --mode append requires a body source`, { number });
  }
  const { owner, name } = repoParts(repo);
  let oldPr: GitHubPullRequest | null = null;
  if (token.length > 0 && options.mode === "append") {
    const pr = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
    if (isGitHubError(pr)) return commandError(commandName, repo, pr, { number, phase: "read-before-update" });
    oldPr = pr;
  }
  const finalBody = bodySource === null ? undefined : (options.mode === "append" ? `${oldPr?.body ?? ""}${bodySource.body}` : bodySource.body);
  const changedFields = [
    ...(options.title === undefined ? [] : ["title"]),
    ...(bodySource === null ? [] : ["body"]),
  ];
  const bodyPlan = prEditBodyPlan(options.mode, bodySource, finalBody, oldPr?.body ?? null);
  const request = {
    method: "PATCH",
    path: `/repos/{owner}/{repo}/pulls/${number}`,
    changedFields,
    body: {
      ...(options.title === undefined ? {} : { title: "provided" }),
      ...(finalBody === undefined ? {} : { bodyChars: finalBody.length, bodySha: bodySha(finalBody) }),
    },
  };
  if (options.dryRun) {
    return {
      ok: true,
      command: commandName,
      repo,
      dryRun: true,
      planned: true,
      number,
      url: prUrl(repo, number),
      changedFields,
      ...(bodyPlan === null ? {} : { body: bodyPlan }),
      request,
      graphQl: false,
      projectsClassic: false,
    };
  }
  const payload: Record<string, unknown> = {};
  if (finalBody !== undefined) payload.body = finalBody;
  if (options.title !== undefined) payload.title = options.title;
  const pr = await githubRequest<GitHubPullRequest>(token, "PATCH", `/repos/${owner}/${name}/pulls/${number}`, payload);
  if (isGitHubError(pr)) return commandError(commandName, repo, pr, { number, planned: { changedFields, body: bodyPlan, request } });
  return {
    ok: true,
    command: commandName,
    repo,
    pr: pr.number ?? number,
    number: pr.number ?? number,
    changedFields,
    url: pr.html_url ?? prUrl(repo, number),
    ...(bodyPlan === null ? {} : { body: bodyPlan }),
    request: { ...request, path: `/repos/${owner}/${name}/pulls/${number}` },
    rest: true,
    graphQl: false,
    projectsClassic: false,
  };
}

async function prState(repo: string, token: string, number: number, state: "open" | "closed", dryRun: boolean): Promise<GitHubCommandResult> {
  const command = state === "closed" ? "pr close" : "pr reopen";
  if (dryRun) return { ok: true, command, repo, dryRun: true, number, wouldPatch: { state } };
  const { owner, name } = repoParts(repo);
  const pr = await githubRequest<GitHubPullRequest>(token, "PATCH", `/repos/${owner}/${name}/pulls/${number}`, { state });
  if (isGitHubError(pr)) return commandError(command, repo, pr, { number });
  return { ok: true, command, repo, number, pullRequest: prSummary(pr), rest: true };
}

async function listIssueComments(token: string, repo: string, issueNumber: number): Promise<GitHubComment[] | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubComment[]>(token, "GET", `/repos/${owner}/${name}/issues/${issueNumber}/comments?per_page=100`);
}

function githubSearchLabelQualifier(label: string): string {
  if (/^[A-Za-z0-9_.:-]+$/u.test(label)) return `label:${label}`;
  return `label:"${label.replace(/["\\]/gu, "\\$&")}"`;
}

function issueListPaginationSummary(result: GitHubIssueListResult): Record<string, unknown> {
  return {
    pageSize: result.pageSize,
    fetchedPages: result.fetchedPages,
    exhausted: result.exhausted,
    hasMore: result.hasMore,
    pages: result.pages,
  };
}

async function listIssues(token: string, repo: string, state: IssueListState, limit: number, search?: string, labels: string[] = []): Promise<GitHubIssueListResult | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  const normalizedSearch = String(search ?? "").trim();
  const pageSize = GITHUB_REST_PAGE_SIZE;
  const collected: GitHubIssue[] = [];
  const pages: GitHubIssueListPage[] = [];
  let rawCount = 0;
  let fetchedPages = 0;
  let exhausted = false;
  let searchTotalCount: number | undefined;
  let searchIncomplete: boolean | undefined;
  const maxPages = Math.max(1, Math.ceil(limit / pageSize) + 10);
  for (let page = 1; collected.length < limit && page <= maxPages; page += 1) {
    const perPage = pageSize;
    let path: string;
    let rawIssueItems: GitHubIssue[];
    if (normalizedSearch) {
      const qualifiers = [`repo:${owner}/${name}`, "type:issue"];
      if (state !== "all") qualifiers.push(`state:${state}`);
      for (const label of labels) qualifiers.push(githubSearchLabelQualifier(label));
      const params = new URLSearchParams({ q: `${normalizedSearch} ${qualifiers.join(" ")}`, per_page: String(perPage), page: String(page) });
      path = `/search/issues?${params.toString()}`;
      const response = await githubRequest<GitHubIssueSearchResponse>(token, "GET", path);
      if (isGitHubError(response)) return response;
      rawIssueItems = Array.isArray(response.items) ? response.items : [];
      if (searchTotalCount === undefined) searchTotalCount = response.total_count;
      searchIncomplete = searchIncomplete === true || response.incomplete_results === true;
    } else {
      const params = new URLSearchParams({ state, per_page: String(perPage), page: String(page) });
      if (labels.length > 0) params.set("labels", labels.join(","));
      path = `/repos/${owner}/${name}/issues?${params.toString()}`;
      const response = await githubRequest<GitHubIssue[]>(token, "GET", path);
      if (isGitHubError(response)) return response;
      rawIssueItems = response;
    }
    const issueItems = rawIssueItems.filter((issue) => issue.pull_request === undefined);
    rawCount += rawIssueItems.length;
    fetchedPages += 1;
    pages.push({ path, rawCount: rawIssueItems.length, issueCount: issueItems.length });
    collected.push(...issueItems);
    if (rawIssueItems.length < perPage) {
      exhausted = true;
      break;
    }
  }
  const hasMore = collected.length > limit || !exhausted;
  return {
    items: collected.slice(0, limit),
    rawCount,
    fetchedPages,
    pageSize,
    exhausted,
    hasMore,
    pages,
    searchTotalCount,
    searchIncomplete,
  };
}

async function getIssue(token: string, repo: string, issueNumber: number): Promise<GitHubIssue | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubIssue>(token, "GET", `/repos/${owner}/${name}/issues/${issueNumber}`);
}

function selectedIssueJson(issue: GitHubIssue, comments: GitHubComment[] | null, fields: IssueViewJsonField[] | undefined): Record<string, unknown> | null {
  if (fields === undefined) return null;
  const summary = issueSummary(issue);
  const selected: Record<string, unknown> = {};
  for (const field of fields) {
    if (field === "comments") {
      selected.comments = (comments ?? []).map(commentSummary);
    } else {
      selected[field] = summary[field];
    }
  }
  return selected;
}

async function issueRead(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, commandName = "issue read", disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
  const issue = await getIssue(token, repo, issueNumber);
  if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber });
  const needsComments = jsonFields === undefined || jsonFields.includes("comments");
  const comments = needsComments ? await listIssueComments(token, repo, issueNumber) : null;
  if (isGitHubError(comments)) return commandError(commandName, repo, comments, { issueNumber, issue: issueSummary(issue) });
  return {
    ok: true,
    command: commandName,
    repo,
    ...(disclosure === null ? {} : { disclosure }),
    issue: issueSummary(issue),
    codeQueueBoardHint: codeQueueBoardCommanderBriefHint(issueNumber, issue.body ?? ""),
    ...(comments === null ? {} : { comments: comments.map(commentSummary) }),
    ...(jsonFields === undefined ? {} : {
      jsonFields,
      json: selectedIssueJson(issue, comments, jsonFields),
      compatibility: {
        legacyJsonBodyPath: ".data.issue.body",
      },
    }),
  };
}

async function issueView(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
  return issueRead(repo, token, issueNumber, jsonFields, "issue view", disclosure);
}

function shellWord(value: string): string {
  return JSON.stringify(value);
}

function issueListNextCommand(repo: string, state: IssueListState, limit: number, search: string, labels: string[]): string {
  const parts = [
    "bun scripts/cli.ts gh issue list",
    "--repo", shellWord(repo),
    "--state", state,
    "--limit", String(Math.min(limit * 2, MAX_ISSUE_LIST_LIMIT)),
  ];
  if (search.length > 0) parts.push("--search", shellWord(search));
  for (const label of labels) parts.push("--label", shellWord(label));
  return parts.join(" ");
}

async function issueList(repo: string, token: string, state: IssueListState, limit: number, jsonFields: IssueListJsonField[] | undefined, search?: string, labels: string[] = [], noDump = false): Promise<GitHubCommandResult> {
  const normalizedSearch = String(search ?? "").trim();
  const result = await listIssues(token, repo, state, limit, normalizedSearch, labels);
  if (isGitHubError(result)) return commandError("issue list", repo, result, { state, limit, search: normalizedSearch || null, labels });
  const issues = result.items;
  const fields = jsonFields ?? ISSUE_LIST_JSON_FIELDS.slice();
  return {
    ok: true,
    command: "issue list",
    repo,
    ...(noDump ? { noDump: true } : {}),
    state,
    limit,
    search: normalizedSearch || null,
    labels,
    count: issues.length,
    rawCount: result.rawCount,
    searchTotalCount: result.searchTotalCount,
    searchIncomplete: result.searchIncomplete,
    pagination: issueListPaginationSummary(result),
    hasMore: result.hasMore,
    jsonFields: fields,
    issues: issues.map((issue) => issueListSummary(issue, fields)),
    ...(result.hasMore && limit < MAX_ISSUE_LIST_LIMIT
      ? {
        next: {
          command: issueListNextCommand(repo, state, limit, normalizedSearch, labels),
          note: "Increase --limit to continue scanning beyond the current bounded result set.",
        },
      }
      : result.hasMore
        ? {
          scanLimit: {
            maxLimitReached: true,
            maxLimit: MAX_ISSUE_LIST_LIMIT,
            note: "The command reached the maximum bounded issue scan; narrow with --state/--label/--search before treating the repository as exhausted.",
          },
        }
      : {}),
    request: {
      method: "GET",
      path: normalizedSearch ? "/search/issues" : "/repos/{owner}/{repo}/issues",
      query: normalizedSearch
        ? { q: `${normalizedSearch} repo:${repo} type:issue${state === "all" ? "" : ` state:${state}`}${labels.map((label) => ` ${githubSearchLabelQualifier(label)}`).join("")}`, per_page: GITHUB_REST_PAGE_SIZE }
        : { state, labels, per_page: GITHUB_REST_PAGE_SIZE },
    },
    note: "GitHub's issues endpoint may include pull requests; this command filters pull requests from .issues.",
  };
}

async function issueBoardAudit(repo: string, token: string, options: GitHubOptions): Promise<GitHubCommandResult> {
  const commandName = "issue board-audit";
  const boardIssue = await getIssue(token, repo, options.boardIssue);
  if (isGitHubError(boardIssue)) return commandError(commandName, repo, boardIssue, { boardIssue: options.boardIssue });
  const parsed = parseBoardTables(boardIssue.body ?? "");
  const openRows = parsed.sections.filter((section) => section.kind === "open").flatMap((section) => section.rows);
  const closedRows = parsed.sections.filter((section) => section.kind === "closed").flatMap((section) => section.rows);
  const body = boardIssue.body ?? "";

  return {
    ok: true,
    command: commandName,
    repo,
    dryRun: true,
    planned: true,
    readOnly: true,
    boardIssue: {
      number: boardIssue.number,
      title: boardIssue.title,
      state: boardIssue.state,
      url: boardIssue.html_url,
      bodyChars: body.length,
      bodyLines: body.length === 0 ? 0 : normalizeNewlines(body).split("\n").length,
      bodySha: bodySha(body),
      updatedAt: boardIssue.updated_at ?? null,
    },
    config: {
      limit: options.limit,
      knownMetaIssues: mergedKnownMetaIssues(options),
      ignoredIssues: options.ignoredIssues,
      briefIndexManagedIssuePattern: options.boardIssue === CODE_QUEUE_BOARD_TARGET_ISSUE ? "YYYY-MM-DD 指挥简报（北京时间）" : null,
      openClosedCoverageValidation: false,
      requiredColumns: [],
    },
    summary: {
      openIssues: null,
      closedIssues: null,
      openRows: openRows.length,
      closedRows: closedRows.length,
      parsedSections: parsed.sections.length,
      parsedRows: parsed.sections.reduce((sum, section) => sum + section.rows.length, 0),
      parserWarnings: parsed.warnings.length,
      missingOpenIssues: 0,
      closedInOpenRows: 0,
      missingClosedRows: 0,
      openInClosedRows: 0,
      rowValidationWarnings: 0,
      ignoredIssues: 0,
      boardOnlyRows: 0,
      staleOpenRows: 0,
      recommendedActions: 0,
    },
    validation: {
      openClosedCoverage: {
        enabled: false,
        reason: "OPEN/CLOSED table coverage validation has been disabled; this command no longer compares GitHub issue state against board rows.",
      },
      rowRequiredColumns: {
        enabled: false,
        reason: "board-audit no longer reports missing required row columns; use board-row commands when intentionally maintaining a legacy OPEN/CLOSED table.",
      },
    },
    sections: parsed.sections.map((section) => ({
      kind: section.kind,
      heading: section.heading,
      headingLine: section.headingLine,
      headerLine: section.headerLine,
      headers: section.headers,
      rows: section.rows.length,
      issueNumbers: section.rows.flatMap((row) => row.issueNumbers),
    })),
    parserWarnings: parsed.warnings,
    missingOpenIssues: [],
    closedInOpenRows: [],
    missingClosedRows: [],
    openInClosedRows: [],
    staleOpenRows: [],
    boardOnlyRows: [],
    rowValidationWarnings: [],
    ignoredIssues: [],
    recommendedActions: [],
    request: {
      method: "GET",
      paths: [
        `/repos/{owner}/{repo}/issues/${options.boardIssue}`,
      ],
      query: {},
    },
    note: "Read-only board structure audit; OPEN/CLOSED coverage validation is disabled, no issue body was edited, no issue was closed, and no comments were written.",
  };
}

async function issueCreate(repo: string, token: string, options: GitHubOptions): Promise<GitHubCommandResult> {
  if (options.title === undefined) throw new Error("issue create requires --title <title>");
  if (options.body !== undefined) {
    return validationError("issue create", repo, "issue create does not support --body; use --body-file <file|-> for Markdown", {
      title: options.title,
      bodySource: "inline",
    });
  }
  const { body, bodySource } = readMarkdownBody({
    ...options,
    body: undefined,
  }, "issue create");
  const labels = options.labels;
  if (options.dryRun) {
    return {
      ok: true,
      command: "issue create",
      repo,
      dryRun: true,
      planned: true,
      title: options.title,
      labels,
      ...writeBodyPlan("issue create", repo, body, bodySource, { title: options.title, labels }),
    };
  }
  const { owner, name } = repoParts(repo);
  const payload: Record<string, unknown> = { title: options.title, body };
  if (labels.length > 0) payload.labels = labels;
  const issue = await githubRequest<GitHubIssue>(token, "POST", `/repos/${owner}/${name}/issues`, payload);
  if (isGitHubError(issue)) return commandError("issue create", repo, issue, { title: options.title, labels });
  const appliedLabels = issueLabelNames(issue);
  const missingLabels = labels.filter((label) => !appliedLabels.includes(label));
  if (missingLabels.length > 0) {
    return validationError("issue create", repo, "GitHub created the issue but did not return all requested labels; refusing to report silent label success", {
      issue: issueSummary(issue),
      requestedLabels: labels,
      appliedLabels,
      missingLabels,
    });
  }
  return { ok: true, command: "issue create", repo, issue: issueSummary(issue), labels, bodySource, rest: true };
}

async function issueEdit(repo: string, token: string, issueNumber: number, options: GitHubOptions, commandName = "issue edit"): Promise<GitHubCommandResult> {
  if (options.bodyFile === undefined) {
    return validationError(commandName, repo, `${commandName} requires --body-file <file|->`, { issueNumber });
  }
  let bodyInput: { body: string; bodySource: Record<string, unknown> };
  try {
    bodyInput = readMarkdownBodyFileOrStdin(options.bodyFile);
  } catch (error) {
    return validationError(commandName, repo, error instanceof Error ? error.message : String(error), { issueNumber });
  }
  const { body, bodySource } = bodyInput;
  const needsProfileMetadata = issueProfileNeedsMetadata(issueNumber, options.bodyProfile);
  if (options.mode === "replace" && !needsProfileMetadata) {
    const bodyGuard = validateIssueBodyGuard(repo, issueNumber, body, options);
    if (bodyGuard !== null) return bodyGuard;
  }
  const concurrencyOptionError = assertConcurrencyOptions(options, commandName);
  if (concurrencyOptionError !== null) return concurrencyOptionError;
  let oldIssue: GitHubIssue | null = null;
  let briefDiff: CommanderBriefDiff | null = null;
  let profileContext: IssueProfileValidationContext = {};
  const claudeQqConfig = commanderBriefClaudeQqConfig();
  if (options.notifyClaudeQqBriefDiff && issueNumber !== COMMANDER_BRIEF_TARGET_ISSUE) {
    return validationError(commandName, repo, "--notify-claudeqq-brief-diff is only supported for commander brief issue #24", { issueNumber });
  }
  const needsReadBeforeEdit = options.mode === "append"
    || needsProfileMetadata && token.length > 0
    || !options.dryRun;
  if (needsReadBeforeEdit) {
    const issue = await getIssue(token, repo, issueNumber);
    if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber, phase: "read-before-update" });
    oldIssue = issue;
    profileContext = { issueTitle: issue.title, issueBody: issue.body, issueMetadataFetched: true };
    const concurrencyError = validateIssueConcurrency(repo, issueNumber, issue, options);
    if (concurrencyError !== null) return concurrencyError;
    if (options.notifyClaudeQqBriefDiff) briefDiff = commanderBriefDiff(issue.body ?? "", options.mode === "append" ? `${issue.body ?? ""}${body}` : body);
  }
  const finalBody = options.mode === "append" ? `${oldIssue?.body ?? ""}${body}` : body;
  if (options.mode === "append" || needsProfileMetadata) {
    const bodyGuard = validateIssueBodyGuard(repo, issueNumber, finalBody, options, profileContext);
    if (bodyGuard !== null) return bodyGuard;
  }
  if (options.dryRun) {
    const dryRunDiff = options.notifyClaudeQqBriefDiff ? commanderBriefDiff("", finalBody) : null;
    const guard = issueEditGuardSummary(issueNumber, finalBody, options, profileContext);
    let dryRunOldBody: Record<string, unknown> = {
      fetched: false,
      bodyChars: null,
      bodySha: null,
      updatedAt: null,
      reason: token.length > 0 ? "not-requested" : "no token supplied to dry-run",
    };
    if (oldIssue !== null) {
      const oldBody = oldIssue.body ?? "";
      dryRunOldBody = {
        fetched: true,
        bodyChars: oldBody.length,
        bodySha: bodySha(oldBody),
        updatedAt: oldIssue.updated_at ?? null,
      };
    } else if (token.length > 0) {
      const issue = await getIssue(token, repo, issueNumber);
      if (isGitHubError(issue)) {
        dryRunOldBody = {
          fetched: false,
          bodyChars: null,
          bodySha: null,
          updatedAt: null,
          readError: {
            degradedReason: issue.degradedReason,
            runnerDisposition: issue.runnerDisposition,
            message: issue.message,
            status: issue.status ?? null,
          },
        };
      } else {
        const oldBody = issue.body ?? "";
        dryRunOldBody = {
          fetched: true,
          bodyChars: oldBody.length,
          bodySha: bodySha(oldBody),
          updatedAt: issue.updated_at ?? null,
        };
      }
    }
    return {
      ok: true,
      command: commandName,
      repo,
      dryRun: true,
      issueNumber,
      mode: options.mode,
      ...dryRunBody(repo, options.title, finalBody),
      disclosure: issueWriteDisclosure(options, repo, issueNumber, true),
      readCommands: issueBodyReadCommands(repo, issueNumber),
      guard,
      update: bodyUpdatePlan(commandName, repo, issueNumber, options.mode, body, bodySource, oldIssue?.body ?? null),
      bodyOnlySafety: {
        oldBody: dryRunOldBody,
        newBody: {
          bodyChars: finalBody.length,
          bodySha: bodySha(finalBody),
          bodyPreview: preview(finalBody),
          bodyPreviewLines: previewLines(finalBody),
        },
      },
      concurrency: {
        dryRunNoWrite: true,
        expectUpdatedAt: options.expectUpdatedAt ?? null,
        expectBodySha: options.expectBodySha ?? null,
        note: "non-dry-run checks --expect-updated-at and --expect-body-sha against the current GitHub issue before PATCH",
      },
      wouldPatch: {
        issueNumber,
        title: options.title ?? null,
        bodySource,
        mode: options.mode,
        bodyChars: finalBody.length,
        bodySha: bodySha(finalBody),
      },
      ...(options.notifyClaudeQqBriefDiff
        ? {
          commanderBriefNotification: commanderBriefNotificationPlan(issueNumber, finalBody, dryRunDiff ?? commanderBriefDiff("", finalBody), claudeQqConfig),
          dryRunOldBodySource: "not-fetched",
          dryRunDiffReliability: "new-body-only preview; non-dry-run reads the GitHub issue body before PATCH and sends only sections absent from the old body",
          dryRunNoWrite: true,
          dryRunNoClaudeQqSend: true,
        }
        : {}),
    };
  }
  const { owner, name } = repoParts(repo);
  const payload: Record<string, unknown> = { body: finalBody };
  if (options.title !== undefined) payload.title = options.title;
  const issue = await githubRequest<GitHubIssue>(token, "PATCH", `/repos/${owner}/${name}/issues/${issueNumber}`, payload);
  if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber });
  const guard = issueEditGuardSummary(issueNumber, finalBody, options, profileContext);
  const concurrency = oldIssue === null
    ? { checked: false, expectUpdatedAt: options.expectUpdatedAt ?? null, expectBodySha: options.expectBodySha ?? null }
    : { checked: true, oldIssueUpdatedAt: oldIssue.updated_at ?? null, oldBodySha: bodySha(oldIssue.body ?? ""), expectUpdatedAt: options.expectUpdatedAt ?? null, expectBodySha: options.expectBodySha ?? null };
  if (!options.notifyClaudeQqBriefDiff) {
    return {
      ok: true,
      command: commandName,
      repo,
      issue: issueSummary(issue, { includeBody: options.raw || options.full }),
      mode: options.mode,
      bodySource,
      guard,
      concurrency,
      disclosure: issueWriteDisclosure(options, repo, issueNumber, false),
      readCommands: issueBodyReadCommands(repo, issueNumber),
      rest: true,
    };
  }

  const diff = briefDiff ?? commanderBriefDiff(oldIssue?.body ?? "", finalBody);
  const claudeqq = diff.ok
    ? await sendCommanderBriefClaudeQq(claudeQqConfig, diff.message)
    : {
      ok: true,
      attempted: false,
      skipped: true,
      skippedReason: diff.skippedReason ?? "no commander brief diff to send",
      target: maskedTarget(claudeQqConfig),
    } satisfies ClaudeQqSendResult;
  return {
    ok: true,
    command: commandName,
    repo,
    issue: issueSummary(issue, { includeBody: options.raw || options.full }),
    guard,
    concurrency,
    disclosure: issueWriteDisclosure(options, repo, issueNumber, false),
    readCommands: issueBodyReadCommands(repo, issueNumber),
    rest: true,
    commanderBriefNotification: {
      issueNumber,
      oldIssueUpdatedAt: oldIssue?.updated_at ?? null,
      diff: {
        ok: diff.ok,
        mode: diff.mode,
        chars: diff.chars,
        sectionCount: diff.sectionCount,
        skippedReason: diff.skippedReason ?? null,
        preview: diff.ok ? preview(diff.message) : "",
        previewLines: diff.ok ? previewLines(diff.message) : [],
        containsLiteralBackslashN: diff.message.includes("\\n"),
        containsBackticks: diff.message.includes("`"),
        containsMarkdownTable: /^\s*\|.+\|\s*$/m.test(diff.message),
      },
    },
    claudeqq,
  };
}

async function issueComment(repo: string, token: string, issueNumber: number, options: GitHubOptions): Promise<GitHubCommandResult> {
  let bodyInput: { body: string; bodySource: Record<string, unknown> };
  try {
    bodyInput = readIssueCommentBody(options);
  } catch (error) {
    return validationError("issue comment create", repo, error instanceof Error ? error.message : String(error), { issueNumber });
  }
  const { body, bodySource } = bodyInput;
  if (options.dryRun) {
    return {
      ok: true,
      command: "issue comment create",
      repo,
      dryRun: true,
      planned: true,
      issueNumber,
      readCommands: issueCommentReadCommands(repo, issueNumber),
      ...writeBodyPlan("issue comment create", repo, body, bodySource, { issueNumber }),
    };
  }
  const { owner, name } = repoParts(repo);
  const comment = await githubRequest<GitHubComment>(token, "POST", `/repos/${owner}/${name}/issues/${issueNumber}/comments`, { body });
  if (isGitHubError(comment)) return commandError("issue comment", repo, comment, { issueNumber });
  return {
    ok: true,
    command: "issue comment create",
    repo,
    issueNumber,
    comment: compactCommentSummary(comment),
    bodySource,
    bodyChars: body.length,
    bodySha: bodySha(body),
    bodyPreview: preview(body),
    source: String(bodySource.kind ?? "unknown"),
    readCommands: issueCommentReadCommands(repo, issueNumber),
    rest: true,
  };
}

async function commentDelete(repo: string, token: string, ownerKind: "issue" | "pr", commentId: number, dryRun: boolean): Promise<GitHubCommandResult> {
  const command = `${ownerKind} comment delete`;
  const { owner, name } = repoParts(repo);
  if (dryRun) {
    return {
      ok: true,
      command,
      repo,
      dryRun: true,
      planned: true,
      commentId,
      request: { method: "DELETE", path: `/repos/{owner}/{repo}/issues/comments/${commentId}` },
    };
  }
  const result = await githubRequest<null>(token, "DELETE", `/repos/${owner}/${name}/issues/comments/${commentId}`);
  if (isGitHubError(result)) return commandError(command, repo, result, { commentId });
  return {
    ok: true,
    command,
    repo,
    commentId,
    deleted: true,
    request: { method: "DELETE", path: `/repos/${owner}/${name}/issues/comments/${commentId}` },
    rest: true,
  };
}

async function issueState(repo: string, token: string, issueNumber: number, state: "open" | "closed", dryRun: boolean, options?: GitHubOptions): Promise<GitHubCommandResult> {
  const command = state === "closed" ? "issue close" : "issue reopen";
  let lifecycleComment: { body: string; bodySource: Record<string, unknown> } | null = null;
  try {
    lifecycleComment = options === undefined ? null : readIssueLifecycleCommentBody(options, command);
  } catch (error) {
    return validationError(command, repo, error instanceof Error ? error.message : String(error), { issueNumber });
  }
  if (dryRun) {
    return {
      ok: true,
      command,
      dryRun: true,
      repo,
      issueNumber,
      disclosure: issueLifecycleDisclosure(repo, issueNumber, true),
      readCommands: issueBodyReadCommands(repo, issueNumber),
      comment: lifecycleComment === null
        ? null
        : {
          planned: true,
          ...writeBodyPlan("issue comment create", repo, lifecycleComment.body, lifecycleComment.bodySource, { issueNumber }),
        },
      wouldPatch: { state },
    };
  }
  const { owner, name } = repoParts(repo);
  let commentSummary: Record<string, unknown> | null = null;
  if (lifecycleComment !== null) {
    const comment = await githubRequest<GitHubComment>(token, "POST", `/repos/${owner}/${name}/issues/${issueNumber}/comments`, { body: lifecycleComment.body });
    if (isGitHubError(comment)) {
      return commandError(command, repo, comment, {
        issueNumber,
        phase: "comment",
        commentBodySource: lifecycleComment.bodySource,
        commentBodyChars: lifecycleComment.body.length,
        commentBodySha: bodySha(lifecycleComment.body),
      });
    }
    commentSummary = compactCommentSummary(comment);
  }
  const issue = await githubRequest<GitHubIssue>(token, "PATCH", `/repos/${owner}/${name}/issues/${issueNumber}`, { state });
  if (isGitHubError(issue)) return commandError(command, repo, issue, { issueNumber, phase: "state", comment: commentSummary });
  return {
    ok: true,
    command,
    repo,
    comment: commentSummary,
    issue: issueLifecycleSummary(issue),
    disclosure: issueLifecycleDisclosure(repo, issueNumber, false),
    readCommands: issueBodyReadCommands(repo, issueNumber),
    rest: true,
  };
}

function parseGitHubTimestamp(value: string | undefined): number | null {
  if (value === undefined) return null;
  const millis = Date.parse(value);
  return Number.isFinite(millis) ? millis : null;
}

function inactiveIssueCandidates(issues: GitHubIssue[], cutoffMs: number): GitHubIssue[] {
  return issues.filter((issue) => {
    const updatedAtMs = parseGitHubTimestamp(issue.updated_at);
    return updatedAtMs !== null && updatedAtMs < cutoffMs;
  });
}

async function closeIssueForBatch(repo: string, token: string, issueNumber: number): Promise<GitHubIssue | GitHubErrorPayload> {
  const { owner, name } = repoParts(repo);
  return githubRequest<GitHubIssue>(token, "PATCH", `/repos/${owner}/${name}/issues/${issueNumber}`, { state: "closed" });
}

async function issueStaleClose(repo: string, token: string, options: GitHubOptions): Promise<GitHubCommandResult> {
  const command = "issue stale-close";
  const observedAt = new Date();
  const cutoffMs = observedAt.getTime() - Math.round(options.inactiveHours * 60 * 60 * 1000);
  const cutoffAt = new Date(cutoffMs).toISOString();
  const result = await listIssues(token, repo, "open", options.limit, "", options.labels);
  if (isGitHubError(result)) {
    return commandError(command, repo, result, {
      state: "open",
      limit: options.limit,
      inactiveHours: options.inactiveHours,
      cutoffAt,
      labels: options.labels,
    });
  }
  const staleIssues = inactiveIssueCandidates(result.items, cutoffMs);
  const base = {
    ok: true,
    command,
    repo,
    dryRun: options.dryRun,
    state: "open",
    inactiveHours: options.inactiveHours,
    observedAt: observedAt.toISOString(),
    cutoffAt,
    limit: options.limit,
    labels: options.labels,
    scannedCount: result.items.length,
    rawCount: result.rawCount,
    staleCount: staleIssues.length,
    pagination: issueListPaginationSummary(result),
    hasMore: result.hasMore,
    stale: issueLifecycleBatchSummary(staleIssues),
    policy: {
      basis: "GitHub issue updatedAt",
      selectedWhen: "updatedAt is older than observedAt - inactiveHours",
      commentsAndStateChangesCountAsActivity: true,
      pullRequestsFiltered: true,
    },
    readCommands: {
      dryRun: `bun scripts/cli.ts gh issue stale-close --repo ${repo} --inactive-hours ${options.inactiveHours} --limit ${options.limit} --dry-run`,
      openList: `bun scripts/cli.ts gh issue list --repo ${repo} --state open --limit ${options.limit} --json number,title,state,url,updatedAt`,
    },
    ...(result.hasMore && options.limit < MAX_ISSUE_LIST_LIMIT
      ? {
        next: {
          command: `bun scripts/cli.ts gh issue stale-close --repo ${repo} --inactive-hours ${options.inactiveHours} --limit ${Math.min(options.limit * 2, MAX_ISSUE_LIST_LIMIT)} --dry-run`,
          note: "The scan reached the bounded --limit before GitHub pagination was exhausted; increase --limit before treating the cleanup as complete.",
        },
      }
      : result.hasMore
        ? {
          scanLimit: {
            maxLimitReached: true,
            maxLimit: MAX_ISSUE_LIST_LIMIT,
            note: "The cleanup reached the maximum bounded issue scan; narrow with --label or split the policy before treating it as complete.",
          },
        }
      : {}),
  };
  if (options.dryRun) {
    return {
      ...base,
      planned: true,
      wouldCloseCount: staleIssues.length,
      wouldCloseNumbers: staleIssues.map((issue) => issue.number),
      note: staleIssues.length === 0
        ? "No open issues matched the inactive-hours policy; no GitHub issue would be closed."
        : "Dry-run only; no GitHub issue was closed.",
    };
  }

  const closed: GitHubIssue[] = [];
  const failures: Array<Record<string, unknown>> = [];
  for (const issue of staleIssues) {
    const closedIssue = await closeIssueForBatch(repo, token, issue.number);
    if (isGitHubError(closedIssue)) {
      failures.push({
        number: issue.number,
        title: issue.title,
        url: issue.html_url,
        updatedAt: issue.updated_at ?? null,
        degradedReason: closedIssue.degradedReason,
        runnerDisposition: closedIssue.runnerDisposition,
        message: closedIssue.message,
        status: closedIssue.status ?? null,
      });
      continue;
    }
    closed.push(closedIssue);
  }

  return {
    ...base,
    dryRun: false,
    planned: false,
    closedCount: closed.length,
    failedCount: failures.length,
    closed: issueLifecycleBatchSummary(closed),
    failures,
    rest: true,
    ok: failures.length === 0,
    ...(failures.length > 0
      ? {
        degradedReason: "github-transient" as GitHubDegradedReason,
        runnerDisposition: "infra-blocked" as RunnerDisposition,
        retryable: true,
      }
      : {}),
    note: failures.length === 0
      ? "Closed all open issues that matched the inactive-hours policy."
      : "Some stale issue close operations failed; rerun the same command after checking failures.",
  };
}

function escapeSnippet(text: string, index: number, radius = 80): string {
  const start = Math.max(0, index - radius);
  const end = Math.min(text.length, index + radius + 40);
  return text.slice(start, end).replace(/\n/g, "\\n");
}

function lineColumnAt(text: string, index: number): { lineNumber: number; column: number } {
  let lineNumber = 1;
  let column = 1;
  for (let cursor = 0; cursor < index && cursor < text.length; cursor += 1) {
    if (text[cursor] === "\n") {
      lineNumber += 1;
      column = 1;
    } else {
      column += 1;
    }
  }
  return { lineNumber, column };
}

function lineTextAt(text: string, lineNumber: number): string {
  const lines = text.split(/\r?\n/);
  return lines[lineNumber - 1] ?? "";
}

function isInsideFencedCodeBlock(text: string, index: number): boolean {
  const lines = text.slice(0, index).split(/\r?\n/);
  let openFence = false;
  for (const line of lines) {
    const trimmed = line.trimStart();
    if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) openFence = !openFence;
  }
  return openFence;
}

function isInsideInlineCode(text: string, index: number): boolean {
  const { lineNumber, column } = lineColumnAt(text, index);
  const line = lineTextAt(text, lineNumber);
  const before = line.slice(0, Math.max(0, column - 1));
  const after = line.slice(Math.max(0, column - 1));
  return (before.match(/`/g)?.length ?? 0) % 2 === 1 && (after.match(/`/g)?.length ?? 0) > 0;
}

function isExplanatoryLiteralBackslashN(text: string, index: number): boolean {
  if (isInsideFencedCodeBlock(text, index) || isInsideInlineCode(text, index)) return true;
  const window = text.slice(Math.max(0, index - 90), Math.min(text.length, index + 90)).toLowerCase();
  return [
    "字面量",
    "说明",
    "示例",
    "举例",
    "提到",
    "引用",
    "文本",
    "字符串",
    "escape",
    "literal",
    "example",
    "mention",
    "quoted",
    "反斜杠",
  ].some((keyword) => window.includes(keyword));
}

function cleanupEscapedText(text: string): string {
  return text
    .replace(/\\r\\n/g, "\n")
    .replace(/\\n/g, "\n")
    .replace(/\\t/g, "\t")
    .replace(/\\x0a/gi, "\n")
    .replace(/\\x1b/gi, "")
    .replace(/\\u001b/gi, "")
    .replace(/\\033/g, "")
    .replace(/\\`/g, "`");
}

function escapeBodyId(bodyKind: EscapeBodyKind, issueNumber: number, issueId: number, commentId?: number): string {
  return bodyKind === "issue-body"
    ? `issue:${issueNumber}:body:${issueId}`
    : `issue:${issueNumber}:comment:${commentId ?? "unknown"}`;
}

function textFindingPatterns(): EscapePatternDefinition[] {
  return [
    {
      kind: "literal-backslash-n",
      pattern: /\\n/g,
      description: "literal backslash-n",
    },
    {
      kind: "literal-backslash-t",
      pattern: /\\t/g,
      description: "literal backslash-t",
    },
    {
      kind: "escaped-backtick",
      pattern: /\\`/g,
      description: "escaped backtick",
    },
    {
      kind: "shell-escaped-newline",
      pattern: /\\r\\n|\\012|\\x0a/gi,
      description: "shell escaped newline",
    },
    {
      kind: "ansi-escape-literal",
      pattern: /\\x1b|\\u001b|\\033/gi,
      description: "ANSI escape literal",
    },
  ];
}

function bodyRiskFindings(kind: EscapeBodyKind, issueNumber: number, issueId: number, body: string | null, url: string, commentId?: number): EscapeMatchFinding[] {
  const bodyChars = body === null ? null : body.length;
  const bodyTrimmedChars = body === null ? null : body.trim().length;
  const location = {
    type: kind === "issue-body" ? "issue" as const : "comment" as const,
    bodyKind: kind,
    bodyId: escapeBodyId(kind, issueNumber, issueId, commentId),
    issueNumber,
    issueId,
    ...(commentId === undefined ? {} : { commentId }),
    url,
    bodyChars,
    bodyTrimmedChars,
  };
  if (body === null) {
    return [{
      ...location,
      kind: "null-body",
      classification: "risk",
      severity: "high",
      reason: "GitHub returned a null body",
      snippet: "",
      lineNumber: 0,
      column: 0,
      match: "",
    }];
  }
  const trimmed = body.trim();
  if (trimmed.toLowerCase() === "null") {
    return [{
      ...location,
      kind: "literal-null-body",
      classification: "risk",
      severity: "high",
      reason: "body text is the literal string null",
      snippet: escapeSnippet(body, 0),
      lineNumber: 1,
      column: 1,
      match: "null",
    }];
  }
  if (trimmed.length === 0) {
    return [{
      ...location,
      kind: "blank-body",
      classification: "risk",
      severity: "medium",
      reason: "body is blank after trimming",
      snippet: "",
      lineNumber: 0,
      column: 0,
      match: "",
    }];
  }
  if (trimmed.length < MIN_SAFE_BODY_SCAN_CHARS) {
    return [{
      ...location,
      kind: "short-body",
      classification: "risk",
      severity: "medium",
      reason: `body is shorter than the safe guard threshold (${MIN_SAFE_BODY_SCAN_CHARS} chars)`,
      snippet: escapeSnippet(body, 0),
      lineNumber: 1,
      column: 1,
      match: "",
    }];
  }
  return [];
}

function scanText(text: string, patterns: EscapePatternDefinition[], issueNumber: number, issueId: number, bodyKind: EscapeBodyKind, url: string, commentId?: number): EscapeMatchFinding[] {
  const findings: EscapeMatchFinding[] = [];
  const counts = new Map<string, number>();
  for (const item of patterns) {
    item.pattern.lastIndex = 0;
    const matches = text.match(item.pattern);
    counts.set(item.kind, matches === null ? 0 : matches.length);
  }
  for (const item of patterns) {
    item.pattern.lastIndex = 0;
    let match = item.pattern.exec(text);
    while (match !== null) {
      const { lineNumber, column } = lineColumnAt(text, match.index);
      const explanatory = item.kind === "literal-backslash-n" && counts.get(item.kind) === 1 && isExplanatoryLiteralBackslashN(text, match.index);
      const classification: EscapeFindingClassification = explanatory ? "explanatory-mention" : "suspected-pollution";
      const severity: EscapeFindingSeverity = explanatory ? "low" : item.kind === "ansi-escape-literal" ? "high" : "medium";
      findings.push({
        type: bodyKind === "issue-body" ? "issue" : "comment",
        bodyKind,
        bodyId: escapeBodyId(bodyKind, issueNumber, issueId, commentId),
        issueNumber,
        issueId,
        ...(commentId === undefined ? {} : { commentId }),
        url,
        kind: item.kind,
        classification,
        severity,
        reason: explanatory
          ? "literal backslash-n appears in explanatory context"
          : `matched ${item.description}`,
        snippet: escapeSnippet(text, match.index),
        lineNumber,
        column,
        match: match[0],
        bodyChars: text.length,
        bodyTrimmedChars: text.trim().length,
        ...(classification === "suspected-pollution"
          ? { cleanupPreview: { before: escapeSnippet(text, match.index), after: escapeSnippet(cleanupEscapedText(text), match.index) } }
          : {}),
      });
      if (findings.length >= ISSUE_SCAN_MAX_FINDINGS) return findings;
      match = item.pattern.exec(text);
    }
  }
  return findings;
}

function summarizeCleanupSuggestion(findings: EscapeScanEntry[]): EscapeCleanupSuggestion[] {
  const groups = new Map<string, EscapeMatchFinding[]>();
  for (const finding of findings) {
    if (finding.type === "comment-scan-error") continue;
    if (finding.classification !== "suspected-pollution" && finding.kind !== "short-body" && finding.kind !== "null-body" && finding.kind !== "blank-body" && finding.kind !== "literal-null-body") continue;
    const locatorKey = `${finding.bodyKind}:${finding.issueNumber}:${finding.issueId}:${finding.commentId ?? ""}`;
    const list = groups.get(locatorKey);
    if (list === undefined) groups.set(locatorKey, [finding]);
    else list.push(finding);
  }

  const suggestions: EscapeCleanupSuggestion[] = [];
  for (const group of groups.values()) {
    const first = group[0];
    const suspected = group.some((finding) => finding.classification === "suspected-pollution");
    const bodyRisk = group.some((finding) => finding.classification === "risk");
    suggestions.push({
      type: first.bodyKind,
      bodyId: first.bodyId,
      issueNumber: first.issueNumber,
      issueId: first.issueId,
      ...(first.commentId === undefined ? {} : { commentId: first.commentId }),
      url: first.url,
      classification: suspected ? "suspected-pollution" : "risk",
      action: first.bodyKind === "issue-body"
        ? (suspected ? "rewrite-issue-body-with-body-file" : "review-body-length")
        : "review-comment-manually",
      reason: suspected
        ? "suspected shell escape pollution should be rewritten from a body file"
        : bodyRisk
          ? "body length/null risk should be reviewed before any write"
          : "no cleanup needed",
      findings: group.map((finding) => ({
        kind: finding.kind,
        classification: finding.classification,
        severity: finding.severity,
        lineNumber: finding.lineNumber,
        column: finding.column,
        snippet: finding.snippet,
        match: finding.match,
      })),
      ...(first.bodyKind === "issue-body"
        ? {
          plannedCommand: `bun scripts/cli.ts gh issue update ${first.issueNumber} --mode replace --body-file <file> --dry-run`,
          bodyFileHint: "write a cleaned Markdown body to a file, then pass it with --body-file; use a quoted heredoc to stage the file instead of inline shell text",
          preview: {
            before: first.cleanupPreview?.before ?? first.snippet,
            after: first.cleanupPreview?.after ?? first.snippet,
          },
        }
        : {
          preview: {
            before: first.cleanupPreview?.before ?? first.snippet,
            after: first.cleanupPreview?.after ?? first.snippet,
          },
        }),
    });
  }
  return suggestions;
}

async function issueScanEscape(repo: string, token: string, limit: number, dryRun: boolean, commandName = "issue scan-escape"): Promise<GitHubCommandResult> {
  const issues = await listIssues(token, repo, "all", limit);
  if (isGitHubError(issues)) return commandError(commandName, repo, issues);
  const issueOnly = issues.items;

  const patterns = textFindingPatterns();
  const findings: EscapeScanEntry[] = [];
  let scannedComments = 0;
  for (const issue of issueOnly) {
    findings.push(...bodyRiskFindings("issue-body", issue.number, issue.id, issue.body, issue.html_url));
    findings.push(...scanText(issue.body ?? "", patterns, issue.number, issue.id, "issue-body", issue.html_url));
    const comments = await listIssueComments(token, repo, issue.number);
    if (isGitHubError(comments)) {
      findings.push({
        type: "comment-scan-error",
        bodyKind: "comment-body",
        bodyId: escapeBodyId("comment-body", issue.number, issue.id),
        issueNumber: issue.number,
        issueId: issue.id,
        url: issue.html_url,
        degradedReason: comments.degradedReason,
        runnerDisposition: comments.runnerDisposition ?? runnerDisposition(comments.degradedReason),
        details: comments,
      });
      continue;
    }
    for (const comment of comments) {
      scannedComments += 1;
      findings.push(...bodyRiskFindings("comment-body", issue.number, issue.id, comment.body, comment.html_url, comment.id));
      findings.push(...scanText(comment.body ?? "", patterns, issue.number, issue.id, "comment-body", comment.html_url, comment.id));
    }
  }
  const cleanupSuggestions = summarizeCleanupSuggestion(findings);
  const summary = findings.reduce((acc, finding) => {
    if (finding.type === "comment-scan-error") {
      acc.commentScanErrors += 1;
      return acc;
    }
    if (finding.classification === "suspected-pollution") acc.suspectedPollution += 1;
    if (finding.classification === "explanatory-mention") acc.explanatoryMention += 1;
    if (finding.classification === "risk") acc.risk += 1;
    if (finding.kind === "null-body" || finding.kind === "literal-null-body" || finding.kind === "blank-body" || finding.kind === "short-body") acc.bodyRisks += 1;
    return acc;
  }, { suspectedPollution: 0, explanatoryMention: 0, risk: 0, bodyRisks: 0, commentScanErrors: 0 });
  return {
    ok: true,
    command: commandName,
    repo,
    dryRun,
    planned: true,
    scannedIssues: issueOnly.length,
    rawIssues: issues.rawCount,
    pagination: issueListPaginationSummary(issues),
    scannedComments,
    findings,
    cleanupSuggestions,
    summary: {
      findings: findings.length,
      suggestions: cleanupSuggestions.length,
      ...summary,
    },
    note: dryRun
      ? "Dry-run cleanup planning only; no issue or comment content was modified."
      : "Read-only scan; no issue or comment content was modified.",
  };
}

async function authStatus(repo: string): Promise<GitHubCommandResult> {
  const ghPath = ghBinaryPath();
  const { token, probe } = resolveToken(ghPath !== null);
  const degraded: GitHubDegradedReason[] = [];
  if (ghPath === null) degraded.push("missing-binary");
  if (!probe.present || token === null) {
    if (ghPath === null) {
      return {
        ok: false,
        command: "auth status",
        repo,
        degradedReason: "missing-binary",
        degraded,
        runnerDisposition: runnerDisposition("missing-binary"),
        gh: { binaryFound: false, path: null },
        token: probe,
        probes: { restApi: "skipped", repo: "skipped", issueRead: "skipped" },
      };
    }
    degraded.push("missing-token");
    return commandError("auth status", repo, errorPayload("missing-token", "GH_TOKEN or GITHUB_TOKEN is required", { details: probe }), {
      degraded,
      gh: { binaryFound: ghPath !== null, path: ghPath },
      token: probe,
      probes: { restApi: "skipped", repo: "skipped", issueRead: "skipped" },
    });
  }

  const { owner, name } = repoParts(repo);
  const api = await githubRequest<Record<string, unknown>>(token, "GET", "/rate_limit");
  if (isGitHubError(api)) {
    return commandError("auth status", repo, api, {
      degraded: [...degraded, api.degradedReason],
      gh: { binaryFound: ghPath !== null, path: ghPath },
      token: probe,
      probes: { restApi: api, repo: "skipped", issueRead: "skipped" },
    });
  }

  const repoProbe = await githubRequest<{ full_name?: string; private?: boolean }>(token, "GET", `/repos/${owner}/${name}`);
  if (isGitHubError(repoProbe)) {
    return commandError("auth status", repo, repoProbe, {
      degraded: [...degraded, repoProbe.degradedReason],
      gh: { binaryFound: ghPath !== null, path: ghPath },
      token: probe,
      probes: { restApi: "ok", repo: repoProbe, issueRead: "skipped" },
    });
  }

  const issueProbe = await githubRequest<GitHubIssue[]>(token, "GET", `/repos/${owner}/${name}/issues?per_page=1&state=all`);
  if (isGitHubError(issueProbe)) {
    return commandError("auth status", repo, issueProbe, {
      degraded: [...degraded, issueProbe.degradedReason],
      gh: { binaryFound: ghPath !== null, path: ghPath },
      token: probe,
      probes: { restApi: "ok", repo: { ok: true, fullName: repoProbe.full_name ?? repo, private: repoProbe.private ?? null }, issueRead: issueProbe },
    });
  }

  return {
    ok: true,
    command: "auth status",
    repo,
    degraded,
    gh: { binaryFound: ghPath !== null, path: ghPath },
    token: probe,
    probes: {
      restApi: "ok",
      repo: { ok: true, fullName: repoProbe.full_name ?? repo, private: repoProbe.private ?? null },
      issueRead: { ok: true, readable: true, sampleCount: issueProbe.length },
    },
    restFallback: true,
  };
}

async function prList(repo: string, token: string, state: PrListState, limit: number, jsonFields: PrListJsonField[] | undefined, noDump = false): Promise<GitHubCommandResult> {
  const { owner, name } = repoParts(repo);
  const prs = await githubRequest<GitHubPullRequest[]>(token, "GET", `/repos/${owner}/${name}/pulls?state=${state}&per_page=${limit}`);
  if (isGitHubError(prs)) return commandError("pr list", repo, prs, { state, limit });
  const fields = jsonFields ?? PR_LIST_JSON_FIELDS.slice();
  return {
    ok: true,
    command: "pr list",
    repo,
    ...(noDump ? { noDump: true } : {}),
    state,
    limit,
    count: prs.length,
    jsonFields: fields,
    pullRequests: prs.map((pr) => selectedPrJson(prSummary(pr), fields)),
  };
}

async function prFiles(repo: string, token: string, number: number, limit: number, commandName = "pr files"): Promise<GitHubCommandResult> {
  const { owner, name } = repoParts(repo);
  const boundedLimit = Math.min(limit, MAX_PR_FILES_LIMIT);
  const pr = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
  if (isGitHubError(pr)) return commandError(commandName, repo, pr, { number });
  const perPage = Math.max(1, Math.min(100, boundedLimit));
  const files: GitHubPullRequestFile[] = [];
  let page = 1;
  while (files.length < boundedLimit) {
    const remaining = boundedLimit - files.length;
    const pageSize = Math.min(perPage, remaining);
    const path = `/repos/${owner}/${name}/pulls/${number}/files?per_page=${pageSize}&page=${page}`;
    const pageFiles = await githubRequest<GitHubPullRequestFile[]>(token, "GET", path);
    if (isGitHubError(pageFiles)) return commandError(commandName, repo, pageFiles, { number, phase: "fetch-pr-files", filesReturned: files.length });
    files.push(...pageFiles);
    if (pageFiles.length < pageSize || pageFiles.length === 0) break;
    page += 1;
  }
  const totalFiles = numberOrNull(pr.changed_files);
  const listedStats = sumPrFileStats(files);
  const totalAdditions = numberOrNull(pr.additions);
  const totalDeletions = numberOrNull(pr.deletions);
  const fullStatsAvailable = totalAdditions !== null && totalDeletions !== null;
  const totalChanges = fullStatsAvailable ? totalAdditions + totalDeletions : listedStats.changes;
  const truncated = totalFiles !== null ? files.length < totalFiles : files.length >= boundedLimit;
  const nextLimit = totalFiles === null ? MAX_PR_FILES_LIMIT : Math.min(totalFiles, MAX_PR_FILES_LIMIT);
  const nextCommand = truncated
    ? `bun scripts/cli.ts gh pr files ${number} --repo ${repo} --limit ${nextLimit}`
    : `bun scripts/cli.ts gh pr view ${number} --repo ${repo} --json body,title,state,head,base`;
  return {
    ok: true,
    command: commandName,
    repo,
    readOnly: true,
    rawDiffIncluded: false,
    pullRequest: prCompactSummary(pr),
    summary: {
      files: totalFiles ?? files.length,
      additions: totalAdditions ?? listedStats.additions,
      deletions: totalDeletions ?? listedStats.deletions,
      changes: totalChanges,
      commits: numberOrNull(pr.commits),
      source: fullStatsAvailable && totalFiles !== null ? "pull-request-rest" : "listed-files-rest",
    },
    files: files.map(prFileSummary),
    filesReturned: files.length,
    limit: boundedLimit,
    truncation: {
      truncated,
      requestedLimit: limit,
      appliedLimit: boundedLimit,
      returned: files.length,
      totalFiles,
      maxLimit: MAX_PR_FILES_LIMIT,
    },
    next: {
      command: nextCommand,
      purpose: truncated ? "Fetch a larger bounded file summary page." : "Fetch full PR metadata/body; raw diffs remain intentionally excluded.",
    },
    request: {
      method: "GET",
      paths: [
        `/repos/${owner}/${name}/pulls/${number}`,
        `/repos/${owner}/${name}/pulls/${number}/files`,
      ],
    },
  };
}

async function prRead(repo: string, token: string, number: number, jsonFields: PrReadJsonField[] | undefined, commandName = "pr read", disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
  const { owner, name } = repoParts(repo);
  const pr = await githubRequest<GitHubPullRequest>(token, "GET", `/repos/${owner}/${name}/pulls/${number}`);
  if (isGitHubError(pr)) return commandError(commandName, repo, pr, { number });
  const summary = prSummary(pr);
  const metadata = needsPrGraphqlMetadata(jsonFields) ? await prGraphqlMetadata(repo, token, number) : null;
  if (isGitHubError(metadata)) return commandError(commandName, repo, metadata, { number, phase: "fetch-pr-closeout-metadata", pullRequest: summary, requestedJsonFields: jsonFields ?? [], closeoutMetadata: prCloseoutMetadataError(metadata) });
  const selectionSummary = metadata === null ? summary : { ...summary, ...prMetadataSummary(metadata) };
  return {
    ok: true,
    command: commandName,
    repo,
    ...(disclosure === null ? {} : { disclosure }),
    pullRequest: summary,
    ...(metadata === null ? {} : { closeoutMetadata: prCloseoutMetadata(metadata) }),
    ...(jsonFields === undefined ? {} : { jsonFields, json: selectedPrJson(selectionSummary, jsonFields) }),
  };
}

async function prView(repo: string, token: string, number: number, jsonFields: PrReadJsonField[] | undefined, disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
  return prRead(repo, token, number, jsonFields, "pr view", disclosure);
}

export function ghHelp(): unknown {
  return {
    command: "gh",
    output: "json",
    usage: [
      "bun scripts/cli.ts gh auth status [--repo owner/name]",
      "bun scripts/cli.ts gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--search text] [--label label[,label...]]... [--repo owner/name] [--json number,title,state,closed,closedAt,url,updatedAt,createdAt,author,labels] [--raw|--full]",
      "bun scripts/cli.ts gh issue view <number|url|owner/repo#number> [--repo owner/name] [--number N compat] [--json body,title,state,closed,closedAt,comments] [--raw|--full]",
      "bun scripts/cli.ts gh issue read <number|url|owner/repo#number> [--repo owner/name] [--number N compat] [--raw|--full] [compatibility alias for issue view]",
      "bun scripts/cli.ts gh issue create --title <title> --body-file <file|-> [--label label[,label...]]... [--repo owner/name] [--dry-run]",
      "bun scripts/cli.ts gh issue update <number> --mode replace|append --body-file <file|-> [--title title] [--repo owner/name] [--number N compat] [--dry-run] [--expect-updated-at ts|--expect-body-sha sha256] [--body-profile auto|code-queue-board|commander-brief] [--allow-short-body] [--full|--raw]",
      "bun scripts/cli.ts gh issue edit <number> --body-file <file|-> [--repo owner/name] [--number N compat] [--full|--raw] [compat alias for issue update --mode replace]",
      "bun scripts/cli.ts gh issue edit 24 --body-file <file> --notify-claudeqq-brief-diff [--dry-run]",
      "bun scripts/cli.ts gh issue comment create <number> --body-file <file|->|--body <short-text> [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh issue comment delete <commentId> [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh issue close|reopen <number> [--repo owner/name] [--number N compat] [--comment <short-text>|--comment-file <file|->] [--dry-run]",
      "bun scripts/cli.ts gh issue stale-close [--repo owner/name] [--inactive-hours N] [--limit N] [--label label[,label...]]... [--dry-run]",
      "bun scripts/cli.ts gh issue delete <number> [unsupported: use close]",
      "bun scripts/cli.ts gh issue scan-escape [--repo owner/name] [--limit N] [--dry-run]",
      "bun scripts/cli.ts gh issue cleanup-plan [--repo owner/name] [--limit N] [--dry-run]",
      "bun scripts/cli.ts gh issue board-audit [--repo owner/name] [--board-issue 20] [--limit N] [--known-meta-issue N[,N...]] [--ignore-issue N[,N...]] [--dry-run]",
      "bun scripts/cli.ts gh issue board-row list [--repo owner/name] --board-issue 20 [--state open|closed|all] [--dry-run]",
      "bun scripts/cli.ts gh issue board-row get <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20",
      "bun scripts/cli.ts gh issue board-row update <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20 --field progress|status|validation|branch|tasks|focus --value <text> [--dry-run] [--expect-updated-at ts|--expect-body-sha sha256]",
      "bun scripts/cli.ts gh issue board-row add <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20 --section open|closed --row-file <markdown-row-file> [--dry-run] [--expect-body-sha sha256]",
      "bun scripts/cli.ts gh issue board-row upsert <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20 --section open|closed [--category text] --branch <branch> --tasks <task> --summary <text> --focus <text> --validation <text> --progress <text> [--status OPEN|CLOSED] [--dry-run] [--expect-updated-at ts|--expect-body-sha sha256]",
      "bun scripts/cli.ts gh issue board-row move <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20 --to open|closed [--status OPEN|CLOSED] [--dry-run] [--expect-body-sha sha256]",
      "bun scripts/cli.ts gh issue board-row delete <issueNumber> [--repo owner/name] [--number N compat] --board-issue 20 [--dry-run] [--expect-body-sha sha256]",
      "bun scripts/cli.ts gh preflight <prNumber|owner/repo#number> [--repo owner/name] [--number N compat] [--full|--raw] [compatibility alias for gh pr preflight]",
      "bun scripts/cli.ts gh pr list [owner/repo] [--repo owner/name] [--state open|closed|all] [--limit N] [--json number,title,state,url,updatedAt,createdAt,author,head,base,draft]",
      "bun scripts/cli.ts gh pr files <number> [--repo owner/name] [--number N compat] [--limit N] [number may appear before or after options]",
      "bun scripts/cli.ts gh pr diff <number> --stat [--repo owner/name] [--number N compat] [--limit N] [number may appear before or after options; compatibility alias for pr files; no raw diff]",
      "bun scripts/cli.ts gh pr view <number|url|owner/repo#number> [--repo owner/name] [--number N compat] [--json body,title,state,stateDetail,closed,closedAt,merged,mergedAt,mergeCommit,head,base,draft,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup] [--raw|--full]",
      "bun scripts/cli.ts gh pr read <number|url|owner/repo#number> [--repo owner/name] [--number N compat] [--raw|--full] [compatibility alias for pr view]",
      "bun scripts/cli.ts gh pr preflight <number|owner/repo#number> [--repo owner/name] [--number N compat] [--full|--raw] [number may appear before or after options]",
      "bun scripts/cli.ts gh pr closeout <number|owner/repo#number> [--repo owner/name] [--number N compat] [--full|--raw] [number may appear before or after options; compatibility alias for pr preflight]",
      "bun scripts/cli.ts gh pr create --title <title> --body-file <file>|--body <text> --base <branch> --head <branch> [--repo owner/name] [--draft] [--dry-run]",
      "bun scripts/cli.ts gh pr edit <number> [--title title] [--body-file <file>|--body-file -|--body <text>] [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh pr update <number> --mode replace|append [--body-file <file>|--body-file -|--body <text>] [--title title] [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh pr comment create <number> --body-file <file>|--body <text> [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh pr comment delete <commentId> [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh pr close|reopen <number> [--repo owner/name] [--number N compat] [--dry-run]",
      "bun scripts/cli.ts gh pr merge <number> [--repo owner/name] [--number N compat] [--merge|--squash|--rebase] [--delete-branch] [--dry-run]",
      "bun scripts/cli.ts gh pr delete <number> [unsupported: use close]",
    ],
    defaults: { repo: DEFAULT_REPO },
    notes: [
      "Issue and PR create/read/update/comment/close/reopen use GitHub REST and do not require the gh binary when GH_TOKEN or GITHUB_TOKEN is present.",
      "Token values are never printed; auth status reports only token source and presence.",
      "issue list and pr list accept a single positional owner/repo as a compatibility alias for --repo owner/name. The positional repo and --repo must match if both are supplied; non-repo positionals fail structurally instead of falling back to the default repo.",
      "issue list defaults to --state open and bounded --limit 30; it paginates GitHub REST/Search pages internally when --limit exceeds GitHub's per-page cap and discloses pagination/rawCount/hasMore so operators do not mistake a single page for the full repository. --search uses GitHub Search Issues API with repo/type/state qualifiers for low-friction dedupe lookup before creating a new issue. Supported --json fields are number,title,state,closed,closedAt,url,updatedAt,createdAt,author,labels and unknown fields fail structurally.",
      "PR list defaults to --state all for compatibility with earlier UniDesk CLI behavior; supported states are open, closed, and all.",
      "issue view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. View/read accept positional numbers, GitHub issue URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single issue/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create/scan-escape/cleanup-plan/board-audit/board-row list do not accept it. Comment delete treats --number as commentId, not an issue number. View supports lifecycle fields closed/closedAt plus legacy --json field selection such as --json body and still exposes .data.issue.body for compatibility; unsupported fields fail structurally.",
      "--raw and --full are explicit full-disclosure aliases for gh issue list/read/view/update/edit and gh pr list/read/view. For issue writes, default success output omits full issue.body and returns bodyChars/bodySha/bodyPreview plus readCommands; --full|--raw includes the full returned issue body only on commands that explicitly support full disclosure.",
      "GitHub CLI output larger than 20 KiB is automatically written to /tmp/unidesk-cli-output/*.json; stdout stays bounded JSON with outputTruncated=true, the dump path, total bytes/lines, and head/tail previews.",
      "issue create accepts --body-file <file|-> plus repeatable --label values and comma-separated labels; inline --body is intentionally unsupported for issue creation. Dry-run prints the parsed labels and non-dry-run sends them in the GitHub REST create-issue payload.",
      "--body-file is the recommended source for Markdown bodies so real newlines, backticks, and tables are read as file bytes instead of shell arguments.",
      "update defaults to --mode replace; --mode append reads the current body and appends file bytes so real newlines, backticks, and Markdown tables are preserved.",
      "issue edit is a compatibility alias for issue update --mode replace.",
      "issue update --body-file accepts files or - for stdin, refuses literal null, blank, and too-short bodies by default. Use --allow-short-body only for intentional short writes; #20 requires its board heading, warns when HWLAB product/user issue routing appears in favor of pikasTech/HWLAB, and still rejects commander brief update sections; commander-brief requires its stable heading on legacy #24 plus daily rolling brief issues titled YYYY-MM-DD 指挥简报（北京时间）.",
      "issue update dry-run reports bounded bodyPreview/bodyPreviewLines, old/new body length slots, body SHA, required heading checks, literal \\n detection, shell-pollution signals, guard/concurrency summary, wouldPatch, and readCommands without printing an unbounded full body. Non-dry-run automatically reads current issue metadata before PATCH and returns oldBodySha/updatedAt; --expect-updated-at or --expect-body-sha remain available for explicit stale-cache protection.",
      "issue comment create accepts --body-file <file|-> for Markdown/generated content and --body only for short single-line text. Blank, multiline, shell-polluted, secret-like, and overlong inline bodies fail structurally.",
      "issue close/reopen default success output is compact and omits full issue.body. Optional --comment <short-text> or --comment-file <file|-> posts a bounded lifecycle comment before the state change and aborts the state change if the comment POST fails. --comment-file is the recommended path for generated Markdown closeout evidence; --comment remains the short inline form. Use gh issue view <number> --json body or --full/--raw on view when full text is needed.",
      "issue stale-close is the reusable lifecycle cleanup path for policies such as closing open issues inactive for more than 48 hours. It selects open issues by GitHub updatedAt older than observedAt - --inactive-hours, treats comments and state changes as activity, filters pull requests, supports --dry-run, and returns bounded candidate/closed/failure summaries without echoing full bodies.",
      "For one-shot issue writes, pipe reviewed Markdown through stdin: cat body.md | bun scripts/cli.ts gh issue update <number> --repo owner/name --body-file - or gh issue comment create <number> --body-file -. When staging a body file from a shell, use a quoted heredoc such as cat <<'EOF' > /tmp/body.md so backticks and backslashes are not expanded before --body-file reads the file.",
      "For JSON request bodies in other CLI namespaces, prefer --body-file or --body-stdin over long inline shell arguments. GitHub issue/PR Markdown writes use --body-file <file|-> for long or multiline content.",
      "issue scan-escape classifies literal \\n findings as suspected-pollution, explanatory-mention, or risk, and emits cleanupSuggestions with body/comment ids plus diff-like previews. cleanup-plan is an alias that remains dry-run/read-only.",
      "issue board-audit is read-only and defaults to repo pikasTech/unidesk plus board issue #20. It reads only the board issue body, returns body size/SHA and parsed Markdown board sections, and no longer validates GitHub open/closed issue coverage against OPEN/CLOSED tables. The legacy coverage fields remain present as empty arrays/zero counts for compatibility.",
      "issue board-row list/get reuse the board-audit table parser and are read-only. board-row update changes one table cell by issue number, returns old/new row, body SHA, body guard and request plan, and defaults to dry-run unless --expect-updated-at or --expect-body-sha is supplied for the guarded PATCH. Field aliases map status and validation to the 验收状态 column, tasks to 相关 Code Queue 任务, and focus to 当前关注点.",
      "issue board-row upsert updates an existing row when the issue is already present, or generates a complete row in --section open|closed when missing. It returns operation=update or operation=add, defaults to dry-run, requires --expect-body-sha or --expect-updated-at before PATCH, and refuses section migration; use board-row move for OPEN/CLOSED migration.",
      "issue board-row add/move/delete are row-scoped #20 table mutations. add validates a one-line --row-file against the target table column count, Issue column, and GitHub 状态 column; move refuses duplicate/ambiguous rows and can update GitHub 状态 via --status; delete removes only the matched row. All three default to dry-run and require --expect-body-sha or --expect-updated-at before PATCH. add/move/delete return old/new row, body SHA, and line/section plan details for the parsed table mutation, and the shared #20 guard warns about HWLAB product/user issue routing in favor of pikasTech/HWLAB without refusing the write.",
      "issue edit 24 --notify-claudeqq-brief-diff remains the legacy #24 notification helper: it reads the old issue body, PATCHes the new body, and sends only newly added '## 更新 ... 北京时间' sections to ClaudeQQ; ClaudeQQ failure does not roll back GitHub.",
      "Commander brief ClaudeQQ defaults to private target 645275593 through backend-core /api/microservices/claudeqq/proxy; UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_* env vars can override target, base URL, timeout, and enabled state.",
      "comment delete is supported because GitHub supports deleting issue comments; issue/pr hard delete is unsupported and close is the lifecycle alternative.",
      "PR files is the canonical compact changed-file/stat summary. It uses GitHub REST, returns bounded file rows, additions/deletions/changes when available, truncation metadata, and a next command for full details. Raw diff patches are not emitted by default; gh pr diff <number> --stat is a compatibility alias for the same JSON summary.",
      "PR edit/update PATCHes /repos/{owner}/{repo}/pulls/{number} through REST only, never GitHub Projects Classic GraphQL/projectCards, and returns low-noise JSON with repo, PR number, changedFields, url, and body size/SHA metadata instead of echoing the full body.",
      "PR view is the canonical GitHub CLI-compatible read path; read remains a UniDesk compatibility alias. PR view/read accept positional numbers, GitHub PR URLs, and owner/repo#number shorthand, deriving --repo unless an explicit conflicting --repo is supplied. --number is accepted on single PR/comment numeric target commands for low-friction compatibility and returns a standard syntax hint; list/create do not accept it. PR comment delete treats --number as commentId, not a PR number. PR view/read supports REST closeout fields stateDetail, closed, closedAt, merged, mergedAt, mergeCommit, headRefName, and baseRefName; mergeable, mergeStateStatus, and statusCheckRollup are fetched through GitHub GraphQL only when requested or when --raw/--full requests full disclosure, and closeoutMetadata makes GraphQL errors plus UNKNOWN/null metadata explicit.",
      "PR preflight/closeout accept the same owner/repo#number shorthand as PR view/read so merge readiness checks do not require repeating --repo after a PR URL has already been normalized.",
      "PR list does not fetch mergeability or statusCheckRollup; request those closeout fields with gh pr view <number> --json headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup.",
      "PR preflight is a low-noise read-only closeout helper. It combines redacted auth capability, PR branch/state metadata, mergeability, mergeStateStatus, compact status check counts, and an explicit read-only policy. Use --full or --raw to include all fetched status contexts; gh pr merge is the separate guarded write path.",
      "PR merge is a guarded write operation: it first reads closeout metadata, refuses non-open/draft/conflicting/non-clean/failed/pending PRs, then uses GitHub REST merge. Use --dry-run to see the exact merge plan without writing.",
    ],
  };
}

export async function runGhCommand(args: string[]): Promise<GitHubCommandResult | unknown> {
  const [top, sub, third] = args;
  if (top === undefined || top === "help" || top === "--help" || top === "-h") return ghHelp();
  let options: GitHubOptions;
  try {
    options = parseOptions(args);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    const repo = optionValue(args, "--repo") ?? DEFAULT_REPO;
    const unknownOption = /^unknown gh option:\s+(.+)$/u.exec(message)?.[1];
    return unknownOption !== undefined
      ? unsupportedCommand(command, repo, message, unknownGhOptionDetails(args, unknownOption))
      : validationError(command, repo, message);
  }
  if (options.notifyClaudeQqBriefDiff && !(top === "issue" && sub === "edit")) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--notify-claudeqq-brief-diff is only supported by gh issue edit 24");
  }
  if (optionWasProvided(args, "--state") && !(top === "issue" && (sub === "list" || sub === "board-row" && third === "list") || top === "pr" && sub === "list")) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--state is only supported by gh issue list, gh issue board-row list, and gh pr list");
  }
  if (optionWasProvided(args, "--json") && !(top === "issue" && (isIssueReadCommand(sub) || sub === "list"))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    if (!(top === "pr" && (isPrReadCommand(sub) || sub === "list"))) {
      return validationError(command, options.repo, "--json field selection is only supported by gh issue read/view/list and gh pr read/view/list");
    }
  }
  if ((optionWasProvided(args, "--raw") || optionWasProvided(args, "--full")) && !((top === "issue" && (isIssueReadCommand(sub) || sub === "list" || sub === "update" || sub === "edit")) || top === "preflight" || (top === "pr" && (isPrReadCommand(sub) || sub === "list" || sub === "preflight" || sub === "closeout")))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--raw and --full are explicit full-disclosure aliases only for gh issue list/read/view/update/edit, gh pr list/read/view, and gh pr preflight/closeout.", {
      supportedCommands: [
        "bun scripts/cli.ts gh issue list --repo owner/name --limit 200 --full",
        "bun scripts/cli.ts gh issue view owner/name#<number> --raw",
        "bun scripts/cli.ts gh issue view <number> --repo owner/name --json body,title,state,comments",
        "cat body.md | bun scripts/cli.ts gh issue update <number> --repo owner/name --body-file - --full",
        "bun scripts/cli.ts gh pr list --repo owner/name --limit 100 --full",
        "bun scripts/cli.ts gh pr view owner/name#<number> --raw",
        `bun scripts/cli.ts gh pr view <number> --repo owner/name --json ${readViewSupportedJsonFields("pr")}`,
        "bun scripts/cli.ts gh pr preflight <number> --repo owner/name --full",
      ],
    });
  }
  if (optionWasProvided(args, "--stat") && !(top === "pr" && (sub === "diff" || sub === "files"))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--stat is only supported by gh pr diff <number> --stat.", {
      supportedCommands: [
        "bun scripts/cli.ts gh pr files <number> --repo owner/name --limit 30",
        "bun scripts/cli.ts gh pr diff <number> --stat --repo owner/name --limit 30",
      ],
    });
  }
  if (optionWasProvided(args, "--number") && !allowsNumberTargetAlias(top, sub, third)) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    const standardViewCommand = top === "issue" || top === "pr" ? `gh ${top} view` : "gh issue/pr view";
    return validationError(command, options.repo, `--number is only a compatibility alias for single numeric target commands; standard ${standardViewCommand} uses a positional number or URL target.`, {
      supportedCommands: [
        "bun scripts/cli.ts gh issue view <number> --repo owner/name --json body,title,state",
        "bun scripts/cli.ts gh issue view https://github.com/owner/name/issues/<number> --raw",
        "bun scripts/cli.ts gh pr view <number> --repo owner/name --json body,title,state,head,base",
        "bun scripts/cli.ts gh pr view https://github.com/owner/name/pull/<number> --raw",
      ],
      rejectedOption: "--number",
    });
  }
  if (optionWasProvided(args, "--mode") && !((top === "issue" && (sub === "update" || sub === "edit")) || (top === "pr" && (sub === "update" || sub === "edit")))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--mode is only supported by gh issue update/edit and gh pr update/edit");
  }
  if ((options.allowShortBody || options.expectUpdatedAt !== undefined || options.expectBodySha !== undefined || optionWasProvided(args, "--body-profile")) && !(top === "issue" && (sub === "edit" || sub === "update" || sub === "board-row" && ["update", "add", "move", "delete", "upsert"].includes(third ?? "")))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--allow-short-body, --expect-updated-at, --expect-body-sha, and --body-profile are only supported by gh issue update/edit and gh issue board-row update/add/move/delete/upsert");
  }
  if ((optionWasProvided(args, "--board-issue") || optionWasProvided(args, "--known-meta-issue") || optionWasProvided(args, "--ignore-issue")) && !(top === "issue" && (sub === "board-audit" || sub === "board-row"))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--board-issue, --known-meta-issue, and --ignore-issue are only supported by gh issue board-audit and --board-issue by gh issue board-row");
  }
  if ((optionWasProvided(args, "--known-meta-issue") || optionWasProvided(args, "--ignore-issue")) && !(top === "issue" && sub === "board-audit")) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--known-meta-issue and --ignore-issue are only supported by gh issue board-audit");
  }
  if ((optionWasProvided(args, "--field") || optionWasProvided(args, "--value")) && !(top === "issue" && sub === "board-row" && third === "update")) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--field and --value are only supported by gh issue board-row update");
  }
  if (optionWasProvided(args, "--row-file") && !(top === "issue" && sub === "board-row" && third === "add")) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--row-file is only supported by gh issue board-row add");
  }
  if (optionWasProvided(args, "--section") && !(top === "issue" && sub === "board-row" && (third === "add" || third === "upsert"))) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--section is only supported by gh issue board-row add/upsert");
  }
  if (optionWasProvided(args, "--to") && !(top === "issue" && sub === "board-row" && third === "move")) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--to is only supported by gh issue board-row move");
  }
  if (optionWasProvided(args, "--status") && !(top === "issue" && sub === "board-row" && (third === "move" || third === "upsert"))) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--status is only supported by gh issue board-row move/upsert");
  }
  if ((optionWasProvided(args, "--category") || optionWasProvided(args, "--branch") || optionWasProvided(args, "--tasks") || optionWasProvided(args, "--summary") || optionWasProvided(args, "--focus") || optionWasProvided(args, "--validation") || optionWasProvided(args, "--progress")) && !(top === "issue" && sub === "board-row" && third === "upsert")) {
    const command = [top, sub, third].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--category, --branch, --tasks, --summary, --focus, --validation, and --progress are only supported by gh issue board-row upsert");
  }
  if (optionWasProvided(args, "--label") && !(top === "issue" && (sub === "create" || sub === "list" || sub === "stale-close"))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--label is only supported by gh issue create, gh issue list, and gh issue stale-close");
  }
  if (optionWasProvided(args, "--search") && !(top === "issue" && sub === "list")) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--search is only supported by gh issue list");
  }
  if (optionWasProvided(args, "--inactive-hours") && !(top === "issue" && sub === "stale-close")) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--inactive-hours is only supported by gh issue stale-close");
  }
  if (optionWasProvided(args, "--comment") && !(top === "issue" && (sub === "close" || sub === "reopen"))) {
    const command = [top, sub].filter((value): value is string => value !== undefined).join(" ") || "gh";
    return validationError(command, options.repo, "--comment/--comment-file is only supported by gh issue close/reopen; use gh issue comment create for standalone comments");
  }

  if (top === "auth" && sub === "status") return authStatus(options.repo);

  if (top === "preflight") {
    const resolved = resolvePositionalPrReference(args, 1, "preflight", options);
    if (isGitHubCommandResult(resolved)) return resolved;
    return withNumberOptionHint(prPreflight(resolved.repo, resolved.number, "preflight", options.full || options.raw), resolved);
  }

  if (top === "issue") {
    if (sub === "delete") return unsupportedCommand("issue delete", options.repo, "GitHub REST does not support hard-deleting issues; use gh issue close for lifecycle deletion semantics.");
    if (sub === "comment" && third === "delete") {
      const resolved = resolvePositionalNumberReference("issue", args, 3, "issue comment delete", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      const commentId = resolved.number;
      if (typeof commentId !== "number") return commentId;
      if (options.dryRun) return withNumberOptionHint(commentDelete(resolved.repo, "", "issue", commentId, true), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "issue comment delete", probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, "issue comment delete", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(commentDelete(resolved.repo, token, "issue", commentId, false), resolved);
    }
    if (sub === "comment" && third === "create") {
      const resolved = resolvePositionalIssueReference(args, 3, "issue comment create", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      const issueNumber = resolved.number;
      if (typeof issueNumber !== "number") return issueNumber;
      if (options.dryRun) return withNumberOptionHint(issueComment(resolved.repo, "", issueNumber, { ...options, repo: resolved.repo }), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "issue comment create", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "issue comment create", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(issueComment(resolved.repo, token, issueNumber, { ...options, repo: resolved.repo }), resolved);
    }
    if (sub === "scan-escape" || sub === "cleanup-plan") {
      const { token, probe } = resolveToken(true);
      const commandName = sub === "cleanup-plan" ? "issue cleanup-plan" : "issue scan-escape";
      const missing = authRequired(options.repo, commandName, probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, commandName, { present: false, source: null, ghFallbackAttempted: true });
      return issueScanEscape(options.repo, token, options.limit, options.dryRun || sub === "cleanup-plan", commandName);
    }
    if (sub === "board-audit") {
      const { token, probe } = resolveToken(true);
      const missing = authRequired(options.repo, "issue board-audit", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "issue board-audit", { present: false, source: null, ghFallbackAttempted: true });
      return issueBoardAudit(options.repo, token, options);
    }
    if (sub === "board-row") {
      const action = third;
      const commandName = `issue board-row ${action ?? ""}`.trim();
      if (action !== "list" && action !== "get" && action !== "update" && action !== "add" && action !== "move" && action !== "delete" && action !== "upsert") {
        return unsupportedCommand(commandName, options.repo, "board-row supported commands are list, get, update, add, move, delete, and upsert.");
      }
      const { token, probe } = resolveToken(true);
      const missing = authRequired(options.repo, commandName, probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, commandName, { present: false, source: null, ghFallbackAttempted: true });
      if (action === "list") return issueBoardRowList(options.repo, token, options);
      const resolvedBoardRow = resolvePositionalIssueReference(args, 3, commandName, options);
      if (isGitHubCommandResult(resolvedBoardRow)) return resolvedBoardRow;
      const issueNumber = resolvedBoardRow.number;
      if (typeof issueNumber !== "number") return issueNumber;
      const boardRowOptions = { ...options, repo: resolvedBoardRow.repo };
      if (action === "get") return withNumberOptionHint(issueBoardRowGet(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
      if (action === "update") return withNumberOptionHint(issueBoardRowUpdate(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
      if (action === "add") return withNumberOptionHint(issueBoardRowAdd(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
      if (action === "upsert") return withNumberOptionHint(issueBoardRowUpsert(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
      if (action === "move") return withNumberOptionHint(issueBoardRowMove(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
      return withNumberOptionHint(issueBoardRowDelete(resolvedBoardRow.repo, token, issueNumber, boardRowOptions), resolvedBoardRow);
    }
    if (options.dryRun) {
      if (sub === "create") return issueCreate(options.repo, "", options);
      if (sub === "edit") {
        const issueEditRef = resolvePositionalIssueReference(args, 2, "issue edit", options);
        if (isGitHubCommandResult(issueEditRef)) return issueEditRef;
        const issueNumber = issueEditRef.number;
        const { token } = resolveToken(false);
        return withNumberOptionHint(issueEdit(issueEditRef.repo, token ?? "", issueNumber, { ...options, repo: issueEditRef.repo }), issueEditRef);
      }
      if (sub === "update") {
        const issueUpdateRef = resolvePositionalIssueReference(args, 2, "issue update", options);
        if (isGitHubCommandResult(issueUpdateRef)) return issueUpdateRef;
        const issueNumber = issueUpdateRef.number;
        const { token } = resolveToken(false);
        return withNumberOptionHint(issueEdit(issueUpdateRef.repo, token ?? "", issueNumber, { ...options, repo: issueUpdateRef.repo }, "issue update"), issueUpdateRef);
      }
      if (sub === "comment") {
        const r = resolvePositionalIssueReference(args, 2, "issue comment", options);
        if (isGitHubCommandResult(r)) return r;
        return withNumberOptionHint(issueComment(r.repo, "", r.number, { ...options, repo: r.repo }), r);
      }
      if (sub === "close") {
        const r = resolvePositionalIssueReference(args, 2, "issue close", options);
        if (isGitHubCommandResult(r)) return r;
        return withNumberOptionHint(issueState(r.repo, "", r.number, "closed", true, { ...options, repo: r.repo }), r);
      }
      if (sub === "reopen") {
        const r = resolvePositionalIssueReference(args, 2, "issue reopen", options);
        if (isGitHubCommandResult(r)) return r;
        return withNumberOptionHint(issueState(r.repo, "", r.number, "open", true, { ...options, repo: r.repo }), r);
      }
    }
    if (sub === "read" || sub === "view") {
      const resolved = resolveReadViewNumberReference("issue", sub, third, options, args);
      if (isGitHubCommandResult(resolved)) return resolved;
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, `issue ${sub}`, probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, `issue ${sub}`, { present: false, source: null, ghFallbackAttempted: true });
      const disclosure = readDisclosureOptions(options, resolved.shorthand);
      if (sub === "read") return withNumberOptionHint(issueRead(resolved.repo, token, resolved.number, issueReadJsonFields(options), "issue read", disclosure), resolved);
      return withNumberOptionHint(issueView(resolved.repo, token, resolved.number, issueReadJsonFields(options), disclosure), resolved);
    }
    const { token, probe } = resolveToken(true);
    const missing = authRequired(options.repo, `issue ${sub ?? ""}`.trim(), probe);
    if (missing !== null || token === null) return missing ?? authRequired(options.repo, `issue ${sub ?? ""}`.trim(), { present: false, source: null, ghFallbackAttempted: true });

    if (sub === "list") return issueList(options.repo, token, options.listState, options.limit, options.issueListJsonFields, options.search, options.labels, options.raw || options.full);
    if (sub === "stale-close") return issueStaleClose(options.repo, token, options);
    if (sub === "create") return issueCreate(options.repo, token, options);
    if (sub === "edit") {
      const r = resolvePositionalIssueReference(args, 2, "issue edit", options);
      if (isGitHubCommandResult(r)) return r;
      return withNumberOptionHint(issueEdit(r.repo, token, r.number, { ...options, repo: r.repo }), r);
    }
    if (sub === "update") {
      const r = resolvePositionalIssueReference(args, 2, "issue update", options);
      if (isGitHubCommandResult(r)) return r;
      return withNumberOptionHint(issueEdit(r.repo, token, r.number, { ...options, repo: r.repo }, "issue update"), r);
    }
    if (sub === "comment") {
      const r = resolvePositionalIssueReference(args, 2, "issue comment", options);
      if (isGitHubCommandResult(r)) return r;
      return withNumberOptionHint(issueComment(r.repo, token, r.number, { ...options, repo: r.repo }), r);
    }
    if (sub === "close") {
      const r = resolvePositionalIssueReference(args, 2, "issue close", options);
      if (isGitHubCommandResult(r)) return r;
      return withNumberOptionHint(issueState(r.repo, token, r.number, "closed", options.dryRun, { ...options, repo: r.repo }), r);
    }
    if (sub === "reopen") {
      const r = resolvePositionalIssueReference(args, 2, "issue reopen", options);
      if (isGitHubCommandResult(r)) return r;
      return withNumberOptionHint(issueState(r.repo, token, r.number, "open", options.dryRun, { ...options, repo: r.repo }), r);
    }
  }

  if (top === "pr") {
    if (sub === "diff") {
      const resolved = resolvePositionalPrReference(args, 2, "pr diff", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      if (!optionWasProvided(args, "--stat")) {
        return unsupportedCommand("pr diff", options.repo, "Raw PR diff output is intentionally unsupported by UniDesk CLI; use gh pr diff <number> --stat or gh pr files for a bounded REST file/stat summary.", {
          rawDiffIncluded: false,
          supportedCommands: [
            `bun scripts/cli.ts gh pr files ${resolved.number} --repo ${resolved.repo} --limit 30`,
            `bun scripts/cli.ts gh pr diff ${resolved.number} --stat --repo ${resolved.repo} --limit 30`,
          ],
        });
      }
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "pr diff --stat", probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, "pr diff --stat", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prFiles(resolved.repo, token, resolved.number, options.limit, "pr diff --stat"), resolved);
    }
    if (sub === "files") {
      const resolved = resolvePositionalPrReference(args, 2, "pr files", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "pr files", probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, "pr files", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prFiles(resolved.repo, token, resolved.number, options.limit, "pr files"), resolved);
    }
    if (sub === "delete") return unsupportedCommand("pr delete", options.repo, "GitHub REST does not support hard-deleting pull requests; use gh pr close for lifecycle deletion semantics.");
    if (sub === "preflight" || sub === "closeout") {
      const resolved = resolvePositionalPrReference(args, 2, `pr ${sub}`, options);
      if (isGitHubCommandResult(resolved)) return resolved;
      return withNumberOptionHint(prPreflight(resolved.repo, resolved.number, sub === "closeout" ? "pr closeout" : "pr preflight", options.full || options.raw), resolved);
    }
    if (sub === "comment" && third === "delete") {
      const resolved = resolvePositionalNumberReference("pr", args, 3, "pr comment delete", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      if (options.dryRun) return withNumberOptionHint(commentDelete(resolved.repo, "", "pr", resolved.number, true), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "pr comment delete", probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, "pr comment delete", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(commentDelete(resolved.repo, token, "pr", resolved.number, false), resolved);
    }
    if (sub === "comment" && third === "create") {
      const resolved = resolvePositionalPrReference(args, 3, "pr comment create", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      if (options.dryRun) return withNumberOptionHint(prComment(resolved.repo, "", resolved.number, { ...options, repo: resolved.repo }), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, "pr comment create", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "pr comment create", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prComment(resolved.repo, token, resolved.number, { ...options, repo: resolved.repo }), resolved);
    }
    if (sub === "create") {
      if (options.dryRun) return prCreate(options.repo, "", options);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(options.repo, "pr create", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "pr create", { present: false, source: null, ghFallbackAttempted: true });
      return prCreate(options.repo, token, options);
    }
    if (sub === "comment") {
      if (options.dryRun) {
        const resolved = resolvePositionalPrReference(args, 2, "pr comment", options);
        if (isGitHubCommandResult(resolved)) return resolved;
        return withNumberOptionHint(prComment(resolved.repo, "", resolved.number, { ...options, repo: resolved.repo }), resolved);
      }
      const { token, probe } = resolveToken(true);
      const missing = authRequired(options.repo, "pr comment", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "pr comment", { present: false, source: null, ghFallbackAttempted: true });
      const resolved = resolvePositionalPrReference(args, 2, "pr comment", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      return withNumberOptionHint(prComment(resolved.repo, token, resolved.number, { ...options, repo: resolved.repo }), resolved);
    }
    if (sub === "update" || sub === "edit") {
      const commandName = `pr ${sub}`;
      const resolved = resolvePositionalPrReference(args, 2, commandName, options);
      if (isGitHubCommandResult(resolved)) return resolved;
      if (options.dryRun && options.mode === "replace") return withNumberOptionHint(prUpdate(resolved.repo, "", resolved.number, { ...options, repo: resolved.repo }, commandName), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, commandName, probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, commandName, { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prUpdate(resolved.repo, token, resolved.number, { ...options, repo: resolved.repo }, commandName), resolved);
    }
    if (sub === "close" || sub === "reopen") {
      const resolved = resolvePositionalPrReference(args, 2, `pr ${sub}`, options);
      if (isGitHubCommandResult(resolved)) return resolved;
      if (options.dryRun) return withNumberOptionHint(prState(resolved.repo, "", resolved.number, sub === "close" ? "closed" : "open", true), resolved);
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, `pr ${sub}`, probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, `pr ${sub}`, { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prState(resolved.repo, token, resolved.number, sub === "close" ? "closed" : "open", false), resolved);
    }
    if (sub === "merge") {
      const resolved = resolvePositionalPrReference(args, 2, "pr merge", options);
      if (isGitHubCommandResult(resolved)) return resolved;
      const { token, probe } = resolveToken(true);
      const missing = authRequired(options.repo, "pr merge", probe);
      if (missing !== null || token === null) return missing ?? authRequired(options.repo, "pr merge", { present: false, source: null, ghFallbackAttempted: true });
      return withNumberOptionHint(prMerge(resolved.repo, token, resolved.number, options), resolved);
    }
    if (sub !== "list" && !isPrReadCommand(sub)) {
      return unsupportedCommand(`pr ${sub ?? ""}`.trim(), options.repo, "PR supported commands are list, files, diff --stat, read/view, preflight/closeout, create, update/edit, close, reopen, merge, comment create/delete, and unsupported delete.");
    }
    if (sub === "read" || sub === "view") {
      const resolved = resolveReadViewNumberReference("pr", sub, third, options, args);
      if (isGitHubCommandResult(resolved)) return resolved;
      const { token, probe } = resolveToken(true);
      const missing = authRequired(resolved.repo, `pr ${sub}`, probe);
      if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, `pr ${sub}`, { present: false, source: null, ghFallbackAttempted: true });
      const disclosure = readDisclosureOptions(options, resolved.shorthand);
      if (sub === "read") return withNumberOptionHint(prRead(resolved.repo, token, resolved.number, prReadJsonFields(options), "pr read", disclosure), resolved);
      return withNumberOptionHint(prView(resolved.repo, token, resolved.number, prReadJsonFields(options), disclosure), resolved);
    }
    const { token, probe } = resolveToken(true);
    const missing = authRequired(options.repo, `pr ${sub}`, probe);
    if (missing !== null || token === null) return missing ?? authRequired(options.repo, `pr ${sub}`, { present: false, source: null, ghFallbackAttempted: true });
    if (sub === "list") return prList(options.repo, token, options.prListState, options.limit, options.prListJsonFields, options.raw || options.full);
  }

  return unsupportedCommand(args.join(" ") || "gh", options.repo, "Unsupported gh command", { help: ghHelp() });
}
