/** * v0.3.0 — typed wrapper around `claude --print` subprocess. * * Replaces the single-shot `claude-runner.ts` for Chief session use. The * scheduler still uses `claude-runner.ts` for stateless cron prompts. * * Design (per docs/plan/v0.3-pm-mode-orchestration.md §3.2 + PoC #1/#2): * - Chief session uses pre-generated `--session-id ` + `--resume` * - Always `--output-format stream-json --verbose`; the user message is fed as * PLAIN TEXT over stdin (v1.3.10 — `--input-format stream-json` was dropped * because claude 2.1.x ignores `--add-dir` under stream-json input) * - `ClaudeProcessFactory` interface lets us swap a Fake impl for unit tests * - Real impl handles Windows shell quoting (DEP0190-safe) */ export type ContentBlock = { type: "text"; text: string; } | { type: "tool_result"; tool_use_id: string; content: string | ContentBlock[]; is_error?: boolean; } | { type: "tool_use"; id: string; name: string; input: Record; } | { type: "thinking"; thinking: string; signature?: string; }; export type StreamJsonInputLine = { type: "user"; message: { role: "user"; content: string | ContentBlock[]; }; }; export interface AssistantMessage { model?: string; id?: string; role: "assistant"; content: ContentBlock[]; stop_reason?: string | null; usage?: TokenUsage; } export interface TokenUsage { input_tokens?: number; output_tokens?: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number; } export interface RateLimitInfo { status: "allowed" | "warning" | "exceeded"; resetsAt?: number; rateLimitType?: string; overageStatus?: string; overageDisabledReason?: string; isUsingOverage?: boolean; } export type StreamJsonOutputLine = { type: "system"; subtype: "init"; session_id: string; cwd: string; tools: string[]; agents?: string[]; } | { type: "system"; subtype: "task_started"; task_id: string; tool_use_id: string; task_type: "local_agent" | string; description: string; prompt: string; session_id: string; uuid: string; } | { type: "system"; subtype: "task_notification"; task_id: string; tool_use_id: string; status: "completed" | "failed" | "cancelled"; summary: string; output_file?: string; usage?: { total_tokens?: number; tool_uses?: number; duration_ms?: number; }; uuid: string; session_id: string; } | { type: "assistant"; message: AssistantMessage; session_id: string; uuid?: string; } | { type: "user"; message: { role: "user"; content: ContentBlock[]; }; parent_tool_use_id: string | null; session_id: string; uuid?: string; } | { type: "rate_limit_event"; rate_limit_info: RateLimitInfo; session_id: string; uuid?: string; } | { type: "result"; subtype: "success" | "error_max_turns" | "error_during_execution"; is_error: boolean; result: string; session_id: string; total_cost_usd: number; usage?: TokenUsage; stop_reason?: string | null; terminal_reason?: string; duration_ms?: number; } | { type: string; [k: string]: unknown; }; export interface ClaudeInvocation { sessionId: string; cwd: string; /** true on every call after the session has been created. */ resume: boolean; /** User message stream. v1.3.10: rendered to PLAIN TEXT and written to stdin * (not stream-json NDJSON) so `--add-dir` is honored. Chief sends one line. */ input: AsyncIterable; /** Default true for PM session — improves cross-call prompt-cache hit rate. */ excludeDynamicSystemPromptSections?: boolean; /** Cap cost per call. Workspace default. */ maxBudgetUsd?: number; /** Default true for PM session so users get realtime partial replies. */ includePartialMessages?: boolean; /** Append to the default system prompt. */ appendSystemPrompt?: string; /** Hard timeout in ms (abort the child if exceeded). */ timeoutMs?: number; /** * v0.8.2 §4.2 — Claude Code `--allowed-tools` / `--disallowed-tools` * passthrough. When omitted, the CLI's defaults apply (broad permissions — * legacy behavior). The PM-session caller (`chief-runner.ts`) keeps these * unset; per-spawn engineering tasks set them via `applyDevPermissions()`. */ allowedTools?: string[]; disallowedTools?: string[]; /** * v0.8.2 §4.2 — pre-check bash allowlist / denylist. Used by the SoloSquad * wrapper around Bash tool invocations to reject commands at the *bot* layer * before they reach Claude Code (because Claude Code does not yet expose a * native bash allowlist flag). */ bashAllowlist?: string[]; bashDenylist?: string[]; /** * v1.2.9 §E — Claude Code `--permission-mode`. Controls headless tool * approval. Unset ⇒ CLI default, where a tool needing approval (Write / * Edit / Bash) PROMPTS — which HANGS in non-interactive (`--print`) mode * since there's no TTY to answer. Chief-runner sets `acceptEdits` when dev * mode is ON so file edits + allow-listed Bash run without a prompt; OFF * keeps it unset and instead denies Bash/Edit/Write via `disallowedTools` * (deny removes the tool → no hang). */ permissionMode?: "default" | "acceptEdits" | "plan" | "bypassPermissions"; /** * v1.2.9 §E — path to a Claude Code settings file (`--settings`). Used to * inject the PreToolUse Bash deny hook in dev-ON mode (blocks git push / * pr-merge / pr-close even in compound commands, which CLI deny can't). * Merges with the user's own settings as an additive layer. */ settingsPath?: string; /** * v1.2.7 §A.6 — additional working directories the Claude session can * read/write outside of `cwd`. Maps to `claude --add-dir * ...`. Used by chief-runner to grant the bot's spawn access * to every repo registered under `/repositories/` — without * this, Chief operating from `cwd=` reports "no access" to * repos at `C:\Dev\` etc. */ addDirs?: string[]; /** * v1.3.0 Part A — extra environment variables merged into the spawned * `claude` process env (on top of `process.env`). Carries the dev-confirm * gate context (pending dir, org, user, timeout, protected branches) that the * PreToolUse hook reads. Empty/undefined ⇒ child inherits the bot env as-is. */ extraEnv?: Record; } export interface ClaudeInvocationResult { exitCode: number | null; signal: NodeJS.Signals | null; stdoutLines: StreamJsonOutputLine[]; stderr: string; /** stdout content that failed to JSON-parse — surfaced to callers in case the * "Not logged in" sentinel arrives instead of stream-json. */ unparsedStdout: string; durationMs: number; } export interface ClaudeStreamingResult { /** Hot async iterator over parsed stream-json lines. */ lines: AsyncIterable; /** Abort the child process. */ abort: () => void; /** Resolves after child exit (drain stderr inside). */ done: Promise<{ exitCode: number | null; signal: NodeJS.Signals | null; stderr: string; unparsedStdout: string; }>; } export interface AuthStatus { loggedIn: boolean; authMethod?: string; apiProvider?: string; email?: string; orgId?: string; orgName?: string; subscriptionType?: string; } export interface ClaudeProcessFactory { /** Run a single PM/specialist invocation, accumulate all output, resolve on exit. */ invoke(inv: ClaudeInvocation): Promise; /** Same as invoke but exposes a hot async iterator for real-time forwarding. */ invokeStreaming(inv: ClaudeInvocation): ClaudeStreamingResult; /** Call `claude auth status --json`. Used at bot startup + on auth-fail recovery. */ authStatus(): Promise; } /** Sentinel produced by Claude Code itself when the user is not signed in. */ export declare const NOT_LOGGED_IN_PATTERN: RegExp; /** stderr emitted when `--resume ` points at a session that no longer exists. */ export declare const SESSION_NOT_FOUND_PATTERN: RegExp; /** stderr emitted when `--resume ` is malformed. */ export declare const INVALID_SESSION_ID_PATTERN: RegExp; /** v1.3.11 §fix — write the (multi-line) system prompt to a temp file and pass * `--append-system-prompt-file`. On Windows the bot spawns claude with * `shell: true`, building a command STRING; a newline inside the * `--append-system-prompt` value breaks cmd.exe parsing and drops every flag * after it (including `--add-dir`), so Chief lost access to registered repos. * A file keeps newlines off the command line entirely. Returns the path (or * undefined when there is no prompt); the caller deletes it after the run. */ export declare function writeSystemPromptFile(prompt: string | undefined): string | undefined; export declare function buildArgs(inv: ClaudeInvocation, appendSystemPromptFile?: string): string[]; /** * v0.8.2 §4.2 — Bash command pre-check. Returns null when the command is * permitted; returns a rejection reason string otherwise. Exposed so the * PM-runner / spawn caller can intercept Bash tool_use blocks emitted by * the spawn and short-circuit them with a tool_result. * * Matching rules: * - DENY wins. Any substring match on `bashDenylist` rejects the command. * - If `bashAllowlist` is empty, no allow check happens (back-compat: PM * session retains full Bash). If non-empty, the command's *first * non-whitespace token sequence* must start with one of the entries (e.g. * entry `"gh pr create"` matches the command `gh pr create --title foo`). * * `cmd` should be the literal string the SKILL passed to the Bash tool. */ export declare function checkBashCommand(cmd: string, bashAllowlist?: readonly string[], bashDenylist?: readonly string[]): { ok: true; } | { ok: false; reason: string; matched: string; }; /** Parse a single stream-json line. Returns null on JSON parse failure. */ export declare function parseLine(raw: string): StreamJsonOutputLine | null; export declare class RealClaudeProcessFactory implements ClaudeProcessFactory { invoke(inv: ClaudeInvocation): Promise; invokeStreaming(inv: ClaudeInvocation): ClaudeStreamingResult; authStatus(): Promise; } /** Extract the plain-text body of a user input line. v1.3.10: we feed claude * plain stdin (not stream-json) so `--add-dir` is honored, so each line is * rendered to text. String content passes through; ContentBlock[] concatenates * its text blocks (best-effort — Chief only ever sends string content). */ export declare function inputLineToText(line: StreamJsonInputLine): string; /** Convenience: create a single-line input iterable from a user text. */ export declare function singleUserMessage(text: string): AsyncIterable;