import { type SkillSpec } from "./skill-parser.js"; import { type OnCapAction } from "./author-budget.js"; import { type CostModel, type UsageBreakdown } from "../util/cost.js"; /** * v0.5 §5 — Author loop entrypoint. * * Multi-turn state machine that drives a messenger conversation through the * five steps in §5.2-5.6: CLARIFY → DRAFT → SANDBOX_PROMPT (optional) → * AWAIT_CONFIRM → APPLY. Per-user, single in-flight draft, persisted at * `/.solosquad/sessions/.author-draft.json`. * * S3 stubs the real LLM behind a `ClaudeCaller` interface so unit tests * can drive the state machine deterministically. Production wiring happens * in S5 (when the goal-runner integration lands). * * Important invariant — every SKILL produced by this author loop has * `stateful: false` enforced before write. Validation runs after * serialization; if the validator rejects, the loop refuses to write and * surfaces the error. */ export type AuthorState = "CLARIFY" | "DRAFT" | "SANDBOX_PROMPT" | "AWAIT_CONFIRM" | "APPLIED" | "ABORTED"; export interface ClarifyAnswer { inputs?: string; outputs?: string; cadence?: string; } export interface AuthorDraft { skill_draft_id: string; user_id: string; org_slug: string; intent: string; team: string; /** Working name (kebab-case slug) — used for path. */ slug: string; /** Display name for SKILL.md `name` field. */ display_name: string; description: string; triggers_keyword: string[]; inputs: { required: string[]; optional: string[]; }; outputs: string[]; body_md: string; /** Optional spec-gate config — set when `loop_mode.kind: spec-gate`. */ spec_gate?: { spec_path: string; stop_when: string; }; /** Optional `workflow.yaml` stub if author flow elected a workflow chain. */ workflow_yaml?: string; /** Optional `goal.md` body (spec-gate only). */ goal_md?: string; state: AuthorState; /** Free-text trail of turns — diagnostics. */ history: { role: "user" | "pm"; text: string; ts: string; }[]; /** Sandbox dry-run output, set when SANDBOX_PROMPT completes. */ sandbox_preview?: { truncated: boolean; preview: string; full_path?: string; }; /** Last error message, if any. */ error?: string; created_at: string; updated_at: string; } export interface ClaudeCallResult { text: string; usage: UsageBreakdown; model: CostModel; } export interface ClaudeCallInput { step: string; prompt: string; model?: CostModel; } /** Injection point for LLM calls. S3 uses a fake; production wires real Claude. */ export interface ClaudeCaller { call(input: ClaudeCallInput): Promise; } export interface AuthorLoopOpts { workspace: string; orgSlug: string; userId: string; /** Latest user turn (single message). */ userMessage: string; /** Initial intent on first turn — ignored after first call. */ intent?: string; /** LLM stub injection. */ claude: ClaudeCaller; /** Budget caps from workspace.yaml `author.budget`. */ budget?: { perCallUsd?: number; dailyUsd?: number; weeklyUsd?: number; onCapAction?: OnCapAction; }; /** When true (default), validates produced SKILL.md before APPLY. */ validate?: boolean; } export interface AuthorLoopResult { state: AuthorState; /** Message PM should send back to the user. */ reply: string; draft: AuthorDraft; /** Set when state === "APPLIED" — path to the new SKILL.md. */ applied_path?: string; } export interface ApplyDraftInput { workspace: string; orgSlug: string; draft: AuthorDraft; /** Override default destination (for tests / manual installs). */ destination?: string; /** * v0.6 §3.4 — `frontmatter-only` mode patches an *existing* SKILL.md's * frontmatter in place; body bytes are preserved. Used by the freq-keyword * miner to add `triggers.keyword` entries without touching the body. * Default `full` writes the SKILL from scratch (v0.5 author loop path). */ mode?: "full" | "frontmatter-only"; } export interface ApplyDraftResult { skill_path: string; spec: SkillSpec; workflow_path?: string; goal_path?: string; } export declare function loadDraft(workspace: string, orgSlug: string, userId: string): AuthorDraft | null; export declare function clearDraft(workspace: string, orgSlug: string, userId: string): void; export declare function runAuthorLoop(opts: AuthorLoopOpts): Promise; export declare function applyDraft(input: ApplyDraftInput): ApplyDraftResult;