{"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../../src/slash/slash-commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAkC,MAAM,2BAA2B,CAAC;AAkB9F,OAAO,EAWN,KAAK,aAAa,EAElB,MAAM,oBAAoB,CAAC;AAqmB5B,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CA+ZlF","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { type ExtensionAPI, type ExtensionContext, keyText } from \"@lpb-work/pi-coding-agent\";\nimport { type Component, Key, matchesKey, type TUI, truncateToWidth } from \"@lpb-work/pi-tui\";\nimport { BUILTIN_AGENT_NAMES, discoverAgents } from \"../agents/agents.ts\";\nimport {\n\tapplySubagentProfile,\n\tcheckSubagentProfile,\n\tDEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,\n\tgenerateProfilesForProvider,\n\tlistSubagentProfiles,\n\treadSubagentProfile,\n\trefreshProviderModelCatalog,\n} from \"../profiles/profiles.ts\";\nimport { type AsyncRunSummary, formatAsyncRunProgressLabel, listAsyncRuns } from \"../runs/background/async-status.ts\";\nimport { listScheduledRunSummaries } from \"../runs/background/scheduled-runs.ts\";\nimport type { SubagentParamsLike } from \"../runs/foreground/subagent-executor.ts\";\nimport { SUBAGENT_FANOUT_CHILD_ENV } from \"../runs/shared/pi-args.ts\";\nimport { formatTokens, shortenPath } from \"../shared/formatters.ts\";\nimport { findModelInfo, toModelInfo } from \"../shared/model-info.ts\";\nimport {\n\ttype Details,\n\tDIRS,\n\ttype SingleResult,\n\tSLASH_RESULT_TYPE,\n\tSLASH_SUBAGENT_CANCEL_EVENT,\n\tSLASH_SUBAGENT_REQUEST_EVENT,\n\tSLASH_SUBAGENT_RESPONSE_EVENT,\n\tSLASH_SUBAGENT_STARTED_EVENT,\n\tSLASH_SUBAGENT_UPDATE_EVENT,\n\tSLASH_TEXT_RESULT_TYPE,\n\ttype SubagentState,\n\ttype Usage,\n} from \"../shared/types.ts\";\nimport { openSubagentFleet } from \"../tui/fleet.ts\";\nimport { registerPromptWorkflowCommands } from \"./prompt-workflows.ts\";\nimport type { SlashSubagentResponse, SlashSubagentUpdate } from \"./slash-bridge.ts\";\nimport {\n\tapplySlashUpdate,\n\tbuildSlashInitialResult,\n\tfailSlashResult,\n\tfinalizeSlashResult,\n\tresolveSlashMessageDetails,\n} from \"./slash-live-state.ts\";\nimport { openSubagentsAdmin } from \"./subagents-admin.ts\";\n\ninterface InlineConfig {\n\toutput?: string | false;\n\toutputMode?: \"inline\" | \"file-only\";\n\treads?: string[] | false;\n\tmodel?: string;\n\tskill?: string[] | false;\n}\n\nconst parseInlineConfig = (raw: string): InlineConfig => {\n\tconst config: InlineConfig = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst trimmed = part.trim();\n\t\tif (!trimmed) continue;\n\t\tconst eq = trimmed.indexOf(\"=\");\n\t\tif (eq === -1) continue;\n\t\tconst key = trimmed.slice(0, eq).trim();\n\t\tconst val = trimmed.slice(eq + 1).trim();\n\t\tswitch (key) {\n\t\t\tcase \"output\":\n\t\t\t\tconfig.output = val === \"false\" ? false : val;\n\t\t\t\tbreak;\n\t\t\tcase \"outputMode\":\n\t\t\t\tif (val === \"inline\" || val === \"file-only\") config.outputMode = val;\n\t\t\t\tbreak;\n\t\t\tcase \"reads\":\n\t\t\t\tconfig.reads = val === \"false\" ? false : val.split(\"+\").filter(Boolean);\n\t\t\t\tbreak;\n\t\t\tcase \"model\":\n\t\t\t\tconfig.model = val || undefined;\n\t\t\t\tbreak;\n\t\t\tcase \"skill\":\n\t\t\tcase \"skills\":\n\t\t\t\tconfig.skill = val === \"false\" ? false : val.split(\"+\").filter(Boolean);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn config;\n};\n\nconst parseAgentToken = (token: string): { name: string; config: InlineConfig } => {\n\tconst bracket = token.indexOf(\"[\");\n\tif (bracket === -1) return { name: token, config: {} };\n\tconst end = token.lastIndexOf(\"]\");\n\treturn {\n\t\tname: token.slice(0, bracket),\n\t\tconfig: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : undefined)),\n\t};\n};\n\nconst extractExecutionFlags = (rawArgs: string): { args: string; bg: boolean; fork: boolean } => {\n\tlet args = rawArgs.trim();\n\tlet bg = false;\n\tlet fork = false;\n\n\twhile (true) {\n\t\tif (args.endsWith(\" --bg\") || args === \"--bg\") {\n\t\t\tbg = true;\n\t\t\targs = args === \"--bg\" ? \"\" : args.slice(0, -5).trim();\n\t\t\tcontinue;\n\t\t}\n\t\tif (args.endsWith(\" --fork\") || args === \"--fork\") {\n\t\t\tfork = true;\n\t\t\targs = args === \"--fork\" ? \"\" : args.slice(0, -7).trim();\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn { args, bg, fork };\n};\n\nconst makeAgentCompletions = (state: SubagentState) => (prefix: string) => {\n\tif (!state.baseCwd || prefix.includes(\" \")) return null;\n\treturn discoverAgents(state.baseCwd, \"both\")\n\t\t.agents.filter((agent) => agent.name.startsWith(prefix))\n\t\t.map((agent) => ({ value: agent.name, label: agent.name }));\n};\n\nconst makeBuiltinAgentNameCompletions = () => (prefix: string) => {\n\tif (prefix.includes(\" \")) return null;\n\treturn BUILTIN_AGENT_NAMES.filter((name) => name.startsWith(prefix)).map((name) => ({ value: name, label: name }));\n};\n\nconst makeProviderCompletions = (state: SubagentState) => (prefix: string) => {\n\tif (prefix.includes(\" \")) return null;\n\tconst available = state.lastUiContext?.modelRegistry?.getAvailable?.();\n\tif (!Array.isArray(available)) return null;\n\tconst providers = [\n\t\t...new Set(available.map((model) => (typeof model?.provider === \"string\" ? model.provider : \"\")).filter(Boolean)),\n\t].sort((a, b) => a.localeCompare(b));\n\treturn providers\n\t\t.filter((provider) => provider.startsWith(prefix))\n\t\t.map((provider) => ({ value: provider, label: provider }));\n};\n\nfunction sendSlashText(pi: ExtensionAPI, text: string): void {\n\tpi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });\n}\n\nasync function withSlashStatus<T>(ctx: ExtensionContext, text: string, run: () => Promise<T>): Promise<T> {\n\tif (ctx.hasUI) ctx.ui.setStatus(\"subagent-slash-text\", text);\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tif (ctx.hasUI) ctx.ui.setStatus(\"subagent-slash-text\", undefined);\n\t}\n}\n\ntype Theme = ExtensionContext[\"ui\"][\"theme\"];\n\ntype StopSelectorTarget = {\n\tkind: \"async\" | \"scheduled\";\n\tid: string;\n\tlabel: string;\n\tdetail: string;\n\tactionLabel: string;\n};\n\ntype StopSelectorResult = { confirmed: boolean; target?: StopSelectorTarget };\n\nfunction commandForTarget(target: StopSelectorTarget): string {\n\treturn target.kind === \"scheduled\"\n\t\t? `subagent({ action: \"schedule.pause\", id: ${JSON.stringify(target.id)} })`\n\t\t: `subagent({ action: \"stop\", id: ${JSON.stringify(target.id)} })`;\n}\n\nfunction formatAsyncStopTarget(run: AsyncRunSummary): StopSelectorTarget {\n\tconst progress = formatAsyncRunProgressLabel(run);\n\tconst cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);\n\treturn {\n\t\tkind: \"async\",\n\t\tid: run.id,\n\t\tlabel: `${run.id} · ${run.mode} · ${progress}`,\n\t\tdetail: `${run.state} · ${cwd}`,\n\t\tactionLabel: \"stop async run\",\n\t};\n}\n\nfunction scheduledStopTargets(ctx: ExtensionContext, _state: SubagentState): StopSelectorTarget[] {\n\ttry {\n\t\treturn listScheduledRunSummaries(ctx.cwd)\n\t\t\t.filter((schedule) => !schedule.paused && !schedule.activeRunId && schedule.trigger.nextRunAt)\n\t\t\t.sort((left, right) => left.trigger.nextRunAt!.localeCompare(right.trigger.nextRunAt!))\n\t\t\t.map((schedule) => ({\n\t\t\t\tkind: \"scheduled\" as const,\n\t\t\t\tid: schedule.id,\n\t\t\t\tlabel: `${schedule.id} · ${schedule.name}`,\n\t\t\t\tdetail: `scheduled · ${schedule.trigger.nextRunAt}`,\n\t\t\t\tactionLabel: \"pause schedule\",\n\t\t\t}));\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction discoverStopTargets(ctx: ExtensionContext, state: SubagentState): StopSelectorTarget[] {\n\tconst sessionId = state.currentSessionId ?? ctx.sessionManager.getSessionId() ?? undefined;\n\tconst asyncTargets = listAsyncRuns(DIRS.async, {\n\t\tstates: [\"queued\", \"running\"],\n\t\t...(sessionId ? { sessionId } : {}),\n\t}).map(formatAsyncStopTarget);\n\treturn [...asyncTargets, ...scheduledStopTargets(ctx, state)];\n}\n\nfunction stopFallbackText(targets: StopSelectorTarget[]): string {\n\tif (targets.length === 0) return \"No active current-session async runs or scheduled subagent runs to stop.\";\n\tconst lines = [\"Subagent stop targets:\", \"\"];\n\tfor (const target of targets) {\n\t\tlines.push(`- ${target.label}`);\n\t\tlines.push(`  ${target.detail}`);\n\t\tlines.push(`  ${target.actionLabel}: ${commandForTarget(target)}`);\n\t\tif (target.kind === \"async\") lines.push(`  slash: /subagents-stop ${target.id}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction selectForegroundDetachControl(state: SubagentState, requested: string) {\n\tconst controls = [...state.foregroundControls.values()];\n\tif (requested) {\n\t\tconst matches = controls.filter((control) => control.runId === requested || control.runId.startsWith(requested));\n\t\tif (matches.length > 1)\n\t\t\tthrow new Error(\n\t\t\t\t`Ambiguous foreground run id prefix '${requested}' matched: ${matches.map((control) => control.runId).join(\", \")}. Provide a longer id.`,\n\t\t\t);\n\t\treturn matches[0];\n\t}\n\tconst singleControls = controls.filter((control) => control.mode === \"single\");\n\tif (state.lastForegroundControlId) {\n\t\tconst latest = state.foregroundControls.get(state.lastForegroundControlId);\n\t\tif (latest?.mode === \"single\") return latest;\n\t}\n\treturn singleControls.sort((left, right) => right.updatedAt - left.updatedAt)[0];\n}\n\nclass SubagentsStopSelector implements Component {\n\treadonly width = 84;\n\tprivate selected = 0;\n\tprivate confirming = false;\n\tprivate readonly tui: TUI;\n\tprivate readonly theme: Theme;\n\tprivate readonly targets: StopSelectorTarget[];\n\tprivate readonly done: (result: StopSelectorResult) => void;\n\n\tconstructor(tui: TUI, theme: Theme, targets: StopSelectorTarget[], done: (result: StopSelectorResult) => void) {\n\t\tthis.tui = tui;\n\t\tthis.theme = theme;\n\t\tthis.targets = targets;\n\t\tthis.done = done;\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (matchesKey(data, \"escape\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.done({ confirmed: false });\n\t\t\treturn;\n\t\t}\n\t\tif (this.confirming) {\n\t\t\tif (matchesKey(data, \"return\") || data.toLowerCase() === \"y\") {\n\t\t\t\tthis.done({ confirmed: true, target: this.targets[this.selected] });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (data.toLowerCase() === \"n\" || matchesKey(data, \"backspace\")) {\n\t\t\t\tthis.confirming = false;\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"up\") || matchesKey(data, \"k\")) {\n\t\t\tthis.selected = Math.max(0, this.selected - 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"down\") || matchesKey(data, \"j\")) {\n\t\t\tthis.selected = Math.min(this.targets.length - 1, this.selected + 1);\n\t\t\tthis.tui.requestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"return\")) {\n\t\t\tthis.confirming = true;\n\t\t\tthis.tui.requestRender();\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst contentWidth = Math.max(0, Math.min(this.width, Math.floor(width)));\n\t\tconst lines = [\n\t\t\tthis.theme.bold(\"Stop subagent run\"),\n\t\t\tthis.theme.fg(\"dim\", \"Select a current-session async run to stop, or a scheduled run to cancel.\"),\n\t\t\t\"\",\n\t\t];\n\t\tconst maxRows = 10;\n\t\tconst start = Math.max(0, Math.min(this.selected - maxRows + 1, Math.max(0, this.targets.length - maxRows)));\n\t\tfor (let index = start; index < Math.min(this.targets.length, start + maxRows); index++) {\n\t\t\tconst target = this.targets[index]!;\n\t\t\tconst selected = index === this.selected;\n\t\t\tconst marker = selected ? \"›\" : \" \";\n\t\t\tconst actionLabel = target.actionLabel;\n\t\t\tconst action =\n\t\t\t\ttarget.kind === \"scheduled\" ? this.theme.fg(\"warning\", actionLabel) : this.theme.fg(\"accent\", actionLabel);\n\t\t\tconst labelWidth = Math.max(0, contentWidth - marker.length - actionLabel.length - 2);\n\t\t\tlines.push(`${marker} ${action} ${target.label.slice(0, labelWidth)}`);\n\t\t\tif (selected) lines.push(this.theme.fg(\"dim\", `  ${target.detail}`.slice(0, contentWidth)));\n\t\t}\n\t\tif (this.targets.length > maxRows)\n\t\t\tlines.push(\n\t\t\t\tthis.theme.fg(\n\t\t\t\t\t\"dim\",\n\t\t\t\t\t`Showing ${start + 1}-${Math.min(this.targets.length, start + maxRows)} of ${this.targets.length}`,\n\t\t\t\t),\n\t\t\t);\n\t\tlines.push(\"\");\n\t\tif (this.confirming) {\n\t\t\tconst target = this.targets[this.selected]!;\n\t\t\tlines.push(this.theme.fg(\"warning\", `Confirm: ${target.actionLabel} ${target.id}?`));\n\t\t\tif (target.kind === \"async\")\n\t\t\t\tlines.push(this.theme.fg(\"dim\", \"Stop ends this run; use interrupt for a resumable pause.\"));\n\t\t\tlines.push(this.theme.fg(\"dim\", \"Enter/Y confirms · N returns · Esc cancels\"));\n\t\t} else {\n\t\t\tlines.push(this.theme.fg(\"dim\", \"↑↓/jk select · Enter confirm · Esc cancel\"));\n\t\t}\n\t\treturn lines.map((line) => truncateToWidth(line, contentWidth));\n\t}\n}\n\nfunction emptyUsage(): Usage {\n\treturn { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };\n}\n\nfunction addUsage(target: Usage, source: Usage): void {\n\ttarget.input += source.input;\n\ttarget.output += source.output;\n\ttarget.cacheRead += source.cacheRead;\n\ttarget.cacheWrite += source.cacheWrite;\n\ttarget.cost += source.cost;\n\ttarget.turns += source.turns;\n}\n\nfunction usageHasValue(usage: Usage): boolean {\n\treturn (\n\t\tusage.input !== 0 ||\n\t\tusage.output !== 0 ||\n\t\tusage.cacheRead !== 0 ||\n\t\tusage.cacheWrite !== 0 ||\n\t\tusage.cost !== 0 ||\n\t\tusage.turns !== 0\n\t);\n}\n\nfunction assistantUsageFromMessage(message: unknown): Usage | undefined {\n\tif (!message || typeof message !== \"object\") return undefined;\n\tconst msg = message as { role?: unknown; usage?: unknown };\n\tif (msg.role !== \"assistant\" || !msg.usage || typeof msg.usage !== \"object\") return undefined;\n\tconst usage = msg.usage as {\n\t\tinput?: unknown;\n\t\toutput?: unknown;\n\t\tcacheRead?: unknown;\n\t\tcacheWrite?: unknown;\n\t\tcost?: { total?: unknown };\n\t};\n\treturn {\n\t\tinput: typeof usage.input === \"number\" ? usage.input : 0,\n\t\toutput: typeof usage.output === \"number\" ? usage.output : 0,\n\t\tcacheRead: typeof usage.cacheRead === \"number\" ? usage.cacheRead : 0,\n\t\tcacheWrite: typeof usage.cacheWrite === \"number\" ? usage.cacheWrite : 0,\n\t\tcost: typeof usage.cost?.total === \"number\" ? usage.cost.total : 0,\n\t\tturns: 1,\n\t};\n}\n\nfunction isSubagentDetails(value: unknown): value is Details {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst details = value as { mode?: unknown; results?: unknown };\n\treturn typeof details.mode === \"string\" && Array.isArray(details.results);\n}\n\nfunction detailsFromSessionEntry(entry: unknown): Details | undefined {\n\tif (!entry || typeof entry !== \"object\") return undefined;\n\tconst record = entry as { type?: unknown; customType?: unknown; details?: unknown; message?: unknown };\n\tif (record.type === \"custom_message\" && record.customType === SLASH_RESULT_TYPE) {\n\t\tconst details = resolveSlashMessageDetails(record.details)?.result.details;\n\t\treturn isSubagentDetails(details) ? details : undefined;\n\t}\n\tif (record.type !== \"message\" || !record.message || typeof record.message !== \"object\") return undefined;\n\tconst message = record.message as { role?: unknown; toolName?: unknown; details?: unknown };\n\tif (message.role !== \"toolResult\" || message.toolName !== \"subagent\") return undefined;\n\treturn isSubagentDetails(message.details) ? message.details : undefined;\n}\n\nfunction formatCostUsage(label: string, usage: Usage): string {\n\tconst extras = [\n\t\tusage.cacheRead ? `cache read ${formatTokens(usage.cacheRead)}` : \"\",\n\t\tusage.cacheWrite ? `cache write ${formatTokens(usage.cacheWrite)}` : \"\",\n\t\tusage.turns ? `${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}` : \"\",\n\t].filter(Boolean);\n\treturn `${label}: ↑${formatTokens(usage.input)} ↓${formatTokens(usage.output)} $${usage.cost.toFixed(4)}${extras.length ? ` (${extras.join(\", \")})` : \"\"}`;\n}\n\nfunction buildSubagentCostReport(ctx: ExtensionContext): string {\n\tconst parent = emptyUsage();\n\tconst childTotal = emptyUsage();\n\tconst total = emptyUsage();\n\tconst children: Array<{ label: string; usage: Usage; sessionFile?: string }> = [];\n\tfor (const entry of ctx.sessionManager.getBranch()) {\n\t\tconst message = entry.type === \"message\" ? (entry as { message?: unknown }).message : undefined;\n\t\tconst parentUsage = assistantUsageFromMessage(message);\n\t\tif (parentUsage) addUsage(parent, parentUsage);\n\t\tconst details = detailsFromSessionEntry(entry);\n\t\tif (!details) continue;\n\t\tfor (const result of details.results) {\n\t\t\tif (!usageHasValue(result.usage)) continue;\n\t\t\tconst usage = { ...result.usage };\n\t\t\tchildren.push({\n\t\t\t\tlabel: `Child ${children.length + 1} (${result.agent})`,\n\t\t\t\tusage,\n\t\t\t\t...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),\n\t\t\t});\n\t\t\taddUsage(childTotal, usage);\n\t\t}\n\t}\n\taddUsage(total, parent);\n\taddUsage(total, childTotal);\n\tconst lines = [\"Subagent cost\", \"\", formatCostUsage(\"Parent\", parent)];\n\tif (children.length === 0) {\n\t\tlines.push(\"No subagent child usage found in this session.\");\n\t} else {\n\t\tfor (const child of children) {\n\t\t\tlines.push(formatCostUsage(child.label, child.usage));\n\t\t\tif (child.sessionFile) lines.push(`  Session: ${child.sessionFile}`);\n\t\t}\n\t}\n\tlines.push(\"────────────────────────────\", formatCostUsage(\"Children\", childTotal), formatCostUsage(\"Total\", total));\n\treturn lines.join(\"\\n\");\n}\n\nfunction parseSingleRequiredArg(\n\targs: string,\n\tusage: string,\n): { ok: true; value: string } | { ok: false; message: string } {\n\tconst parts = args.trim().split(/\\s+/).filter(Boolean);\n\tif (parts.length !== 1) return { ok: false, message: usage };\n\treturn { ok: true, value: parts[0]! };\n}\n\nfunction getProfileWorkerModel(profile: {\n\tsubagents?: { agentOverrides?: Record<string, { model?: string }> };\n}): string | undefined {\n\tconst model = profile.subagents?.agentOverrides?.worker?.model;\n\treturn typeof model === \"string\" && model.trim() ? model.trim() : undefined;\n}\n\nasync function requestSlashRun(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\trequestId: string,\n\tparams: SubagentParamsLike,\n): Promise<SlashSubagentResponse> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet done = false;\n\t\tlet started = false;\n\n\t\tconst startTimeoutMs = 15_000;\n\t\tconst startTimeout = setTimeout(() => {\n\t\t\tfinish(() =>\n\t\t\t\treject(\n\t\t\t\t\tnew Error(\"Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.\"),\n\t\t\t\t),\n\t\t\t);\n\t\t}, startTimeoutMs);\n\n\t\tconst onStarted = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tif ((data as { requestId?: unknown }).requestId !== requestId) return;\n\t\t\tstarted = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tif (ctx.hasUI) ctx.ui.setStatus(\"subagent-slash\", \"running...\");\n\t\t};\n\n\t\tconst onResponse = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst response = data as Partial<SlashSubagentResponse>;\n\t\t\tif (response.requestId !== requestId) return;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tfinish(() => resolve(response as SlashSubagentResponse));\n\t\t};\n\n\t\tconst onUpdate = (data: unknown) => {\n\t\t\tif (done || !data || typeof data !== \"object\") return;\n\t\t\tconst update = data as SlashSubagentUpdate;\n\t\t\tif (update.requestId !== requestId) return;\n\t\t\tapplySlashUpdate(requestId, update);\n\t\t\tif (!ctx.hasUI) return;\n\t\t\tconst tool = update.currentTool ? ` ${update.currentTool}` : \"\";\n\t\t\tconst count = update.toolCount ?? 0;\n\t\t\tconst liveDetailKey = keyText(\"app.tools.expand\");\n\t\t\tctx.ui.setStatus(\"subagent-slash\", `${count} tools${tool} | ${liveDetailKey} live detail`);\n\t\t};\n\n\t\tconst onTerminalInput = ctx.hasUI\n\t\t\t? ctx.ui.onTerminalInput((input) => {\n\t\t\t\t\tif (!matchesKey(input, Key.escape)) return undefined;\n\t\t\t\t\tpi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });\n\t\t\t\t\tfinish(() => reject(new Error(\"Cancelled\")));\n\t\t\t\t\treturn { consume: true };\n\t\t\t\t})\n\t\t\t: undefined;\n\n\t\tconst unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);\n\t\tconst unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);\n\t\tconst unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);\n\n\t\tconst finish = (next: () => void) => {\n\t\t\tif (done) return;\n\t\t\tdone = true;\n\t\t\tclearTimeout(startTimeout);\n\t\t\tunsubStarted();\n\t\t\tunsubResponse();\n\t\t\tunsubUpdate();\n\t\t\tonTerminalInput?.();\n\t\t\tnext();\n\t\t};\n\n\t\tpi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ctx });\n\n\t\t// Bridge emits STARTED synchronously during REQUEST emit.\n\t\t// If not started, no bridge received the request.\n\t\tif (!started && done) return;\n\t\tif (!started) {\n\t\t\tfinish(() =>\n\t\t\t\treject(new Error(\"No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.\")),\n\t\t\t);\n\t\t}\n\t});\n}\n\nfunction extractSlashMessageText(content: string | Array<{ type?: string; text?: string }>): string {\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part?.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\");\n}\n\nfunction formatExportPathList(paths: string[]): string {\n\treturn paths.map((file) => `- \\`${file}\\``).join(\"\\n\");\n}\n\nfunction collectResultPaths(results: SingleResult[], getPath: (result: SingleResult) => string | undefined): string[] {\n\treturn results.map(getPath).filter((file): file is string => typeof file === \"string\" && file.length > 0);\n}\n\nfunction buildSlashExportText(response: SlashSubagentResponse): string {\n\tconst output = extractSlashMessageText(response.result.content) || response.errorText || \"(no output)\";\n\tconst results = response.result.details?.results ?? [];\n\tconst sessionFiles = collectResultPaths(results, (result) => result.sessionFile);\n\tconst savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);\n\tconst artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);\n\tconst sections = [\"## Subagent result\", output];\n\tif (sessionFiles.length > 0) sections.push(\"## Child session exports\", formatExportPathList(sessionFiles));\n\tif (savedOutputs.length > 0) sections.push(\"## Saved outputs\", formatExportPathList(savedOutputs));\n\tif (artifactOutputs.length > 0) sections.push(\"## Artifact outputs\", formatExportPathList(artifactOutputs));\n\treturn sections.join(\"\\n\\n\");\n}\n\nfunction persistSlashSessionSnapshot(ctx: ExtensionContext): void {\n\ttry {\n\t\tif (!ctx.sessionManager) return;\n\t\tconst sessionManager = ctx.sessionManager as typeof ctx.sessionManager & {\n\t\t\t_rewriteFile?: () => void;\n\t\t\tflushed?: boolean;\n\t\t};\n\t\tconst sessionFile = sessionManager.getSessionFile();\n\t\tif (!sessionFile || typeof sessionManager._rewriteFile !== \"function\") return;\n\t\tfs.mkdirSync(path.dirname(sessionFile), { recursive: true });\n\t\tsessionManager._rewriteFile();\n\t\tsessionManager.flushed = true;\n\t} catch (error) {\n\t\tconsole.error(\"Failed to persist slash session snapshot for export:\", error);\n\t}\n}\n\nasync function runSlashSubagent(pi: ExtensionAPI, ctx: ExtensionContext, params: SubagentParamsLike): Promise<void> {\n\tif (ctx.hasUI) ctx.ui.setToolsExpanded(false);\n\tconst requestId = randomUUID();\n\tconst initialDetails = buildSlashInitialResult(requestId, params);\n\tconst initialText = extractSlashMessageText(initialDetails.result.content) || \"Running subagent...\";\n\tpi.sendMessage({\n\t\tcustomType: SLASH_RESULT_TYPE,\n\t\tcontent: initialText,\n\t\tdisplay: true,\n\t\tdetails: initialDetails,\n\t});\n\tpersistSlashSessionSnapshot(ctx);\n\n\ttry {\n\t\tconst response = await requestSlashRun(pi, ctx, requestId, params);\n\t\tconst finalDetails = finalizeSlashResult(response);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: buildSlashExportText(response),\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: finalDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (response.isError && ctx.hasUI) {\n\t\t\tctx.ui.notify(response.errorText || \"Subagent failed\", \"error\");\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst failedDetails = failSlashResult(requestId, params, message);\n\t\tpi.sendMessage({\n\t\t\tcustomType: SLASH_RESULT_TYPE,\n\t\t\tcontent: `## Subagent result\\n\\n${message}`,\n\t\t\tdisplay: !ctx.hasUI,\n\t\t\tdetails: failedDetails,\n\t\t});\n\t\tpersistSlashSessionSnapshot(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setStatus(\"subagent-slash\", undefined);\n\t\t}\n\t\tif (message === \"Cancelled\") {\n\t\t\tif (ctx.hasUI) ctx.ui.notify(\"Cancelled\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tif (ctx.hasUI) ctx.ui.notify(message, \"error\");\n\t}\n}\n\nfunction launchSlashSubagent(pi: ExtensionAPI, ctx: ExtensionContext, params: SubagentParamsLike): void {\n\tvoid runSlashSubagent(pi, ctx, params);\n}\n\nfunction slashRunWorkflowScript(key: string, child: Record<string, unknown>): string {\n\treturn `return runs.run(${JSON.stringify(key)}, ${JSON.stringify(child)})`;\n}\n\nexport function registerSlashCommands(pi: ExtensionAPI, state: SubagentState): void {\n\tlet fleetOpen = false;\n\tconst showFleet = async (ctx: ExtensionContext) => {\n\t\tstate.lastUiContext = ctx;\n\t\tif (!ctx.hasUI) {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"status\", view: \"fleet\" });\n\t\t\treturn;\n\t\t}\n\t\tif (fleetOpen) {\n\t\t\tctx.ui.notify(\"Subagent fleet inspector is already open.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tfleetOpen = true;\n\t\ttry {\n\t\t\tawait openSubagentFleet(ctx, state, { asyncDirRoot: DIRS.async, resultsDir: DIRS.results });\n\t\t} finally {\n\t\t\tfleetOpen = false;\n\t\t}\n\t};\n\n\tpi.registerCommand(\"subagents\", {\n\t\tdescription: \"Administer subagents: inspect metadata and update models, thinking, or prompts\",\n\t\thandler: async (args, ctx) => {\n\t\t\tawait openSubagentsAdmin(pi, ctx, args);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"run\", {\n\t\tdescription: \"Run one subagent through workflowScript: /run agent[output=file] [task] [--bg] [--fork]\",\n\t\tgetArgumentCompletions: makeAgentCompletions(state),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);\n\t\t\tconst input = cleanedArgs.trim();\n\t\t\tconst firstSpace = input.indexOf(\" \");\n\t\t\tif (!input) {\n\t\t\t\tctx.ui.notify(\"Usage: /run <agent> [task] [--bg] [--fork]\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { name: agentName, config: inline } = parseAgentToken(\n\t\t\t\tfirstSpace === -1 ? input : input.slice(0, firstSpace),\n\t\t\t);\n\t\t\tconst task = firstSpace === -1 ? \"\" : input.slice(firstSpace + 1).trim();\n\n\t\t\tif (!state.baseCwd) {\n\t\t\t\tctx.ui.notify(\"Subagent session cwd is not initialized yet\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst agents = discoverAgents(state.baseCwd, \"both\").agents;\n\t\t\tif (!agents.find((a) => a.name === agentName)) {\n\t\t\t\tctx.ui.notify(`Unknown agent: ${agentName}`, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet finalTask = task;\n\t\t\tif (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {\n\t\t\t\tfinalTask = `[Read from: ${inline.reads.join(\", \")}]\\n\\n${finalTask}`;\n\t\t\t}\n\t\t\tconst child: Record<string, unknown> = { agent: agentName, task: finalTask, agentScope: \"both\" };\n\t\t\tif (inline.output !== undefined) child.output = inline.output;\n\t\t\tif (inline.outputMode !== undefined) child.outputMode = inline.outputMode;\n\t\t\tif (inline.skill !== undefined) child.skill = inline.skill;\n\t\t\tif (inline.model) child.model = inline.model;\n\t\t\tif (fork) child.context = \"fork\";\n\t\t\tlaunchSlashSubagent(pi, ctx, {\n\t\t\t\tworkflowScript: slashRunWorkflowScript(\"run\", child),\n\t\t\t\tasync: !!bg,\n\t\t\t});\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagent-cost\", {\n\t\tdescription: \"Show parent and subagent child usage cost for this session\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tsendSlashText(pi, buildSubagentCostReport(ctx));\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-doctor\", {\n\t\tdescription: \"Show subagent diagnostics\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"doctor\" });\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-refine\", {\n\t\tdescription: \"Generate a bounded project-local refinement overlay for one subagent\",\n\t\tgetArgumentCompletions: makeAgentCompletions(state),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst parts = args.trim().split(/\\s+/).filter(Boolean);\n\t\t\tif (parts.length !== 1) {\n\t\t\t\tctx.ui.notify(\"Usage: /subagents-refine <agent>\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"refine\", agent: parts[0] });\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-fleet\", {\n\t\tdescription: \"Open the live subagent fleet inspector\",\n\t\thandler: async (_args, ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerShortcut(Key.ctrlAlt(\"f\"), {\n\t\tdescription: \"Open subagent fleet inspector\",\n\t\thandler: async (ctx) => showFleet(ctx),\n\t});\n\n\tpi.registerCommand(\"subagents-detach\", {\n\t\tdescription: \"Detach the active foreground single-subagent run without terminating it\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst id = args.trim();\n\t\t\tlet control: ReturnType<typeof selectForegroundDetachControl>;\n\t\t\ttry {\n\t\t\t\tcontrol = selectForegroundDetachControl(state, id);\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!control) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\tid\n\t\t\t\t\t\t? `No active foreground run found for '${id}'.`\n\t\t\t\t\t\t: \"No active foreground single-subagent run to detach.\",\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (control.mode !== \"single\") {\n\t\t\t\tctx.ui.notify(\"/subagents-detach currently supports single-subagent runs only.\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!control.detach?.()) {\n\t\t\t\tctx.ui.notify(`Foreground run ${control.runId} is not currently detachable.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendSlashText(\n\t\t\t\tpi,\n\t\t\t\t`Detached foreground run ${control.runId} without terminating its child. Use subagent({ action: \"status\", id: ${JSON.stringify(control.runId)} }) or subagent_wait({ id: ${JSON.stringify(control.runId)} }) to recover the eventual result. This does not daemonize the process or guarantee survival across Pi reload/restart.`,\n\t\t\t);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-stop\", {\n\t\tdescription: \"Stop a current-session async subagent run\",\n\t\thandler: async (args, ctx) => {\n\t\t\tconst id = args.trim();\n\t\t\tif (id) {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (process.env[SUBAGENT_FANOUT_CHILD_ENV] === \"1\") {\n\t\t\t\tsendSlashText(\n\t\t\t\t\tpi,\n\t\t\t\t\t'Selector unavailable in child-safe fanout mode. Pass an explicit current-session top-level async run id, for example `/subagents-stop <run-id>` or `subagent({ action: \"stop\", id: \"<run-id>\" })`.',\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet targets: StopSelectorTarget[];\n\t\t\ttry {\n\t\t\t\ttargets = discoverStopTargets(ctx, state);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tctx.ui.notify(message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tsendSlashText(pi, stopFallbackText(targets));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targets.length === 0) {\n\t\t\t\tctx.ui.notify(\"No active current-session async runs or scheduled subagent runs to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst result = await ctx.ui.custom<StopSelectorResult>(\n\t\t\t\t(tui, theme, _kb, done) => new SubagentsStopSelector(tui, theme, targets, done),\n\t\t\t\t{ overlay: true, overlayOptions: { anchor: \"center\", width: 88, maxHeight: \"80%\" } },\n\t\t\t);\n\t\t\tif (!result?.confirmed || !result.target) return;\n\t\t\tif (result.target.kind === \"scheduled\") {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"schedule.pause\", id: result.target.id });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"stop\", id: result.target.id });\n\t\t},\n\t});\n\n\tregisterPromptWorkflowCommands({\n\t\tpi,\n\t\tgetCwd: () => state.baseCwd || process.cwd(),\n\t\trun: async (params, ctx) => {\n\t\t\tlaunchSlashSubagent(pi, ctx, params);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-models\", {\n\t\tdescription: \"Show runtime-loaded builtin subagent models\",\n\t\tgetArgumentCompletions: makeBuiltinAgentNameCompletions(),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!trimmed) {\n\t\t\t\tawait runSlashSubagent(pi, ctx, { action: \"models\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst parts = trimmed.split(/\\s+/).filter(Boolean);\n\t\t\tif (parts.length !== 1) {\n\t\t\t\tctx.ui.notify(\"Usage: /subagents-models [builtin-agent-name]\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst agent = parts[0]!;\n\t\t\tif (!(BUILTIN_AGENT_NAMES as readonly string[]).includes(agent)) {\n\t\t\t\tctx.ui.notify(`Unknown builtin agent: ${agent}`, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait runSlashSubagent(pi, ctx, { action: \"models\", agent });\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-profiles\", {\n\t\tdescription: \"List saved subagent profiles\",\n\t\thandler: async (_args, _ctx) => {\n\t\t\tconst profiles = listSubagentProfiles();\n\t\t\tif (profiles.length === 0) {\n\t\t\t\tsendSlashText(pi, \"Subagent profiles\\n\\nNo subagent profiles found in ~/.pi/agent/profiles/pi-subagents/\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendSlashText(pi, `Subagent profiles\\n\\n${profiles.join(\"\\n\")}`);\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-load-profile\", {\n\t\tdescription: \"Load a subagent profile into ~/.pi/agent/settings.json\",\n\t\tgetArgumentCompletions: (prefix) => {\n\t\t\tif (prefix.includes(\" \")) return null;\n\t\t\treturn listSubagentProfiles()\n\t\t\t\t.filter((name) => name.startsWith(prefix))\n\t\t\t\t.map((name) => ({ value: name, label: name }));\n\t\t},\n\t\thandler: async (args, ctx) => {\n\t\t\tconst parsed = parseSingleRequiredArg(args, \"Usage: /subagents-load-profile <name>\");\n\t\t\tif (parsed.ok === false) {\n\t\t\t\tctx.ui.notify(parsed.message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait withSlashStatus(ctx, `Loading profile ${parsed.value}…`, async () => {\n\t\t\t\t\tconst { profile } = readSubagentProfile(parsed.value);\n\t\t\t\t\tconst workerModel = getProfileWorkerModel(profile);\n\t\t\t\t\tconst result = applySubagentProfile(parsed.value);\n\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t`Loaded subagent profile: ${parsed.value}`,\n\t\t\t\t\t\t`Profile: ${result.filePath}`,\n\t\t\t\t\t\t`Updated: ${result.settingsPath}`,\n\t\t\t\t\t];\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tworkerModel &&\n\t\t\t\t\t\ttypeof pi.setModel === \"function\" &&\n\t\t\t\t\t\ttypeof ctx.modelRegistry?.find === \"function\" &&\n\t\t\t\t\t\ttypeof ctx.modelRegistry?.getAvailable === \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst shouldSwitch = await ctx.ui.confirm(\n\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t`Profile loaded. Also switch this session to the profile worker model?\\n\\n${workerModel}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (shouldSwitch) {\n\t\t\t\t\t\t\tconst modelInfo = findModelInfo(workerModel, ctx.modelRegistry.getAvailable().map(toModelInfo));\n\t\t\t\t\t\t\tconst model = modelInfo ? ctx.modelRegistry.find(modelInfo.provider, modelInfo.id) : undefined;\n\t\t\t\t\t\t\tif (!modelInfo || !model) {\n\t\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\t`Could not switch current session model: '${workerModel}' is not available in the current model registry.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst success = await pi.setModel(model);\n\t\t\t\t\t\t\t\tif (success) lines.push(`Current session model switched to: ${modelInfo.fullId}`);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\t\t`Could not switch current session model to '${workerModel}': no API key or provider access is available.`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (workerModel) {\n\t\t\t\t\t\tlines.push(`Profile worker model: ${workerModel}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tsendSlashText(pi, lines.join(\"\\n\"));\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-refresh-provider-models\", {\n\t\tdescription: \"Refresh the cached model catalog for one provider\",\n\t\tgetArgumentCompletions: makeProviderCompletions(state),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tconst force = /(?:^|\\s)--force$/.test(trimmed) || /(?:^|\\s)force$/.test(trimmed);\n\t\t\tconst withoutForce = trimmed.replace(/(?:^|\\s)(?:--force|force)$/, \"\").trim();\n\t\t\tconst parsed = parseSingleRequiredArg(\n\t\t\t\twithoutForce,\n\t\t\t\t\"Usage: /subagents-refresh-provider-models <provider> [--force]\",\n\t\t\t);\n\t\t\tif (parsed.ok === false) {\n\t\t\t\tctx.ui.notify(parsed.message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait withSlashStatus(ctx, `Refreshing provider models for ${parsed.value}…`, async () => {\n\t\t\t\t\tconst result = await refreshProviderModelCatalog(pi, ctx, parsed.value, {\n\t\t\t\t\t\tforce,\n\t\t\t\t\t\tmaxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,\n\t\t\t\t\t});\n\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\"Provider model catalog\",\n\t\t\t\t\t\t`Provider: ${parsed.value}`,\n\t\t\t\t\t\t`Status: ${result.reused ? \"fresh cache reused\" : \"refreshed\"}`,\n\t\t\t\t\t\t`File: ${result.filePath}`,\n\t\t\t\t\t\t`Models: ${result.catalog.models.length}`,\n\t\t\t\t\t\t`Refreshed at: ${result.catalog.refreshedAt}`,\n\t\t\t\t\t];\n\t\t\t\t\tif (result.heuristicFallbackCount > 0) {\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t`Warning: ${result.heuristicFallbackCount} model${result.heuristicFallbackCount === 1 ? \" was\" : \"s were\"} classified with name heuristics fallback.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tsendSlashText(pi, lines.join(\"\\n\"));\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-generate-profiles\", {\n\t\tdescription: \"Generate <provider>.quota and <provider>.quality subagent profiles\",\n\t\tgetArgumentCompletions: makeProviderCompletions(state),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst parsed = parseSingleRequiredArg(args, \"Usage: /subagents-generate-profiles <provider>\");\n\t\t\tif (parsed.ok === false) {\n\t\t\t\tctx.ui.notify(parsed.message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait withSlashStatus(ctx, `Generating profiles for ${parsed.value}…`, async () => {\n\t\t\t\t\tconst result = await generateProfilesForProvider(pi, ctx, parsed.value, {\n\t\t\t\t\t\tmaxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,\n\t\t\t\t\t});\n\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\"Generated subagent profiles\",\n\t\t\t\t\t\t`Provider: ${parsed.value}`,\n\t\t\t\t\t\t`Catalog: ${result.catalogPath}`,\n\t\t\t\t\t\t`Quota: ${result.quotaPath}`,\n\t\t\t\t\t\t`  cheap=${result.quotaModels.cheap}`,\n\t\t\t\t\t\t`  medium=${result.quotaModels.medium}`,\n\t\t\t\t\t\t`  strong=${result.quotaModels.strong}`,\n\t\t\t\t\t\t`Quality: ${result.qualityPath}`,\n\t\t\t\t\t\t`  cheap=${result.qualityModels.cheap}`,\n\t\t\t\t\t\t`  medium=${result.qualityModels.medium}`,\n\t\t\t\t\t\t`  strong=${result.qualityModels.strong}`,\n\t\t\t\t\t];\n\t\t\t\t\tif (result.selectedHeuristicFallbackCount > 0) {\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t`Warning: generated profiles depend on heuristic-only classification for ${result.selectedHeuristicFallbackCount} selected model${result.selectedHeuristicFallbackCount === 1 ? \"\" : \"s\"}.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (result.heuristicFallbackCount > 0) {\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t`Warning: provider catalog still contains ${result.heuristicFallbackCount} heuristic-classified model${result.heuristicFallbackCount === 1 ? \"\" : \"s\"}.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tsendSlashText(pi, lines.join(\"\\n\"));\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n\n\tpi.registerCommand(\"subagents-check-profile\", {\n\t\tdescription: \"Check whether a saved profile still points to usable models\",\n\t\tgetArgumentCompletions: (prefix) => {\n\t\t\tif (prefix.includes(\" \")) return null;\n\t\t\treturn listSubagentProfiles()\n\t\t\t\t.filter((name) => name.startsWith(prefix))\n\t\t\t\t.map((name) => ({ value: name, label: name }));\n\t\t},\n\t\thandler: async (args, ctx) => {\n\t\t\tconst parsed = parseSingleRequiredArg(args, \"Usage: /subagents-check-profile <name>\");\n\t\t\tif (parsed.ok === false) {\n\t\t\t\tctx.ui.notify(parsed.message, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait withSlashStatus(ctx, `Checking profile ${parsed.value}…`, async () => {\n\t\t\t\t\tconst result = await checkSubagentProfile(pi, ctx, parsed.value);\n\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\"Subagent profile check\",\n\t\t\t\t\t\t`Profile: ${result.profileName}`,\n\t\t\t\t\t\t`File: ${result.filePath}`,\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t...result.results.map(\n\t\t\t\t\t\t\t(entry) =>\n\t\t\t\t\t\t\t\t`${entry.agent} → ${entry.model} — registry ${entry.inRegistry ? \"ok\" : \"missing\"}; probe ${entry.probe.status}${entry.probe.message ? ` (${entry.probe.message.split(/\\r?\\n/, 1)[0]})` : \"\"}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t];\n\t\t\t\t\tsendSlashText(pi, lines.join(\"\\n\"));\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}