import type { CliId, ResumableSession } from '../adapters/cli/types.js';
import { validateWorkingDir } from './working-dir.js';
import { type AdoptableSession } from './session-discovery.js';
import { type ZellijAdoptableSession } from './zellij-adopt-discovery.js';
import { type CodexAppThreadSummary } from '../services/codex-app-threads.js';
import type { LarkMessage } from '../types.js';
import type { DaemonSession } from './types.js';
export declare const DAEMON_COMMANDS: Set<string>;
/**
 * Daemon commands that act on the chat itself rather than opening a
 * conversation. `/group` (`/g`) just creates a Lark group and replies once —
 * no follow-up turns, no CLI worker. The new-topic spawn path normally
 * pre-creates a sessionStore record so a command can attach state and keep
 * card buttons routable, but for these that record is a phantom conversation
 * that pollutes the dashboard's session list. Handle them without a session.
 */
export declare const SESSIONLESS_DAEMON_COMMANDS: Set<string>;
/**
 * Slash commands that are forwarded verbatim to the underlying CLI (e.g.
 * Claude Code's `/compact`, `/model`, `/usage`). The daemon does NOT handle
 * these — it just relays them to the worker via a raw_input IPC message,
 * bypassing the normal prompt-wrapping and bracketed-paste path so the CLI's
 * own slash-command parser sees them.
 */
export declare const PASSTHROUGH_COMMANDS: Set<string>;
export declare function resolveAdapterDefaultPassthroughCommands(larkAppId?: string): string[];
/**
 * Effective passthrough set for a bot: the fixed {@link PASSTHROUGH_COMMANDS}
 * plus adapter-scoped defaults and the bot's `customPassthroughCommands`
 * (bots.json). Entries that would shadow a botmux daemon command are dropped —
 * daemon commands must keep their daemon semantics, and passthrough is checked
 * BEFORE DAEMON_COMMANDS in the router, so an un-filtered custom `/status`
 * would hijack the daemon's own.
 * Unknown / no bot → falls back to the builtin set unchanged.
 */
export declare function resolvePassthroughCommands(larkAppId?: string): Set<string>;
export interface SlashCommandInvocation {
    cmd: string;
    content: string;
}
export { validateWorkingDir };
/**
 * Resolve a non-numeric `/repo <arg>` into a concrete repo path + display name.
 * `arg` is either a path (absolute or relative) or a first-level project name
 * under one of the bot's scan dirs — letting the user skip the selection card.
 *
 * Resolution:
 *   1. Build candidate absolute paths — absolute / `~` taken as-is; relative or
 *      bare names resolved against each scan dir, then the daemon cwd (mirrors
 *      how the card's project list is rooted).
 *   2. Prefer a candidate matching a scanned git project (carries a branch label).
 *   3. For a bare name, also match a scanned project by basename (covers projects
 *      nested deeper than the scan-dir top level).
 *   4. Fall back to any existing directory — lenient like `/cd`, whose trust model
 *      is "owner explicitly chose a dir"; the CLI already runs with full FS access.
 * Returns null when nothing resolves to an existing directory.
 */
export declare function resolveRepoSelection(repoArg: string, scanDirs: string[]): {
    path: string;
    displayName: string;
} | null;
/**
 * Parse a force-topic invocation: `/t [prompt]` or `/topic [prompt]`.
 *
 * This is a routing meta-command, distinct from `parseSlashCommandInvocation`
 * (which routes to daemon command handlers). The match conditions are
 * deliberately tighter than the regular slash parser:
 *
 * - exact-prefix match (`/t` / `/topic`, case-insensitive); `/tea` / `/topical`
 *   must NOT match, otherwise we'd false-trigger on common /-prefixed words.
 * - tolerates leading whitespace (mention-stripping can leave a space).
 * - prompt is whatever follows the prefix (verbatim, including newlines).
 * - `/t` alone (no args) is allowed → empty prompt; the user can fill it in
 *   while the repo selection card is still pending.
 *
 * Returns null for anything else, so callers can fall through to the regular
 * `parseSlashCommandInvocation` / message-handling path.
 */
