/** * Pure helpers extracted from `renderer/commands.ts`. * * The dispatcher is one giant switch/case; many cases contain small but * tricky bits of pure logic (arg parsing, snippet extraction, message * formatting) that were previously untestable because they were inlined * alongside `ctx.app.*` calls. Pulling them here gives them direct unit * coverage. */ import type { SyncFailure, SyncResult } from '../../utils/codeepCloud'; export interface SearchSnippet { role: string; messageIndex: number; matchedText: string; } /** Snippet window: chars of context before / after the match. */ export declare const SEARCH_SNIPPET_BEFORE = 30; export declare const SEARCH_SNIPPET_AFTER = 50; /** * Build search-result snippets for messages matching `term`. Mirrors the * inline loop that used to live in the `/search` case. Case-insensitive. */ export declare function buildSearchSnippets(messages: Array<{ role: string; content: string; }>, term: string): SearchSnippet[]; /** * Parse the `/compact ` argument. Returns a value of at least 2 * (never compacts below 2 messages); defaults to `fallback` when the arg * is missing or unparseable. * * Note: we use `Number.isNaN` rather than `parsed || fallback` because * `0` is a valid (if useless) numeric input that should clamp to 2, not * silently fall through to the default. */ export declare function parseKeepRecent(arg: string | undefined, fallback?: number): number; /** * Join slash-command args into a single hyphen-separated name, as used by * `/rename`. Empty args are dropped so `/rename my session ` still * yields `my-session`. */ export declare function joinSessionName(args: string[]): string; export declare const TASK_TYPES: readonly ["task", "bug", "feature"]; export type TaskType = (typeof TASK_TYPES)[number]; /** Result of parsing `/tasks add` flags. */ export interface ParsedTaskAdd { title: string; description: string; type: TaskType; } /** * Parse the args following `/tasks add` into a title, description, and * type. Flags (`--bug`, `--feature`, `--task`) set the type; `--desc` / * `--description` captures the following words until the next flag. * Non-flag words before any `--desc` form the title. */ export declare function parseTaskAddArgs(args: string[]): ParsedTaskAdd; /** Render a list of tasks as a Markdown list, mirroring `/tasks`. */ export declare function formatTaskList(tasks: Array<{ title: string; type?: string | null; description?: string | null; project_name?: string | null; }>, scopeProjectName?: string): string; /** Render the `/profile list` Markdown message from saved profile names. */ export declare function formatProfileList(profiles: string[]): string; /** Render the `/memory list` Markdown message from saved notes. */ export declare function formatMemoryList(notes: string[]): string; export interface StatsModelRow { model: string; provider: string; promptTokens: number; completionTokens: number; estimatedCost: number; } export interface StatsTotals { requestCount: number; totalTokens: number; totalPromptTokens: number; totalCompletionTokens: number; /** Raw sum across every entry. The report's total is derived from the * breakdown instead, so flat-fee rows don't contribute dollars. */ estimatedCost: number; } export interface StatsCache { cacheReadTokens: number; cacheCreationTokens: number; estimatedSavingsUsd: number; /** Rates that applied, from getCacheStats. Absent → no rate is quoted rather * than assuming Anthropic's 0.1×, which was wrong for DeepSeek, Kimi, Qwen * and Fable 5.1. */ cacheReadRates?: number[]; } export interface PricingRow { model: string; inputPer1M: number; outputPer1M: number; } /** A formatter for token counts (injected so this module stays pure). */ export type TokenFormatter = (n: number) => string; /** Format a single model-row's cost string, mirroring the inline logic. */ export declare function formatModelCost(provider: string, estimatedCost: number): string; /** * Build the full `/stats` Markdown report. `currentProvider` controls * whether the total shows "free" (ollama) or a dollar figure. */ export declare function formatStatsReport(args: { totals: StatsTotals; breakdown: StatsModelRow[]; cache: StatsCache; pricing: PricingRow[]; currentProvider: string; fmt: TokenFormatter; impactLines?: string[]; }): string; /** * Extract every fenced code block body (the text inside ```…```) from a * string, mirroring the `/copy` loop. Language fences (```ts) are ignored — * only the body is captured. */ export declare function extractCodeBlocks(text: string): string[]; /** * Validate a 1-based block index against the available block list, as used * by `/copy `. Returns the 0-based index, or `null` when the index is * out of range (the caller shows an error). */ export declare function resolveBlockIndex(blockNum: number, blockCount: number): number | null; export interface FileChange { path: string; content: string; } /** * Extract file-change pairs from an assistant message, mirroring `/apply`. * Two patterns are tried in order: * 1. fence with a filename header: ```ts\nsrc/foo.ts\n``` * 2. fence with a `// File:` / `# Path:` comment header. * A path is only accepted when it contains a dot and no spaces. */ export declare function extractFileChanges(text: string): FileChange[]; /** Truncate a path for display: keep the last 37 chars, prefixed with “…”. */ export declare function shortPathForDisplay(path: string, max?: number): string; /** * Build a single diff-line summary for a file change, mirroring `/apply`. * Returns `null` when `existingContent` is empty (a CREATE), otherwise a * MODIFY line with the line-count delta. */ export declare function formatApplyDiffLine(change: { path: string; content: string; }, existingContent: string): string[]; /** * Parse `key=value` tokens (as used by `/mcp prompt [k=v...]`) * into a record. Tokens without an `=` (or with `=` at position 0) are * skipped. Mirrors the inline loop. */ export declare function parsePromptArgs(tokens: string[]): Record; /** Pluralise "tool"/"tools" based on the count. */ export declare function pluralTools(n: number): string; /** * Group a flat list of tools by `serverName`, preserving first-seen order. * Used by `/mcp` (default), `/mcp reload`, and the install report. */ export declare function groupToolsByServer(tools: T[]): Array<{ serverName: string; serverTools: T[]; }>; /** Format the `/mcp` default server/tool listing. */ export declare function formatMcpServerList(tools: T[], errors: Array<{ server: string; error: string; }>): string; /** Format the `/mcp reload` report. */ export declare function formatMcpReloadReport(toolCount: number, serverCount: number, errors: Array<{ server: string; error: string; }>): string; /** Format the `/mcp resources` listing. */ export declare function formatMcpResourcesList(groups: Array<{ serverName: string; resources: Array<{ uri: string; name?: string; mimeType?: string; description?: string; }>; }>): string; /** Format the `/mcp read` output for a list of resource contents. */ export declare function formatMcpResourceRead(uri: string, contents: Array<{ text?: string; blob?: string; mimeType?: string; }>): string; /** Format the `/mcp prompts` listing. */ export declare function formatMcpPromptsList(groups: Array<{ serverName: string; prompts: Array<{ name: string; description?: string; arguments?: Array<{ name: string; required?: boolean; }>; }>; }>): string; /** Format the `/mcp prompt` materialised output. */ export declare function formatMcpPromptResult(serverName: string, name: string, description: string | undefined, messages: Array<{ role: string; content?: { text?: string; }; }>): string; /** * Parse the `--days N` / `--days=N` flag from `/insights` args. Returns the * default (7) when absent or unparseable; clamps negatives to 0. */ export declare function parseInsightsDays(args: string[], fallback?: number): number; /** * Format a single cloud-session row for the `/cloud` picker, mirroring the * inline template: `title · date · N msg · [project?]`. */ export declare function formatCloudSessionLabel(s: { sessionId: string; sessionName?: string | null; updatedAt: string; messageCount: number; projectName?: string | null; }): string; /** * Format the `/me sync` result list. For both results `count` is 1 when the * profile moved and 0 when there was nothing to move (no local profile to * push; nothing new to pull). `describe` turns a failure into a sentence — * codeepCloud's describeSyncFailure, passed in so this module stays free of * the config it loads. */ export declare function formatMeSyncReport(pushed: SyncResult, pulled: SyncResult, describe: (reason: SyncFailure) => string): string; /** * Format what `/undo-all` did, one line per action. The results mix restored * files with actions that cannot be undone (a shell command), so a count of * them says nothing about how much was put back. */ export declare function formatUndoAllReport(result: { success: boolean; results: string[]; }): string; /** * Format the `/me learn` result. `updated` distinguishes "new facts written" * from "already covered"; `file` is the human-readable path. */ export declare function formatMeLearnResult(scope: 'global' | 'project', file: string, res: { updated: boolean; facts: string; }): string; /** Format the `/me init` result. */ export declare function formatMeInitResult(scope: 'global' | 'project', res: { created: boolean; path: string; }): string; /** Format the `/skills show` detail view from a skill bundle. */ export declare function formatSkillsShow(bundle: { name: string; description: string; source: string; body: string; }): string; /** Format the `/skills browse` empty-state message. */ export declare function formatSkillsBrowseEmpty(query: string): string; /** Format the `/skills publish` success message. */ export declare function formatSkillsPublishResult(slug: string, isPublic: boolean, owner: string | null | undefined): string;