#!/usr/bin/env node import { type Harness, type InteractiveSpec, type PersonaMount, type PersonaSelection, type SidecarMdMode } from '@agentworkforce/persona-kit'; import { type FastLaunch } from './fast-launch.js'; import { type PickCandidate } from './persona-picker.js'; import { type TuiCandidate } from './persona-tui.js'; export declare function perfMark(label: string): void; export declare const CLI_VERSION: string; export declare const CREATE_SELECTOR = "persona-maker"; /** * Resolve the `` placeholder used in persona systemPrompts. * Personas embed `` inside example install commands (e.g. * `npx prpm install --as `) so the docstring stays * harness-agnostic in source. The active harness is known at selection * time, so swap it in here to give the model a concrete command. * * Exported for test coverage. Only `` is resolved today; other * angle-bracketed tokens in the prompt (e.g. ``, ``, * ``) are deliberately left as LLM-facing placeholders. */ export declare function resolveSystemPromptPlaceholders(prompt: string, harness: Harness): string; /** * Resolve the env-configured drift-check interval. * * `parseCheckInterval` returns three things we must keep distinct: * - a number → an explicit interval (incl. `0` = always) * - `null` → the user explicitly disabled checks (`never`/`off`/`false`) * - `undefined` → unset or unparseable → fall back to the 24h default * * A plain `?? DEFAULT` is wrong here because `??` also coalesces `null`, * silently re-enabling checks the user asked to turn off. */ export declare function resolveEnvCheckIntervalMs(): number | null; interface SkillCacheLockHandle { release(): void; } /** * Acquire a per-fingerprint advisory lock so two concurrent * `agentworkforce agent ` launches don't both miss the cache * and install into the same dir simultaneously (which can interleave * `prpm install` writes and corrupt the cache). * * The lock is a sibling file `.lock` (NOT inside the cache dir, so * a `--refresh-skills` wipe of the dir can't delete a live lock). Created * with `wx` (O_EXCL) for atomicity. A held lock is stolen only if its holder * is dead or its heartbeat is stale. If we can't get it within the wait * budget we return null and the caller proceeds unlocked (best-effort — * never hang a launch on locking). */ export declare function acquireSkillCacheLock(skillCacheDir: string): Promise; /** * Remove every `--agent ` pair from a harness argv. Used on the non-mount * opencode path where we cannot safely materialize the persona's * opencode.json (it would land in the user's real repo), so we fall back to * launching opencode without a persona-specific agent selection. * * Strips all occurrences rather than just the first — the current producer * (the opencode branch in persona-kit) emits exactly one pair, so both * behaviors are equivalent today, but "remove all" is idempotent and safer if * a future caller ever appends a second `--agent` for any reason. A trailing * `--agent` with no following value is preserved so the malformed argv * surfaces at the harness rather than getting silently swallowed here. */ export declare function stripAgentFlag(args: readonly string[]): string[]; /** * Validate that a configFile's relative path is safe to resolve under a * sandbox/session directory. Rejects absolute paths and any segment equal to * `..` so a malformed or adversarial persona cannot escape the mount via * `join()` and overwrite files elsewhere. Called at materialization time so * the failure surfaces with a clear path before any disk write happens. */ export declare function assertSafeRelativePath(relPath: string): void; /** Patterns hidden from an interactive claude session by the sandbox mount. * Applied by `@relayfile/local-mount` with gitignore semantics, so bare names * match at any depth in the project tree (e.g. `.claude` hides both * `./.claude/` and `./packages/foo/.claude/`). */ export declare const CLEAN_IGNORED_PATTERNS: readonly ["CLAUDE.md", "CLAUDE.local.md", ".claude", ".mcp.json", "AGENTS.md"]; /** * Skill-install artifacts that should never be copied into the mount nor * synced back to the real repo. Applied to non-claude interactive sessions * that rely on the mount to keep `npx skills add` / `npx prpm install` * writes out of the user's project tree. Covers every per-provider output * root that skill.sh / prpm scatter into on install — missing one here * re-introduces repo pollution, so this list is deliberately superset-y. * Claude sessions use `installRoot` for out-of-repo staging instead, so * these patterns don't apply there. */ export declare const SKILL_INSTALL_IGNORED_PATTERNS: readonly [".agents", ".claude/skills", ".factory/skills", ".grok/skills", ".kiro/skills", "skills", ".opencode", ".skills", "prpm.lock", "skills-lock.json", "AGENTS.md"]; export interface RelayfileMountPatterns { ignoredPatterns: string[]; readonlyPatterns: string[]; } export declare function buildRelayfileMountPatterns(input: { projectDir: string; personaId: string; harness: Harness; mount?: PersonaMount; configFilePaths?: readonly string[]; }): RelayfileMountPatterns; /** * Build the block appended to `/.git/info/exclude` so untracked-and- * hidden files (e.g. `.claude/skills/` materialized by skill installs, or * `opencode.json` written by `onBeforeLaunch`) don't surface under * "Untracked files" in `git status` inside the mount. * * Exported pure helper for unit-testing the formatting separately from the * disk-writing wrapper below. */ export declare function buildMountGitExcludeBlock(patterns: readonly string[]): string; /** * Configure the mount's per-session `.git` so paths hidden by the relayfile * mount don't show as deleted/untracked inside the sandbox. * * - Appends `patterns` to `.git/info/exclude` so untracked-and-hidden files * don't show up under "Untracked files". * - Marks every tracked file matching one of the patterns as skip-worktree * so `git status` doesn't list it as deleted (the work tree omits it on * purpose, not by user action). * * Best-effort: if the mount has no `.git` (project isn't a repo, or * `includeGit: false`) or any git command fails, returns silently. The * mount's `.git` is per-session and `noSyncBack` per relayfile 0.6+, so * writes here are sandboxed and never leak to the user's main checkout. */ export declare function configureGitForMount(mountDir: string, patterns: readonly string[]): void; /** * Persona-supplied sidecar markdown materialized into a sandbox mount. * Pure data carrier — `runInteractive` translates it into the on-disk * write inside `onBeforeLaunch` (mount path) and warns/skips when the * harness has no mount (`--install-in-repo`). */ export interface ResolvedSidecar { /** Filename inside the mount: `CLAUDE.md` (claude) or `AGENTS.md` (opencode/codex/grok). */ mountFile: 'CLAUDE.md' | 'AGENTS.md'; /** Persona-author content. Already inlined for built-ins; read from disk for local. */ personaContent: string; mode: SidecarMdMode; } /** * Resolve the sidecar for a given selection + harness, returning the * persona-author content the runtime should materialize into the mount. * Returns `{}` when no sidecar applies (no path/content set, or harness * doesn't support sidecar files at all). Read errors surface as a warning * string so the caller can drop the sidecar gracefully rather than * failing the whole session. */ export declare function loadSidecarForSelection(selection: PersonaSelection): { sidecar?: ResolvedSidecar; warning?: string; }; /** * Compute the bytes to write into the mount for a sidecar. In `extend` * mode, prepends the user's real-cwd file (if any) joined to the persona * content with `\n\n---\n\n`. Pure — exposed for unit tests. */ export declare function buildSidecarBody(sidecar: ResolvedSidecar, realCwdDir: string): string; /** * Decide whether to run the interactive session inside a * `@relayfile/local-mount` sandbox. * * All interactive harnesses default to the mount. * The mount hides CLAUDE.md / .claude / .mcp.json (claude) or the * skill-install patterns + AGENTS.md (codex / opencode / grok) so * persona-supplied sidecars and any per-session writes stay sandboxed and * don't leak into the user's real repo. `--install-in-repo` is the single * opt-out that disengages the mount across all harnesses. * * Pure — no side effects, trivially testable. */ export declare function decideCleanMode(harness: Harness, installInRepo?: boolean): { useClean: boolean; }; export declare function formatSandboxMountReadyMessage(mountDir: string, handle: { initialMountDurationMs?: number; initialFileCount?: number; }): string; export declare function buildSpawnSummary(input: { harness: Harness; model: string; spec: Pick; permissions?: PersonaSelection['permissions']; useClean?: boolean; }): string[]; export interface PersonaInstallArgs { source: string; personaIds: string[]; overwrite: boolean; } export declare function parseInstallArgs(args: readonly string[]): PersonaInstallArgs; /** * Manage a session the fast path already spawned (see fast-launch.ts): the * harness child is running inside a claimed warm mount, and this function — * loaded while the harness TUI is still booting — owns everything else: * reattach the mount with the previous session's autosync state, reconcile * to catch up with repo changes made while the mount sat warm, start launch * metadata, and on child exit run the same drain/sync-back/re-warm flow as * a full launch. * * Also re-derives the harness spec from live persona resolution and compares * it against the plan's argv. The plan's digests should make a mismatch * impossible; if one slips through, the session keeps running (killing it * would eat the user's terminal for a stale-plan technicality) but the plan * is deleted and the mismatch reported so the next launch re-resolves. */ export declare function resumeFastSession(fast: FastLaunch): Promise; export interface ImproverPatch { path: string; op: 'set' | 'append'; value: unknown; } export interface ImproverProposal { id: string; summary: string; rationale: string; patches: ImproverPatch[]; } export interface ImproverProposalsFile { personaId: string; personaFilePath: string; transcriptPath: string; proposals: ImproverProposal[]; } export declare function parseProposals(raw: string): ImproverProposalsFile; /** * Read a single-character choice from stdin synchronously, looping on * invalid input. Empty Enter (no character) returns the first option in * `valid` — callers should put the safe / default-no answer first. * * Test seam: callers can inject `read` so the prompt is exercisable * without a real TTY (mirrors `promptYesNoSync`). */ export declare function readSingleCharChoice(prompt: string, valid: readonly string[], opts?: { write?: (chunk: string) => void; read?: () => string | undefined; }): string; /** * Apply accepted patches to the persona JSON on disk. Reads, mutates the * parsed object, writes back with two-space indent + trailing newline * (matches existing /personas style). Throws on unwriteable file or * unsupported patch op/path resolution. */ export declare function applyAcceptedPatches(personaFilePath: string, accepted: readonly ImproverProposal[]): void; /** * Enumerate personas for the interactive TUI. Source label mirrors the cascade * shown by `agentworkforce list` so the picker tells the user *where* a * persona is coming from (cwd, user, dir:n, library) without a separate * lookup. */ export declare function buildTuiCandidates(): TuiCandidate[]; /** * Enumerate persona candidates for the picker. Local overrides win over the * built-in catalog when ids collide; the picker only needs the projection * fields ({@link PickCandidate}), not full specs. */ export declare function buildPickCandidates(): PickCandidate[]; /** * Synchronous y/n prompt over /dev/tty-equivalent stdin. Default is "no" on * empty input or non-y answer. Used by `pick` when the picker reports * no-match in an interactive session. * * Test seam: callers can inject `read` so the prompt path is exercisable * without a real TTY. */ export declare function promptYesNoSync(question: string, opts?: { isTTY?: boolean; write?: (chunk: string) => void; read?: () => string | undefined; }): boolean; export declare function main(): Promise; export interface AgentFlags { installInRepo: boolean; noLaunchMetadata: boolean; dryRun: boolean; /** Bypass the persistent skill-install cache for this launch. */ noSkillCache: boolean; /** Force a fresh install even if the cache entry exists (rebuilds it in place). */ refreshSkills: boolean; /** Force an upstream drift check this launch regardless of the TTL. */ checkUpstream: boolean; /** Skip the upstream drift check this launch regardless of the TTL. */ noCheckUpstream: boolean; } export interface CreateFlags extends AgentFlags { saveInDirectory?: string; saveDefault: boolean; } export declare function parseAgentArgs(args: readonly string[]): { flags: AgentFlags; positional: string[]; }; export declare function parseCreateArgs(args: readonly string[]): { flags: CreateFlags; selector: string; inputValues: Record; }; export {}; //# sourceMappingURL=cli-impl.d.ts.map