export declare function parseForceTopicInvocation(content: string): {
    prompt: string;
} | null;
/** Parse a user-authored slash command after leading @mentions have already
 *  been stripped. Messages that look like command examples or command lists
 *  are intentionally left for the CLI instead of being intercepted by the
 *  daemon; otherwise discussion text such as `/adopt <pane>` can accidentally
 *  trigger real daemon actions. */
export declare function parseSlashCommandInvocation(content: string): SlashCommandInvocation | null;
export interface CommandHandlerDeps {
    activeSessions: Map<string, DaemonSession>;
    sessionReply: (rootId: string, content: string, msgType?: string, larkAppId?: string, turnId?: string) => Promise<string>;
    getActiveCount: () => number;
    lastRepoScan: Map<string, import('../services/project-scanner.js').ProjectInfo[]>;
}
/**
 * Handle `/card` (operator-only). Resolves the active session itself, so off/on
 * work WITHOUT one -- they only toggle the per-chat `noCardChats` config. A
 * summon (show/bare) needs a live session.
 *
 * off  -> suppress the live streaming card for this chat (add to noCardChats);
 *         status falls back to master's pending-card morph.
 * on   -> restore cards for this chat (remove from noCardChats).
 * ''/show -> summon a live card. privateCard -> private ephemeral snapshot
 *         (fail closed on non-group); otherwise a group-visible live card.
 * off/on also clear `streamingCardForced` so a prior summon does not
 * short-circuit `streamingCardDisabled()`.
 */
export declare function handleCardCommand(rootId: string, larkAppId: string, chatId: string, senderOpenId: string | undefined, content: string, deps: CommandHandlerDeps): Promise<void>;
/**
 * Handle `/term` (operator-only) — the slash-command twin of the "🔑 获取操作链接"
 * card button. Privately hands the operator a writable (token-bearing) terminal
 * card: an in-chat visible-to-you ephemeral card in plain groups, auto-falling back
 * to a DM in topic / p2p chats. The link rides only that private channel — never the
 * group. Gated identically to /card (`canOperate`), and strictly needs a live
 * session whose terminal is up. Routed for both the new-topic path (daemon.ts) and
 * the existing-session switch below.
 */
export declare function handleTermLinkCommand(rootId: string, larkAppId: string, chatId: string, senderOpenId: string | undefined, _content: string, deps: CommandHandlerDeps): Promise<void>;
export declare function handleCommand(cmd: string, rootId: string, message: LarkMessage, deps: CommandHandlerDeps, larkAppId?: string): Promise<void>;
export declare function startCodexAppThreadSession(thread: CodexAppThreadSummary, ds: DaemonSession, deps: CommandHandlerDeps, larkAppId?: string): Promise<void>;
export declare function startAdoptSession(target: AdoptableSession | ZellijAdoptableSession, ds: DaemonSession, deps: CommandHandlerDeps, larkAppId?: string): Promise<void>;
/** Discover the sessions resumable from disk for `cliId`, excluding any whose
 *  CLI-native id is already live in a botmux session (so a session botmux
 *  already runs isn't offered for re-import). Returns [] when the adapter has
 *  no on-disk store. */
export declare function discoverResumableSessionsForBot(cliId: CliId, cliPathOverride: string | undefined, activeSessions: Map<string, DaemonSession>, limit?: number): Promise<ResumableSession[]>;
/** Import (resume) a stored session into the current topic: re-spawn the bot's
 *  CLI via `--resume <cliSessionId>` in `cwd`. Mirrors the manual resume path —
 *  the worker owns the CLI (NOT an observe-adopt), so no `adoptedFrom` is set. */
export declare function startResumeImportSession(target: ResumableSession, ds: DaemonSession, deps: CommandHandlerDeps, larkAppId?: string): Promise<void>;
//# sourceMappingURL=command-handler.d.ts.map