@denisvieiradev/gitwise-core 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +54 -19
- package/dist/index.js +521 -155
- package/dist/index.js.map +1 -1
- package/dist/testing/index.d.ts +3 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types-BURqSokx.d.ts +36 -0
- package/package.json +2 -2
- package/dist/types-DnMpR1qf.d.ts +0 -29
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/errors.ts","../src/infra/logger.ts","../src/infra/filesystem.ts","../src/infra/git.ts","../src/infra/github.ts","../src/infra/env.ts","../src/infra/transaction.ts","../src/infra/lockfile.ts","../src/providers/claude-code.ts","../src/config/types.ts","../src/config/merge.ts","../src/config/user.ts","../src/config/repo.ts","../src/template/loader.ts","../src/template/interpolate.ts","../src/providers/model-router.ts","../src/commands/commit.ts","../src/commands/review.ts","../src/commands/pr.ts","../src/commands/release.ts","../src/strategies/release.ts","../src/commands/release-plan.ts","../src/providers/anthropic.ts","../src/providers/factory.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@denisvieiradev/gitwise-core\",\n \"version\": \"1.1.0\",\n \"description\": \"Shared logic for gitwise: non-interactive commit/review/pr/release commands, LLM providers, git/github primitives, prompt templates.\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./testing\": {\n \"types\": \"./dist/testing/index.d.ts\",\n \"import\": \"./dist/testing/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"node --experimental-vm-modules ../../node_modules/.bin/jest --passWithNoTests\",\n \"lint\": \"tsc --noEmit\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"keywords\": [\n \"gitwise\",\n \"git\",\n \"ai\",\n \"claude\",\n \"commit\",\n \"pull-request\",\n \"release\",\n \"code-review\"\n ],\n \"author\": \"Denis Vieira <denisvieira05@gmail.com> (https://github.com/denisvieiradev)\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/denisvieiradev/gitwise.git\",\n \"directory\": \"packages/core\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/denisvieiradev/gitwise/issues\"\n },\n \"homepage\": \"https://github.com/denisvieiradev/gitwise#readme\",\n \"engines\": {\n \"node\": \">=22.12.0\"\n },\n \"dependencies\": {\n \"@anthropic-ai/sdk\": \"^0.109.0\"\n }\n}\n","export const EXIT_CODES: Readonly<Record<string, number>> = Object.freeze({\n OK: 0,\n UNKNOWN: 1,\n NOTHING_STAGED: 10,\n INVALID_INTENT: 11,\n GIT_FAILED: 20,\n GH_FAILED: 21,\n REPO_STATE_INVALID: 22,\n API_FAILED: 30,\n API_KEY_MISSING: 31,\n API_RATE_LIMITED: 32,\n USER_ABORT: 40,\n CONFIG_INVALID: 50,\n RELEASE_PLAN_STALE: 60,\n RELEASE_BRANCH_CONFLICT: 61,\n SENSITIVE_FILE_BLOCKED: 70,\n REPO_LOCKED: 80,\n ROLLBACK_PARTIAL: 81,\n});\n\nexport interface GitwiseErrorArgs {\n code: string;\n message: string;\n exitCode?: number;\n cause?: unknown;\n details?: Record<string, unknown>;\n}\n\nexport class GitwiseError extends Error {\n readonly code: string;\n readonly exitCode: number;\n override readonly cause?: unknown;\n readonly details?: Record<string, unknown>;\n\n constructor(args: GitwiseErrorArgs) {\n super(args.message);\n this.name = \"GitwiseError\";\n this.code = args.code;\n this.exitCode = args.exitCode ?? EXIT_CODES[args.code] ?? 1;\n this.cause = args.cause;\n this.details = args.details;\n }\n\n toJSON(): {\n name: string;\n code: string;\n exitCode: number;\n message: string;\n details?: Record<string, unknown>;\n } {\n return {\n name: this.name,\n code: this.code,\n exitCode: this.exitCode,\n message: this.message,\n ...(this.details !== undefined ? { details: this.details } : {}),\n };\n }\n}\n\nexport function wrapError(err: unknown): GitwiseError {\n if (err instanceof GitwiseError) return err;\n if (err instanceof Error) {\n return new GitwiseError({\n code: \"UNKNOWN\",\n message: err.message,\n cause: err,\n });\n }\n return new GitwiseError({\n code: \"UNKNOWN\",\n message: typeof err === \"string\" ? err : \"Unknown error\",\n cause: err,\n });\n}\n","let verboseEnabled = false;\n\n// Support GITWISE_DEBUG=1 env variable to enable debug output\nif (process.env[\"GITWISE_DEBUG\"] === \"1\") {\n verboseEnabled = true;\n}\n\nexport function setVerbose(enabled: boolean): void {\n verboseEnabled = enabled;\n}\n\nexport function isVerbose(): boolean {\n return verboseEnabled;\n}\n\nexport function info(message: string, context?: Record<string, unknown>): void {\n if (context) {\n console.log(message, context);\n } else {\n console.log(message);\n }\n}\n\nexport function error(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (context) {\n console.error(message, context);\n } else {\n console.error(message);\n }\n}\n\nexport function warn(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (context) {\n console.warn(message, context);\n } else {\n console.warn(message);\n }\n}\n\nexport function debug(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (!verboseEnabled) return;\n if (context) {\n process.stderr.write(`[debug] ${message} ${JSON.stringify(context)}\\n`);\n } else {\n process.stderr.write(`[debug] ${message}\\n`);\n }\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T> {\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\nexport async function writeJSON<T>(filePath: string, data: T): Promise<void> {\n await ensureDir(dirname(filePath));\n const content = JSON.stringify(data, null, 2) + \"\\n\";\n await writeFile(filePath, content, \"utf-8\");\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { debug } from \"./logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\nconst GIT_TIMEOUT_MS = 30_000;\nconst GIT_MAX_BUFFER = 10 * 1024 * 1024;\n\ninterface ExecResult {\n stdout: string;\n stderr: string;\n}\n\nfunction execStderr(err: unknown): string | undefined {\n const stderr = (err as { stderr?: unknown } | null)?.stderr;\n if (typeof stderr === \"string\" && stderr.length > 0) return stderr;\n return undefined;\n}\n\nasync function run(args: string[], cwd: string): Promise<string> {\n debug(\"git command\", { args, cwd });\n try {\n const result: ExecResult = await exec(\"git\", args, { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER });\n return result.stdout.trim();\n } catch (err: unknown) {\n if (err instanceof Error && \"killed\" in err && (err as { killed: boolean }).killed) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Git command timed out after ${GIT_TIMEOUT_MS / 1000}s: git ${args.join(\" \")}`,\n cause: err,\n details: { command: `git ${args.join(\" \")}`, timedOut: true },\n });\n }\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: {\n command: `git ${args.join(\" \")}`,\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n}\n\nexport async function getBranch(cwd: string): Promise<string> {\n return run([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n}\n\nexport async function createBranch(\n cwd: string,\n branchName: string,\n startPoint?: string,\n): Promise<void> {\n const args = [\"checkout\", \"-b\", branchName];\n if (startPoint) args.push(startPoint);\n await run(args, cwd);\n}\n\nexport async function checkout(cwd: string, branchName: string): Promise<void> {\n await run([\"checkout\", branchName], cwd);\n}\n\nexport async function checkoutForce(\n cwd: string,\n branchName: string,\n): Promise<void> {\n await run([\"checkout\", \"-f\", branchName], cwd);\n}\n\nexport async function resetHard(cwd: string, ref: string): Promise<void> {\n await run([\"reset\", \"--hard\", ref], cwd);\n}\n\nexport async function getDiff(cwd: string, base?: string): Promise<string> {\n const args = base ? [\"diff\", `${base}...HEAD`] : [\"diff\"];\n return run(args, cwd);\n}\n\nexport async function getStagedDiff(cwd: string): Promise<string> {\n return run([\"diff\", \"--cached\"], cwd);\n}\n\nexport async function getLog(\n cwd: string,\n range?: string,\n maxCount?: number,\n): Promise<string> {\n const args = [\"log\", \"--oneline\"];\n if (maxCount) args.push(`-${maxCount}`);\n if (range) args.push(range);\n return run(args, cwd);\n}\n\nexport async function add(cwd: string, files: string[]): Promise<void> {\n await run([\"add\", ...files], cwd);\n}\n\n/**\n * Write the current index to a tree object and return its SHA. Captures the\n * fully-staged state so individual paths can later be re-staged from it via\n * the index alone, without ever reading the working tree.\n */\nexport async function writeTree(cwd: string): Promise<string> {\n return run([\"write-tree\"], cwd);\n}\n\n/**\n * Stage the given paths into the index from a tree object, without touching\n * the working tree. Unlike `git add`, this never reads the worktree, so a path\n * that no longer matches a worktree file — a staged-then-deleted file, a staged\n * deletion, or a planned path that was never staged — is handled by the index\n * (added, removed, or no-op'd) instead of aborting with\n * \"pathspec did not match any files\". No-ops when `files` is empty.\n */\nexport async function stagePathsFromTree(\n cwd: string,\n tree: string,\n files: string[],\n): Promise<void> {\n if (files.length === 0) return;\n await run([\"reset\", \"-q\", tree, \"--\", ...files], cwd);\n}\n\nexport async function commit(cwd: string, message: string): Promise<string> {\n return run([\"commit\", \"-m\", message], cwd);\n}\n\nexport async function status(cwd: string): Promise<string> {\n // Bypass `run()`'s `stdout.trim()` because porcelain status lines start with\n // a leading space when the file is unstaged-modified (e.g. \" M .gitignore\").\n // Trimming the outer whitespace strips that space and downstream parsers\n // that rely on the fixed 3-char `XY ` prefix would misread the path.\n debug(\"git command\", { args: [\"status\", \"--porcelain\"], cwd });\n try {\n const result: ExecResult = await exec(\n \"git\",\n [\"status\", \"--porcelain\"],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return result.stdout.replace(/\\n+$/, \"\");\n } catch (err: unknown) {\n if (err instanceof Error && \"killed\" in err && (err as { killed: boolean }).killed) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Git command timed out after ${GIT_TIMEOUT_MS / 1000}s: git status --porcelain`,\n cause: err,\n details: { command: \"git status --porcelain\", timedOut: true },\n });\n }\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: {\n command: \"git status --porcelain\",\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n}\n\nexport async function push(\n cwd: string,\n remote: string,\n branch: string,\n): Promise<void> {\n await run([\"push\", remote, branch], cwd);\n}\n\nexport async function fetch(cwd: string, remote: string): Promise<void> {\n await run([\"fetch\", remote], cwd);\n}\n\nexport async function getChangedFiles(cwd: string): Promise<string[]> {\n const files = await parseStatus(cwd);\n return files.map((f) => f.file);\n}\n\nexport interface ChangedFile {\n file: string;\n indexStatus: string;\n workTreeStatus: string;\n}\n\nexport async function parseStatus(cwd: string): Promise<ChangedFile[]> {\n let result: { stdout: string };\n try {\n result = await exec(\"git\", [\"status\", \"--porcelain\"], { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER });\n } catch (err) {\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Failed to read git status: ${err instanceof Error ? err.message : String(err)}`,\n cause: err,\n details: {\n command: \"git status --porcelain\",\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n // Use raw stdout (no trim) — leading spaces in porcelain format are meaningful\n const output = result.stdout;\n if (!output || !output.trim()) return [];\n return output\n .split(\"\\n\")\n .filter((line) => line.length >= 3)\n .map((line) => {\n const indexStatus = line[0] as string;\n const workTreeStatus = line[1] as string;\n let file = line.slice(3).trim();\n // Handle renamed/copied files: \"R old -> new\" or \"C old -> new\"\n if (\n (indexStatus === \"R\" || indexStatus === \"C\") &&\n file.includes(\" -> \")\n ) {\n file = file.split(\" -> \").pop()!;\n }\n return { file, indexStatus, workTreeStatus };\n })\n .filter((entry) => entry.file.length > 0);\n}\n\nexport async function getStagedFiles(cwd: string): Promise<ChangedFile[]> {\n const files = await parseStatus(cwd);\n return files.filter((f) => f.indexStatus !== \" \" && f.indexStatus !== \"?\");\n}\n\nexport async function resetStaged(cwd: string): Promise<void> {\n await run([\"reset\", \"HEAD\"], cwd);\n}\n\nexport async function getStagedFilesList(cwd: string): Promise<string[]> {\n const output = await run([\"diff\", \"--cached\", \"--name-only\"], cwd);\n if (!output) return [];\n return output.split(\"\\n\").filter((f) => f.length > 0);\n}\n\nexport async function getUnstagedFiles(cwd: string): Promise<ChangedFile[]> {\n const files = await parseStatus(cwd);\n return files.filter(\n (f) =>\n (f.indexStatus === \"?\" && f.workTreeStatus === \"?\") ||\n f.workTreeStatus !== \" \",\n );\n}\n\nexport async function getLatestTag(cwd: string): Promise<string | null> {\n try {\n return await run([\"describe\", \"--tags\", \"--abbrev=0\"], cwd);\n } catch {\n return null;\n }\n}\n\nexport async function createTag(\n cwd: string,\n tag: string,\n message: string,\n options?: { signed?: boolean },\n): Promise<void> {\n const flag = options?.signed === true ? \"-s\" : \"-a\";\n await run([\"tag\", flag, tag, \"-m\", message], cwd);\n}\n\nexport async function tagExists(cwd: string, tag: string): Promise<boolean> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"--quiet\", `refs/tags/${tag}`], {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function pushWithTags(\n cwd: string,\n remote: string,\n branch: string,\n): Promise<void> {\n await run([\"push\", remote, branch, \"--follow-tags\"], cwd);\n}\n\nexport async function mergeNoFf(cwd: string, source: string): Promise<void> {\n await run([\"merge\", \"--no-ff\", source], cwd);\n}\n\nexport async function branchExists(cwd: string, branch: string): Promise<boolean> {\n try {\n await exec(\n \"git\",\n [\"show-ref\", \"--verify\", \"--quiet\", `refs/heads/${branch}`],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function headSha(cwd: string): Promise<string> {\n return run([\"rev-parse\", \"HEAD\"], cwd);\n}\n\nexport async function resetSoft(cwd: string, ref: string): Promise<void> {\n await run([\"reset\", \"--soft\", ref], cwd);\n}\n\nexport async function stashPushNamed(cwd: string, message: string): Promise<void> {\n await run([\"stash\", \"push\", \"--include-untracked\", \"-m\", message], cwd);\n}\n\nexport async function stashList(cwd: string): Promise<string> {\n return run([\"stash\", \"list\"], cwd);\n}\n\nasync function findStashRef(cwd: string, stashName: string): Promise<string> {\n const list = await stashList(cwd);\n const line = list.split(\"\\n\").find((l) => l.includes(stashName));\n if (!line) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Named stash not found in stash list: ${stashName}`,\n details: { stashName },\n });\n }\n const match = /^(stash@\\{\\d+\\})/.exec(line);\n if (!match?.[1]) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Cannot parse stash ref from stash list line: ${line}`,\n details: { stashName, line },\n });\n }\n return match[1];\n}\n\nexport async function stashApplyNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n // Do not use --index: stashes created with --include-untracked are incompatible\n // with --index restoration for newly-staged (never committed) files.\n await run([\"stash\", \"apply\", ref], cwd);\n}\n\nexport async function stashPopNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n await run([\"stash\", \"pop\", ref], cwd);\n}\n\nexport async function stashDropNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n await run([\"stash\", \"drop\", ref], cwd);\n}\n\n/**\n * Force-remove all untracked files and directories from the working tree.\n * Used before stash pop in compensate paths to avoid \"would be overwritten\"\n * conflicts from files that were left untracked after reset --hard.\n */\nexport async function cleanForced(cwd: string): Promise<void> {\n await run([\"clean\", \"-fd\"], cwd);\n}\n\n/**\n * Read a file's contents at `HEAD` via `git show HEAD:<path>`. Returns `null`\n * when the path does not exist in the HEAD tree (so callers can distinguish\n * \"missing in HEAD\" from \"exists but empty\"). Bypasses the helper `run()` to\n * preserve trailing newlines, which the working-tree validators compare\n * byte-for-byte.\n */\nexport async function showFileAtHead(\n cwd: string,\n path: string,\n): Promise<string | null> {\n debug(\"git command\", { args: [\"show\", `HEAD:${path}`], cwd });\n try {\n const result: ExecResult = await exec(\"git\", [\"show\", `HEAD:${path}`], {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n maxBuffer: GIT_MAX_BUFFER,\n });\n return result.stdout;\n } catch {\n return null;\n }\n}\n\nexport async function deleteBranch(\n cwd: string,\n branch: string,\n force = false,\n): Promise<void> {\n await run([\"branch\", force ? \"-D\" : \"-d\", branch], cwd);\n}\n\n/**\n * Is `branch` fully reachable from `target`? Resolves to true when every commit\n * on `branch` is already in `target` (i.e., the merge would be a no-op). Used\n * by abortRelease to refuse deleting a release branch that still has commits\n * not present in main/develop.\n */\nexport async function isBranchMerged(\n cwd: string,\n branch: string,\n target: string,\n): Promise<boolean> {\n try {\n await exec(\n \"git\",\n [\"merge-base\", \"--is-ancestor\", branch, target],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detect the base branch of the repository (main or master).\n * Returns 'main' if both exist, falls back to 'master', throws if neither exists.\n */\nexport async function detectBaseBranch(cwd: string): Promise<string> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"main\"], { cwd, timeout: GIT_TIMEOUT_MS });\n return \"main\";\n } catch {\n // main doesn't exist, try master\n }\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"master\"], { cwd, timeout: GIT_TIMEOUT_MS });\n return \"master\";\n } catch {\n // master doesn't exist either\n }\n throw new GitwiseError({\n code: \"NO_BASE_BRANCH\",\n message: \"No base branch found: neither main nor master exists\",\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n}\n\nexport interface ApplyCommitParams {\n message: string;\n files: string[];\n cwd: string;\n}\n\n/**\n * Stage the given files and create a commit.\n * Throws a typed error on hook failure or other git errors.\n */\nexport async function applyCommit(params: ApplyCommitParams): Promise<void> {\n const { message, files, cwd } = params;\n try {\n if (files.length > 0) {\n await add(cwd, files);\n }\n await commit(cwd, message);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"COMMIT_HOOK_FAILURE\",\n message: `Git commit failed: ${msg}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: stderr !== undefined ? { stderr } : undefined,\n });\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { debug } from \"./logger.js\";\nimport { GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\n\nexport interface CreatePRParams {\n title: string;\n body: string;\n base?: string;\n cwd: string;\n draft?: boolean;\n}\n\nexport interface PRResult {\n url: string;\n}\n\nexport async function isGhAvailable(): Promise<boolean> {\n try {\n await exec(\"gh\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function getGhVersion(): Promise<string | null> {\n try {\n const result = await exec(\"gh\", [\"--version\"]);\n const firstLine = result.stdout.split(\"\\n\")[0] ?? \"\";\n return firstLine.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport async function createPR(params: CreatePRParams): Promise<PRResult> {\n debug(\"Creating PR via gh\", { title: params.title });\n const args = [\"pr\", \"create\", \"--title\", params.title, \"--body\", params.body];\n if (params.base) {\n args.push(\"--base\", params.base);\n }\n if (params.draft) {\n args.push(\"--draft\");\n }\n const result = await exec(\"gh\", args, { cwd: params.cwd });\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: \"gh pr create returned empty output — check gh auth status\",\n details: { command: \"gh pr create\" },\n });\n }\n return { url };\n}\n\nexport interface UpdatePRParams {\n prNumber: string | number;\n title?: string;\n body?: string;\n cwd: string;\n}\n\nexport async function updatePR(params: UpdatePRParams): Promise<PRResult> {\n debug(\"Updating PR via gh\", { prNumber: params.prNumber });\n const args = [\"pr\", \"edit\", String(params.prNumber)];\n if (params.title) args.push(\"--title\", params.title);\n if (params.body) args.push(\"--body\", params.body);\n await exec(\"gh\", args, { cwd: params.cwd });\n const url = await getPrUrl(params.prNumber, params.cwd);\n return { url };\n}\n\nexport async function getPrUrl(prNumber: string | number, cwd: string): Promise<string> {\n const result = await exec(\n \"gh\",\n [\"pr\", \"view\", String(prNumber), \"--json\", \"url\", \"-q\", \".url\"],\n { cwd },\n );\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: `gh pr view ${prNumber} returned empty output — check gh auth status`,\n details: { command: `gh pr view ${prNumber}` },\n });\n }\n return url;\n}\n\nexport interface CreateReleaseParams {\n tag: string;\n title: string;\n body: string;\n cwd: string;\n}\n\nexport async function createGitHubRelease(\n params: CreateReleaseParams,\n): Promise<PRResult> {\n debug(\"Creating GitHub release via gh\", { tag: params.tag });\n const args = [\n \"release\",\n \"create\",\n params.tag,\n \"--title\",\n params.title,\n \"--notes\",\n params.body,\n ];\n const result = await exec(\"gh\", args, { cwd: params.cwd });\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: \"gh release create returned empty output — check gh auth status\",\n details: { command: \"gh release create\" },\n });\n }\n return { url };\n}\n\n// Alias: openPr — used by downstream tasks expecting this name\nexport const openPr = createPR;\n","import { readFile, open, rename, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists, ensureDir } from \"./filesystem.js\";\n\nconst ENV_DIR = \".gitwise\";\nconst ENV_FILE = \".env\";\n\nfunction getEnvPath(projectRoot: string): string {\n return join(projectRoot, ENV_DIR, ENV_FILE);\n}\n\nfunction parseLine(line: string): [string, string] | null {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) return null;\n const eq = trimmed.indexOf(\"=\");\n if (eq < 1) return null;\n return [trimmed.slice(0, eq).trim(), trimmed.slice(eq + 1).trim()];\n}\n\nexport async function loadEnv(projectRoot: string): Promise<void> {\n const envPath = getEnvPath(projectRoot);\n if (!(await fileExists(envPath))) return;\n const content = await readFile(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const parsed = parseLine(line);\n if (!parsed) continue;\n const [key, value] = parsed;\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\nexport async function writeEnvVar(\n projectRoot: string,\n key: string,\n value: string,\n): Promise<void> {\n const envPath = getEnvPath(projectRoot);\n await ensureDir(join(projectRoot, ENV_DIR));\n\n let lines: string[] = [];\n if (await fileExists(envPath)) {\n const content = await readFile(envPath, \"utf-8\");\n lines = content.split(\"\\n\");\n }\n\n const prefix = `${key}=`;\n const idx = lines.findIndex((l) => l.trim().startsWith(prefix));\n const entry = `${key}=${value}`;\n\n if (idx >= 0) {\n lines[idx] = entry;\n } else {\n if (lines.length === 1 && lines[0] === \"\") {\n lines[0] = entry;\n } else {\n lines.push(entry);\n }\n }\n\n const final = lines.join(\"\\n\").replace(/\\n{3,}/g, \"\\n\\n\");\n const payload = final.endsWith(\"\\n\") ? final : final + \"\\n\";\n\n const tmpPath = `${envPath}.${process.pid}.${Date.now()}.tmp`;\n const fd = await open(tmpPath, \"w\", 0o600);\n try {\n await fd.writeFile(payload, \"utf-8\");\n } finally {\n await fd.close();\n }\n try {\n await rename(tmpPath, envPath);\n } catch (err) {\n await unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n\nexport async function readEnvVar(\n projectRoot: string,\n key: string,\n): Promise<string | undefined> {\n const envPath = getEnvPath(projectRoot);\n if (!(await fileExists(envPath))) return undefined;\n const content = await readFile(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const parsed = parseLine(line);\n if (parsed && parsed[0] === key) return parsed[1];\n }\n return undefined;\n}\n\n/**\n * Read a key from process.env, with optional fallback to the project .env file.\n */\nexport async function read(\n key: string,\n projectRoot?: string,\n): Promise<string | undefined> {\n if (process.env[key] !== undefined) {\n return process.env[key];\n }\n if (projectRoot) {\n return readEnvVar(projectRoot, key);\n }\n return undefined;\n}\n","import { GitwiseError } from \"../errors.js\";\n\nexport interface Step<T> {\n name: string;\n apply: () => Promise<T>;\n compensate: (result: T) => Promise<void>;\n}\n\nexport interface Logger {\n warn(message: string, context?: Record<string, unknown>): void;\n}\n\nexport interface RollbackFailure {\n step: string;\n error: unknown;\n}\n\nexport interface RollbackResult {\n partial: boolean;\n failures: RollbackFailure[];\n}\n\ninterface AppliedStep {\n step: Step<unknown>;\n result: unknown;\n}\n\nexport class Transaction {\n private readonly applied: AppliedStep[] = [];\n\n async run<T>(step: Step<T>): Promise<T> {\n const result = await step.apply();\n this.applied.push({ step: step as Step<unknown>, result });\n return result;\n }\n\n get size(): number {\n return this.applied.length;\n }\n\n async rollback(reason: GitwiseError, logger: Logger): Promise<RollbackResult> {\n const failures: RollbackFailure[] = [];\n for (const { step, result } of [...this.applied].reverse()) {\n try {\n await step.compensate(result);\n } catch (err) {\n failures.push({ step: step.name, error: err });\n logger.warn(\"compensate-failed\", {\n step: step.name,\n reason: serializeError(err),\n });\n }\n }\n if (failures.length > 0) {\n logger.warn(\"rollback partial: one or more compensate actions failed\", {\n code: \"ROLLBACK_PARTIAL\",\n originalCode: reason.code,\n failures: failures.map((f) => ({\n step: f.step,\n error: serializeError(f.error),\n })),\n });\n }\n return { partial: failures.length > 0, failures };\n }\n}\n\nfunction serializeError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message };\n }\n return err;\n}\n","import { mkdir, open, readFile, unlink } from \"node:fs/promises\";\nimport { hostname } from \"node:os\";\nimport path from \"node:path\";\nimport { GitwiseError } from \"../errors.js\";\n\nexport const STALE_LOCK_MS = 10 * 60 * 1000;\n\nexport interface LockPayload {\n pid: number;\n host: string;\n command: string;\n acquiredAt: string;\n}\n\nexport interface AcquireRepoLockOptions {\n command?: string;\n staleMs?: number;\n isProcessAlive?: (pid: number) => boolean;\n now?: () => Date;\n /**\n * Test seam: invoked once, awaited, immediately after a stale lock is\n * unlinked and immediately before the re-acquire attempt. Lets tests\n * deterministically simulate another process re-creating the lock inside\n * the reclaim window (the `EEXIST` on `attempt >= 1` → REPO_LOCKED path).\n * Unset in production (no-op).\n */\n onReclaim?: () => void | Promise<void>;\n}\n\nexport async function acquireRepoLock(\n repoPath: string,\n options: AcquireRepoLockOptions = {},\n): Promise<() => Promise<void>> {\n const command = options.command ?? \"unknown\";\n const staleMs = options.staleMs ?? STALE_LOCK_MS;\n const isAlive = options.isProcessAlive ?? defaultIsProcessAlive;\n const now = options.now ?? (() => new Date());\n\n const dir = path.join(repoPath, \".gitwise\");\n const lockPath = path.join(dir, \".lock\");\n await mkdir(dir, { recursive: true });\n\n const payload: LockPayload = {\n pid: process.pid,\n host: hostname(),\n command,\n acquiredAt: now().toISOString(),\n };\n\n await tryAcquire(lockPath, payload, staleMs, isAlive, now, 0, options.onReclaim);\n\n let released = false;\n return async () => {\n if (released) return;\n released = true;\n try {\n await unlink(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n };\n}\n\nasync function tryAcquire(\n lockPath: string,\n payload: LockPayload,\n staleMs: number,\n isAlive: (pid: number) => boolean,\n now: () => Date,\n attempt: number,\n onReclaim?: () => void | Promise<void>,\n): Promise<void> {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(JSON.stringify(payload, null, 2) + \"\\n\", \"utf-8\");\n } finally {\n await handle.close();\n }\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n\n if (attempt >= 1) {\n throw new GitwiseError({\n code: \"REPO_LOCKED\",\n message: \"Another gitwise process holds the lock on this repository\",\n details: { lockPath },\n });\n }\n\n const existing = await readExisting(lockPath);\n if (existing && !isStale(existing, staleMs, isAlive, now)) {\n throw new GitwiseError({\n code: \"REPO_LOCKED\",\n message: `gitwise lock held by pid ${existing.pid} (command: ${existing.command}) since ${existing.acquiredAt}`,\n details: { existing, lockPath },\n });\n }\n\n try {\n await unlink(lockPath);\n } catch (unlinkErr) {\n if ((unlinkErr as NodeJS.ErrnoException).code !== \"ENOENT\") throw unlinkErr;\n }\n // Reclaim window: a competing process may re-create the lock here. Tests\n // drive that deterministically via onReclaim; production leaves it unset.\n if (onReclaim) await onReclaim();\n return tryAcquire(lockPath, payload, staleMs, isAlive, now, attempt + 1, onReclaim);\n}\n\nasync function readExisting(lockPath: string): Promise<LockPayload | null> {\n try {\n const content = await readFile(lockPath, \"utf-8\");\n const parsed = JSON.parse(content) as Partial<LockPayload>;\n if (\n typeof parsed.pid !== \"number\" ||\n typeof parsed.host !== \"string\" ||\n typeof parsed.command !== \"string\" ||\n typeof parsed.acquiredAt !== \"string\"\n ) {\n return null;\n }\n return {\n pid: parsed.pid,\n host: parsed.host,\n command: parsed.command,\n acquiredAt: parsed.acquiredAt,\n };\n } catch {\n return null;\n }\n}\n\nfunction isStale(\n existing: LockPayload,\n staleMs: number,\n isAlive: (pid: number) => boolean,\n now: () => Date,\n): boolean {\n if (!isAlive(existing.pid)) return true;\n const acquiredAt = Date.parse(existing.acquiredAt);\n if (Number.isNaN(acquiredAt)) return true;\n const age = now().getTime() - acquiredAt;\n return age > staleMs;\n}\n\nfunction defaultIsProcessAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") return false;\n if (code === \"EPERM\") return true;\n return false;\n }\n}\n","import { execSync, spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type { LLMChatRequest, LLMChatResponse, LLMProvider, ModelConfig, ModelTier } from \"./types.js\";\n\nconst LARGE_PROMPT_THRESHOLD = 100_000;\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nconst COMMON_CLAUDE_PATHS = [\n // Native installs (Homebrew, manual) — preferred over npm\n \"/opt/homebrew/bin/claude\",\n \"/usr/local/bin/claude\",\n path.join(os.homedir(), \".claude\", \"local\", \"claude\"),\n // npm global installs — fallback\n path.join(os.homedir(), \".npm-global\", \"bin\", \"claude\"),\n];\n\ninterface ClaudeCliResult {\n result: string;\n is_error: boolean;\n usage: {\n input_tokens: number;\n output_tokens: number;\n };\n}\n\nfunction isExecutable(filePath: string): boolean {\n try {\n fs.accessSync(filePath, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function resolveClaudeBinary(customPath?: string): string | null {\n if (customPath) {\n if (isExecutable(customPath)) return customPath;\n return null;\n }\n\n // 1. Check known native install paths first (Homebrew, manual)\n for (const candidate of COMMON_CLAUDE_PATHS) {\n if (isExecutable(candidate)) return candidate;\n }\n\n // 2. Fall back to PATH lookup (may find nvm/npm version)\n try {\n const found = execSync(\"which claude\", { stdio: \"pipe\" }).toString().trim();\n if (found && isExecutable(found)) return found;\n } catch {\n // not in PATH\n }\n\n // 3. Check nvm installations as last resort\n const nvmDir = path.join(os.homedir(), \".nvm\", \"versions\", \"node\");\n try {\n const versions = fs.readdirSync(nvmDir);\n for (const version of versions) {\n const candidate = path.join(nvmDir, version, \"bin\", \"claude\");\n if (isExecutable(candidate)) return candidate;\n }\n } catch {\n // nvm not installed\n }\n\n return null;\n}\n\nexport class ClaudeCodeProvider implements LLMProvider {\n private readonly models: ModelConfig;\n private readonly claudeBinaryPath: string;\n\n constructor(models: ModelConfig, claudeCliPath?: string) {\n this.models = models;\n this.claudeBinaryPath =\n claudeCliPath ?? resolveClaudeBinary() ?? \"claude\";\n }\n\n async chat(req: LLMChatRequest): Promise<LLMChatResponse> {\n const modelId = this.resolveModel(req.tier);\n debug(\"Calling Claude Code CLI\", { model: modelId, tier: req.tier, binary: this.claudeBinaryPath });\n\n const userContent = req.userMessage;\n const args = this.buildArgs(req.systemPrompt, modelId, userContent);\n\n const result =\n userContent.length > LARGE_PROMPT_THRESHOLD\n ? await this.callViaStdin(args, userContent)\n : await this.callViaCli(args);\n\n return {\n content: result.result,\n tokens: {\n input: result.usage.input_tokens,\n output: result.usage.output_tokens,\n },\n };\n }\n\n private buildArgs(\n systemPrompt: string,\n modelId: string,\n userContent: string,\n ): string[] {\n const args = [\n \"-p\",\n ...(userContent.length <= LARGE_PROMPT_THRESHOLD ? [userContent] : []),\n \"--system-prompt\",\n systemPrompt,\n \"--model\",\n modelId,\n \"--output-format\",\n \"json\",\n ];\n return args;\n }\n\n private async callViaCli(args: string[]): Promise<ClaudeCliResult> {\n // No stdin payload for this path — the prompt travels via argv (-p ...).\n // Closing stdin immediately (rather than leaving it open) matters: the\n // `claude` CLI treats a non-TTY stdin as possible piped input and waits\n // on it before proceeding.\n return this.spawnClaude(args, \"\");\n }\n\n private async callViaStdin(\n args: string[],\n input: string,\n ): Promise<ClaudeCliResult> {\n return this.spawnClaude(args, input);\n }\n\n private async spawnClaude(args: string[], input: string): Promise<ClaudeCliResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(this.claudeBinaryPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: DEFAULT_TIMEOUT_MS,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n });\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on(\"close\", (code) => {\n if (code !== 0) {\n if (stdout) {\n try {\n const parsed = JSON.parse(stdout);\n if (parsed.is_error) {\n reject(new Error(`Claude CLI error: ${parsed.result}`));\n return;\n }\n } catch {\n // stdout wasn't valid JSON\n }\n }\n const filteredStderr = stderr\n .replace(/Warning: no stdin data.*\\n?/g, \"\")\n .trim();\n reject(\n new Error(\n `Claude CLI exited with code ${code}${filteredStderr ? `: ${filteredStderr}` : \"\"}`,\n ),\n );\n return;\n }\n try {\n resolve(this.parseResponse(stdout));\n } catch (err) {\n reject(err);\n }\n });\n\n child.on(\"error\", (err) => {\n reject(this.wrapError(err));\n });\n\n child.stdin.write(input);\n child.stdin.end();\n });\n }\n\n private parseResponse(stdout: string): ClaudeCliResult {\n const parsed = JSON.parse(stdout);\n\n if (parsed.is_error) {\n throw new Error(`Claude CLI returned error: ${parsed.result}`);\n }\n\n const usage = { input_tokens: 0, output_tokens: 0 };\n if (parsed.usage) {\n usage.input_tokens = parsed.usage.input_tokens ?? 0;\n usage.output_tokens = parsed.usage.output_tokens ?? 0;\n }\n\n return {\n result: parsed.result ?? \"\",\n is_error: false,\n usage,\n };\n }\n\n private resolveModel(tier: ModelTier): string {\n return this.models[tier];\n }\n\n private wrapError(err: unknown): Error {\n if (err instanceof Error) {\n if (err.message.includes(\"ENOENT\")) {\n return new GitwiseError({\n code: \"PROVIDER_UNAVAILABLE\",\n message: `Claude Code CLI not found at \"${this.claudeBinaryPath}\". Re-run \\`gw config\\` to reconfigure.`,\n exitCode: EXIT_CODES.API_FAILED,\n cause: err,\n });\n }\n return err;\n }\n return new Error(String(err));\n }\n}\n","import type { ReleaseStrategyName } from \"../strategies/release.js\";\n\nexport type ModelTier = \"fast\" | \"balanced\" | \"powerful\";\nexport type Language = \"en\" | \"pt-br\" | \"es\" | \"fr\" | \"de\" | \"zh\" | \"ja\" | \"ko\";\nexport type CommitConvention = \"conventional\" | \"gitmoji\" | \"angular\" | \"kernel\" | \"custom\";\n\nexport interface ModelConfig {\n fast: string;\n balanced: string;\n powerful: string;\n}\n\n/** Persisted in ~/.gitwise/config.json */\nexport interface UserConfig {\n provider: \"api\" | \"claude-code\";\n claudeCliPath?: string;\n models: ModelConfig;\n language: Language;\n defaultBaseBranch?: string;\n commitConvention: CommitConvention;\n}\n\n/** Loaded from <cwd>/.gitwise.json — all fields are optional */\nexport interface RepoConfig {\n models?: Partial<ModelConfig>;\n language?: Language;\n defaultBaseBranch?: string;\n commitConvention?: CommitConvention;\n templatesPath?: string;\n /** When true, applyRelease() propagates the new version to all packages/* */\n workspacePropagation?: boolean;\n /** Release lifecycle strategy. Unset = \"github-flow\" at the consumer level. */\n releaseStrategy?: ReleaseStrategyName;\n /** Develop branch name for gitflow; consumers default to \"develop\" when unset. */\n developBranch?: string;\n}\n\n/** The merged result of UserConfig + RepoConfig overrides */\nexport interface MergedConfig extends UserConfig {\n templatesPath?: string;\n releaseStrategy?: ReleaseStrategyName;\n developBranch?: string;\n}\n\nexport const DEFAULT_USER_CONFIG: UserConfig = {\n provider: \"api\",\n models: {\n fast: \"claude-haiku-4-5-20251001\",\n balanced: \"claude-sonnet-4-6\",\n powerful: \"claude-opus-4-7\",\n },\n language: \"en\",\n commitConvention: \"conventional\",\n};\n","import os from \"node:os\";\nimport { read as readEnvValue } from \"../infra/env.js\";\nimport { readUserConfig } from \"./user.js\";\nimport { readRepoConfig } from \"./repo.js\";\nimport type { MergedConfig, RepoConfig, UserConfig } from \"./types.js\";\n\nexport function deepMerge(base: UserConfig, override: RepoConfig): MergedConfig {\n return {\n ...base,\n ...(override.language !== undefined && { language: override.language }),\n ...(override.defaultBaseBranch !== undefined && { defaultBaseBranch: override.defaultBaseBranch }),\n ...(override.commitConvention !== undefined && { commitConvention: override.commitConvention }),\n ...(override.templatesPath !== undefined && { templatesPath: override.templatesPath }),\n ...(override.releaseStrategy !== undefined && { releaseStrategy: override.releaseStrategy }),\n ...(override.developBranch !== undefined && { developBranch: override.developBranch }),\n models: {\n ...base.models,\n ...(override.models ?? {}),\n },\n };\n}\n\nexport interface GetMergedConfigOptions {\n cwd: string;\n homeDir?: string;\n}\n\n/**\n * Load and merge config:\n * 1. Start from defaults\n * 2. Layer user config (~/.gitwise/config.json)\n * 3. Layer repo config (<cwd>/.gitwise.json)\n *\n * Note: the API key is NOT included in the returned config.\n */\nexport async function getMergedConfig(options: GetMergedConfigOptions): Promise<MergedConfig> {\n const { cwd, homeDir } = options;\n const userConfig = await readUserConfig(homeDir);\n const repoConfig = await readRepoConfig(cwd);\n if (!repoConfig) {\n return userConfig;\n }\n return deepMerge(userConfig, repoConfig);\n}\n\n/**\n * Read the Anthropic API key from process.env first, then ~/.gitwise/.env.\n * Returns undefined if not found anywhere.\n */\nexport async function getApiKey(homeDir?: string): Promise<string | undefined> {\n const home = homeDir ?? os.homedir();\n return readEnvValue(\"ANTHROPIC_API_KEY\", home);\n}\n","import { join } from \"node:path\";\nimport os from \"node:os\";\nimport { fileExists, readJSON, writeJSON } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { writeEnvVar } from \"../infra/env.js\";\nimport { DEFAULT_USER_CONFIG, type UserConfig } from \"./types.js\";\n\nconst GITWISE_DIR = \".gitwise\";\nconst USER_CONFIG_FILE = \"config.json\";\n\nfunction getUserConfigPath(homeDir?: string): string {\n return join(homeDir ?? os.homedir(), GITWISE_DIR, USER_CONFIG_FILE);\n}\n\nexport function mergeWithDefaults(partial: Partial<UserConfig>): UserConfig {\n return {\n ...DEFAULT_USER_CONFIG,\n ...partial,\n models: {\n ...DEFAULT_USER_CONFIG.models,\n ...(partial.models ?? {}),\n },\n };\n}\n\nexport async function readUserConfig(homeDir?: string): Promise<UserConfig> {\n const configPath = getUserConfigPath(homeDir);\n if (!(await fileExists(configPath))) {\n debug(\"User config not found, using defaults\", { path: configPath });\n return { ...DEFAULT_USER_CONFIG };\n }\n const raw = await readJSON<Partial<UserConfig>>(configPath);\n return mergeWithDefaults(raw);\n}\n\nexport async function writeUserConfig(\n partial: Partial<UserConfig>,\n homeDir?: string,\n): Promise<void> {\n const configPath = getUserConfigPath(homeDir);\n const existing = await readUserConfig(homeDir);\n const updated = mergeWithDefaults({ ...existing, ...partial });\n debug(\"Writing user config\", { path: configPath });\n await writeJSON(configPath, updated);\n}\n\n/**\n * Write ANTHROPIC_API_KEY to ~/.gitwise/.env with file mode 0600.\n * Keys MUST NOT be written to config.json.\n *\n * Note: writeEnvVar(root, key, val) writes to root/.gitwise/.env.\n * We pass homeDir (default: os.homedir()) so the file lands at ~/.gitwise/.env.\n */\nexport async function writeApiKey(value: string, homeDir?: string): Promise<void> {\n const home = homeDir ?? os.homedir();\n await writeEnvVar(home, \"ANTHROPIC_API_KEY\", value);\n}\n","import { join } from \"node:path\";\nimport { fileExists, readJSON } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type { RepoConfig } from \"./types.js\";\n\nconst REPO_CONFIG_FILE = \".gitwise.json\";\n\nexport async function readRepoConfig(cwd: string): Promise<RepoConfig | null> {\n const configPath = join(cwd, REPO_CONFIG_FILE);\n if (!(await fileExists(configPath))) {\n debug(\"Repo config not found\", { path: configPath });\n return null;\n }\n try {\n const raw = await readJSON<RepoConfig>(configPath);\n return raw;\n } catch (err) {\n throw new GitwiseError({\n code: \"INVALID_REPO_CONFIG\",\n message: `Invalid repo config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n cause: err,\n });\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport os from \"node:os\";\nimport { fileExists } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { interpolate } from \"./interpolate.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// Bundled templates live at packages/core/templates/. The relative ascent\n// differs depending on where this module ends up at runtime:\n// - source layout: packages/core/src/template/loader.ts → ../../templates\n// - bundled dist: packages/core/dist/index.js → ../templates\n// We probe both so the loader works whether consumers import the source via\n// ts-jest or the built dist via `node`.\nconst BUNDLED_TEMPLATES_CANDIDATES = [\n join(__dirname, \"..\", \"templates\"),\n join(__dirname, \"..\", \"..\", \"templates\"),\n];\n\nfunction validateTemplateName(name: string): void {\n if (!/^[a-zA-Z0-9_-]+$/.test(name)) {\n throw new GitwiseError({\n code: \"TEMPLATE_INVALID_NAME\",\n message: `Invalid template name: '${name}'. Only alphanumeric characters, hyphens, and underscores are allowed.`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n}\n\nexport interface LoadTemplateOptions {\n /** Override the user-global templates directory (default: ~/.gitwise/templates). */\n templatesPath?: string;\n /** Repo root for repo-level override lookup (default: process.cwd()). */\n repoRoot?: string;\n}\n\n/**\n * Load a template by name, applying 3-level precedence:\n * 1. <repoRoot>/.gitwise/templates/<name>.md (highest priority)\n * 2. templatesPath (default: ~/.gitwise/templates/<name>.md)\n * 3. packages/core/templates/<name>.md (bundled fallback)\n *\n * Returns the raw template string (not interpolated).\n * Throws TEMPLATE_NOT_FOUND if no file found at any level.\n */\nexport async function loadTemplate(\n name: string,\n options: LoadTemplateOptions = {},\n): Promise<string> {\n validateTemplateName(name);\n\n const repoRoot = options.repoRoot ?? process.cwd();\n const userTemplatesPath = options.templatesPath ?? join(os.homedir(), \".gitwise\", \"templates\");\n\n // Level 1: repo-level override\n const repoOverride = join(repoRoot, \".gitwise\", \"templates\", `${name}.md`);\n if (await fileExists(repoOverride)) {\n debug(\"Loading repo-level template override\", { path: repoOverride });\n return readFile(repoOverride, \"utf-8\");\n }\n\n // Level 2: user-global (or configured templatesPath)\n const userOverride = join(userTemplatesPath, `${name}.md`);\n if (await fileExists(userOverride)) {\n debug(\"Loading user-global template override\", { path: userOverride });\n return readFile(userOverride, \"utf-8\");\n }\n\n // Level 3: bundled (probe each candidate layout)\n for (const candidate of BUNDLED_TEMPLATES_CANDIDATES) {\n const bundled = join(candidate, `${name}.md`);\n if (await fileExists(bundled)) {\n debug(\"Loading bundled template\", { path: bundled });\n return readFile(bundled, \"utf-8\");\n }\n }\n\n throw new GitwiseError({\n code: \"TEMPLATE_NOT_FOUND\",\n message: `Template '${name}' not found`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n}\n\n/**\n * Load and interpolate a template in one call.\n */\nexport async function loadAndInterpolate(\n name: string,\n ctx: Record<string, string>,\n options: LoadTemplateOptions = {},\n): Promise<string> {\n const template = await loadTemplate(name, options);\n return interpolate(template, ctx);\n}\n","/**\n * Replace {{var}} placeholders in a template string with values from ctx.\n * Unknown placeholders are left untouched.\n */\nexport function interpolate(template: string, ctx: Record<string, string>): string {\n return template.replace(\n /\\{\\{(\\w+)\\}\\}/g,\n (_match, key: string) => ctx[key] ?? _match,\n );\n}\n","import type { ModelTier } from \"./types.js\";\n\n// Four supported commands and their default tiers\n// commit/pr/release default to fast; review defaults to powerful\nconst COMMAND_TIER_MAP: Record<string, ModelTier> = {\n commit: \"fast\",\n review: \"powerful\",\n pr: \"fast\",\n release: \"fast\",\n};\n\nexport function resolveModelTier(command: string): ModelTier {\n return COMMAND_TIER_MAP[command] ?? \"balanced\";\n}\n\nexport const SUPPORTED_COMMANDS = Object.keys(COMMAND_TIER_MAP) as (keyof typeof COMMAND_TIER_MAP)[];\n","import * as git from \"../infra/git.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug, warn as logWarn } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport { Transaction, type Logger, type Step } from \"../infra/transaction.js\";\nimport { acquireRepoLock } from \"../infra/lockfile.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface CommitEntry {\n message: string;\n description?: string;\n files: string[];\n}\n\nexport interface CommitPlan {\n kind: \"single\" | \"split\";\n commits: CommitEntry[];\n tokens: { input: number; output: number };\n}\n\nexport type SplitMode = \"auto\" | \"never\" | \"always\";\n\nexport interface CommitOptions {\n cwd: string;\n provider: LLMProvider;\n prompt?: string;\n split?: SplitMode;\n push?: boolean;\n commitConvention?: string;\n templatesPath?: string;\n repoRoot?: string;\n feedbackHint?: string;\n generateAlternatives?: boolean;\n}\n\nexport interface CommitAlternatives {\n kind: \"alternatives\";\n options: string[];\n tokens: { input: number; output: number };\n}\n\nexport interface ApplyCommitPlanOptions {\n push?: boolean;\n remote?: string;\n}\n\n// ─── Sensitive file detection ────────────────────────────────────────────────\n\nconst SENSITIVE_PATTERNS = [\n /^\\.env$/,\n /^\\.env\\./,\n /\\.pem$/,\n /\\.key$/,\n /^id_rsa/,\n /^id_dsa/,\n /^id_ecdsa/,\n /^id_ed25519/,\n /credentials\\.json$/,\n /secrets\\.json$/,\n /auth\\.json$/,\n /service-account\\.json$/,\n /\\.p12$/,\n /\\.pfx$/,\n /\\.pkcs12$/,\n];\n\n// Env template files are conventionally committed: they contain only\n// placeholder values, not real secrets. Exclude them from the `.env.` block.\nconst SAFE_ENV_TEMPLATE_SUFFIXES = [\n \".example\",\n \".sample\",\n \".template\",\n \".dist\",\n \".defaults\",\n];\n\nfunction isSafeEnvTemplate(basename: string): boolean {\n if (!basename.startsWith(\".env\")) return false;\n return SAFE_ENV_TEMPLATE_SUFFIXES.some((suffix) => basename.endsWith(suffix));\n}\n\nfunction isSensitiveFile(filePath: string): boolean {\n const basename = filePath.split(\"/\").pop() ?? filePath;\n if (isSafeEnvTemplate(basename)) return false;\n return SENSITIVE_PATTERNS.some((pattern) => pattern.test(basename));\n}\n\n// ─── JSON parser strategies ──────────────────────────────────────────────────\n\ninterface LLMSingleResponse {\n type: \"single\";\n message: string;\n}\n\ninterface LLMPlanResponse {\n type: \"plan\";\n commits: Array<{ message: string; description?: string; files: string[] }>;\n}\n\ntype LLMCommitResponse = LLMSingleResponse | LLMPlanResponse;\n\nfunction tryParseJson(text: string): LLMCommitResponse | null {\n try {\n const parsed = JSON.parse(text.trim()) as Record<string, unknown>;\n if (parsed.type === \"plan\" && Array.isArray(parsed.commits)) {\n return parsed as unknown as LLMPlanResponse;\n }\n if (parsed.type === \"single\" && typeof parsed.message === \"string\") {\n return parsed as unknown as LLMSingleResponse;\n }\n } catch { /* not valid JSON */ }\n return null;\n}\n\n// Scans `raw` for balanced-brace substrings using a stack so each `}` emits the\n// substring opened by its matching `{`. String literals (with `\\\\` escapes) are\n// honored so braces inside JSON string values don't skew matching. An unclosed\n// outer `{` does not prevent inner balanced objects from being emitted, which\n// matters when an LLM emits a malformed draft followed by a valid object.\n// Candidates are returned in completion order (inner objects before their\n// enclosing parents).\nfunction extractBalancedJsonCandidates(raw: string): string[] {\n const candidates: string[] = [];\n const stack: number[] = [];\n let inString = false;\n let escape = false;\n for (let i = 0; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escape) {\n escape = false;\n } else if (c === \"\\\\\") {\n escape = true;\n } else if (c === '\"') {\n inString = false;\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n continue;\n }\n if (c === \"{\") {\n stack.push(i);\n } else if (c === \"}\") {\n const start = stack.pop();\n if (start !== undefined) {\n candidates.push(raw.slice(start, i + 1));\n }\n }\n }\n return candidates;\n}\n\nfunction parseAlternativesResponse(raw: string): string[] | null {\n const isValid = (p: unknown): p is { type: string; options: string[] } =>\n typeof p === \"object\" &&\n p !== null &&\n (p as Record<string, unknown>)[\"type\"] === \"alternatives\" &&\n Array.isArray((p as Record<string, unknown>)[\"options\"]) &&\n ((p as Record<string, unknown>)[\"options\"] as unknown[]).length > 0 &&\n ((p as Record<string, unknown>)[\"options\"] as unknown[]).every((o) => typeof o === \"string\");\n\n // Strategy 1: direct JSON\n try {\n const parsed = JSON.parse(raw.trim());\n if (isValid(parsed)) return parsed.options;\n } catch { /* fall through */ }\n\n // Strategy 2: fenced code block\n const fence = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n if (fence?.[1]) {\n try {\n const parsed = JSON.parse(fence[1].trim());\n if (isValid(parsed)) return parsed.options;\n } catch { /* fall through */ }\n }\n\n // Strategy 3: first line starting with {\n for (const line of raw.split(\"\\n\")) {\n const t = line.trim();\n if (!t.startsWith(\"{\")) continue;\n try {\n const parsed = JSON.parse(t);\n if (isValid(parsed)) return parsed.options;\n } catch { /* skip */ }\n }\n\n return null;\n}\n\nexport function parseCommitResponse(raw: string): LLMCommitResponse {\n // Strategy 1: pure JSON\n const direct = tryParseJson(raw);\n if (direct) return direct;\n\n // Strategy 2: fenced code block\n const fenceMatch = raw.match(/```json?\\s*\\n?([\\s\\S]*?)```/);\n if (fenceMatch?.[1]) {\n const fromFence = tryParseJson(fenceMatch[1]);\n if (fromFence) return fromFence;\n }\n\n // Strategy 3: balanced-brace candidate extraction. Prefer a \"plan\" candidate\n // when present so multi-context output is not silently downgraded to the\n // first \"single\" object the model emits alongside it.\n const candidates = extractBalancedJsonCandidates(raw);\n const parsedCandidates = candidates\n .map(tryParseJson)\n .filter((p): p is LLMCommitResponse => p !== null);\n const plan = parsedCandidates.find((p) => p.type === \"plan\");\n if (plan) return plan;\n if (parsedCandidates[0]) return parsedCandidates[0];\n\n // Fallback: treat the whole response as a single commit message\n return { type: \"single\", message: raw.trim() };\n}\n\nconst MAX_DIFF_CHARS = 80_000;\n\nfunction truncateDiff(diff: string): string {\n if (diff.length <= MAX_DIFF_CHARS) return diff;\n return diff.slice(0, MAX_DIFF_CHARS) + \"\\n\\n[diff truncated — too large for context window]\";\n}\n\n// ─── Core commit function ────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = `You are a developer writing commit messages. Analyze the git diff and the list of staged files to determine if the changes span one or multiple contexts.\n\nRules for commit messages:\n- Format: type(scope): description\n- Types: feat, fix, refactor, test, chore, style, docs\n- Description must be imperative, lowercase, max 72 chars\n- Scope is optional but recommended\n- Do NOT mention AI, Claude, generated, LLM, or copilot\n\nResponse format (JSON only, no extra text):\n\nIf all changes belong to a SINGLE context, return:\n{\"type\": \"single\", \"message\": \"type(scope): description\"}\n\nIf changes span MULTIPLE distinct contexts (e.g., a bug fix AND a new feature, or docs AND refactoring), return:\n{\"type\": \"plan\", \"commits\": [{\"message\": \"type(scope): short title\", \"description\": \"brief explanation of what and why\", \"files\": [\"file1.ts\"]}, {\"message\": \"type(scope): short title\", \"description\": \"brief explanation of what and why\", \"files\": [\"file3.ts\"]}]}\n\nRules for plan:\n- \"message\" is the commit title (max 72 chars, imperative, lowercase)\n- \"description\" is a brief one-line explanation of the change purpose\n- \"files\" lists only the files belonging to that commit\n- Every staged file must appear in exactly one commit — do not leave any file unassigned\n- Only return a plan when there are clearly separate concerns. Do not split for minor differences.`;\n\nexport async function commit(opts: CommitOptions): Promise<CommitPlan | CommitAlternatives> {\n const { cwd, provider, prompt, split = \"auto\" } = opts;\n\n // Get staged files and diff\n const stagedFiles = await git.getStagedFilesList(cwd);\n const diff = await git.getStagedDiff(cwd);\n\n if (!diff) {\n throw new GitwiseError({\n code: \"NOTHING_STAGED\",\n message: \"No staged changes to commit\",\n });\n }\n\n // Sensitive file guard.\n // The user-facing Error message intentionally omits filenames: paths like\n // `prod-customer-db-credentials.json` are themselves sensitive and can leak\n // via shell history, CI logs, or pasted terminal output. The flagged files\n // are exposed only on the structured `files` property and emitted through\n // the debug logger so opt-in `--debug` (CLI flag) or `GITWISE_DEBUG=1` (env\n // var, for non-interactive/CI use) surfaces them for triage.\n const sensitiveFiles = stagedFiles.filter(isSensitiveFile);\n if (sensitiveFiles.length > 0) {\n debug(\"Sensitive files blocked from commit\", { files: sensitiveFiles });\n throw new GitwiseError({\n code: \"SENSITIVE_FILE_BLOCKED\",\n message: `SENSITIVE_FILE_BLOCKED: ${sensitiveFiles.length} file(s) matched sensitive patterns (env/pem/credentials). Re-run with --debug (or set GITWISE_DEBUG=1) to see which files were flagged.`,\n details: { files: sensitiveFiles },\n });\n }\n\n // Load template (or use built-in system prompt)\n let systemPrompt = SYSTEM_PROMPT;\n try {\n const templateContent = await loadTemplate(\"commit\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n // Only use the template as system prompt if it's a full prompt, not just a format string\n if (templateContent && !templateContent.includes(\"{{type}}\")) {\n systemPrompt = templateContent;\n }\n } catch {\n // Use built-in prompt if template not found\n }\n\n // When generating alternatives, extend the system prompt to include the third format.\n const effectiveSystemPrompt = opts.generateAlternatives\n ? `${systemPrompt}\\n\\nWhen asked to generate alternatives, return ONLY this JSON (no other text):\\n{\"type\": \"alternatives\", \"options\": [\"message1\", \"message2\", \"message3\"]}`\n : systemPrompt;\n\n // Build user message\n const userMessage = [\n `Staged files:\\n${stagedFiles.join(\"\\n\")}`,\n `\\nDiff:\\n${truncateDiff(diff)}`,\n prompt ? `\\nUser intent: ${prompt}` : \"\",\n opts.feedbackHint ? `\\nUser feedback on previous suggestion: ${opts.feedbackHint}` : \"\",\n opts.generateAlternatives\n ? `\\nIMPORTANT: Generate exactly 3 different alternative commit messages. Return JSON only: {\"type\": \"alternatives\", \"options\": [\"message1\", \"message2\", \"message3\"]}`\n : \"\",\n ].join(\"\");\n\n debug(\"Calling LLM for commit analysis\", { tier: \"fast\", fileCount: stagedFiles.length });\n\n const tier = resolveModelTier(\"commit\");\n const response = await provider.chat({ systemPrompt: effectiveSystemPrompt, userMessage, tier });\n\n const parsed = parseCommitResponse(response.content);\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n\n // Alternatives mode: return up to 3 options instead of a plan\n if (opts.generateAlternatives) {\n const options = parseAlternativesResponse(response.content);\n if (options && options.length > 0) {\n return { kind: \"alternatives\", options, tokens } satisfies CommitAlternatives;\n }\n // Fallback: wrap whatever was parsed as a single-option list\n const fallbackMsg = parsed.type === \"single\"\n ? parsed.message\n : parsed.commits[0]?.message ?? response.content.trim().slice(0, 100);\n return { kind: \"alternatives\", options: [fallbackMsg], tokens } satisfies CommitAlternatives;\n }\n\n // Handle split modes\n if (split === \"never\") {\n // Always return single\n const message = parsed.type === \"single\"\n ? parsed.message\n : parsed.commits.map((c) => c.message).join(\"\\n\\n\");\n return {\n kind: \"single\",\n commits: [{ message, files: stagedFiles }],\n tokens,\n };\n }\n\n if (parsed.type === \"plan\" && parsed.commits.length > 1) {\n if (split === \"always\" || split === \"auto\") {\n const assignedFiles = new Set(parsed.commits.flatMap(c => c.files));\n const missing = stagedFiles.filter(f => !assignedFiles.has(f));\n if (missing.length > 0) {\n parsed.commits[parsed.commits.length - 1]!.files.push(...missing);\n }\n return {\n kind: \"split\",\n commits: parsed.commits,\n tokens,\n };\n }\n }\n\n if (split === \"always\" && parsed.type !== \"plan\") {\n throw new GitwiseError({\n code: \"NO_SPLIT_POSSIBLE\",\n message: \"split: 'always' requested but LLM returned a single-context plan\",\n exitCode: EXIT_CODES.INVALID_INTENT,\n });\n }\n\n // Single commit\n const message = parsed.type === \"single\" ? parsed.message : parsed.commits[0]?.message ?? \"chore: update\";\n return {\n kind: \"single\",\n commits: [{ message, files: stagedFiles }],\n tokens,\n };\n}\n\n// ─── Step factories ──────────────────────────────────────────────────────────\n\nexport interface CommitStepResult {\n priorSha: string;\n newSha: string;\n}\n\n/**\n * Transaction step that saves a named git stash as a backup of the pre-split\n * working tree, then immediately re-applies the stash so the normal flow can\n * continue with the same staged state. The predictable stash name\n * (`gitwise/split-<ISO8601>`) lets `docs/recovery.md` guide manual recovery\n * when the compensate fires.\n *\n * compensate: resets the index and working tree to HEAD (no-data-loss because\n * the stash is still present), then pops the named stash to restore the exact\n * pre-split state.\n */\nexport function takeNamedStashStep(cwd: string, stashName: string): Step<void> {\n return {\n name: `takeNamedStash(${stashName})`,\n async apply(): Promise<void> {\n await git.stashPushNamed(cwd, stashName);\n await git.stashApplyNamed(cwd, stashName);\n },\n async compensate(): Promise<void> {\n // Hard-reset + clean to reach a pristine HEAD state before popping.\n // reset --hard clears tracked/staged changes; clean -fd removes\n // untracked files that were restored by stashApplyNamed and would\n // otherwise conflict with the pop. The stash (taken with\n // --include-untracked) will restore those files during pop.\n // If pop fails, the stash is still in the list under its predictable\n // name so the user can recover manually.\n await git.resetHard(cwd, \"HEAD\");\n await git.cleanForced(cwd);\n await git.stashPopNamed(cwd, stashName);\n },\n };\n}\n\n/**\n * Transaction step that stages the given group's files and creates one commit.\n *\n * Staging goes through `stagedTree` — a tree object captured from the fully\n * staged index before the split unstaged everything — via the index alone\n * (`git reset <tree> -- <paths>`). This never reads the working tree, so a\n * planned path that no longer matches a worktree file (a staged-then-deleted\n * file, a staged deletion, or a path the plan named but that was never staged)\n * is handled by the index instead of aborting the whole commit with\n * \"pathspec did not match any files\". A group whose paths contribute nothing\n * to the index (e.g. all phantom paths) is skipped rather than failing on an\n * empty commit.\n *\n * apply — records the prior HEAD SHA (for compensate) and the new HEAD SHA\n * (as evidence of the created commit) in the result. When the group\n * stages nothing, `newSha === priorSha` and no commit is made.\n * compensate — runs `git reset --soft <priorSha>` to undo only this commit\n * while preserving the staged delta for potential retry.\n */\nexport function applyOneCommitStep(\n entry: CommitEntry,\n cwd: string,\n stagedTree: string,\n): Step<CommitStepResult> {\n const msg = entry.description\n ? `${entry.message}\\n\\n${entry.description}`\n : entry.message;\n return {\n name: `applyCommit(${entry.message})`,\n async apply(): Promise<CommitStepResult> {\n const priorSha = await git.headSha(cwd);\n await git.stagePathsFromTree(cwd, stagedTree, entry.files);\n const staged = await git.getStagedFilesList(cwd);\n if (staged.length === 0) {\n // None of the group's planned paths were actually staged (phantom or\n // already-committed). Skip instead of failing `git commit` with\n // \"nothing to commit\".\n debug(\"Skipping commit group with no staged changes\", {\n message: entry.message,\n files: entry.files,\n });\n return { priorSha, newSha: priorSha };\n }\n // files: [] — staging already done above via the index; this only commits.\n await git.applyCommit({ message: msg, files: [], cwd });\n const newSha = await git.headSha(cwd);\n return { priorSha, newSha };\n },\n async compensate({ priorSha }: CommitStepResult): Promise<void> {\n await git.resetSoft(cwd, priorSha);\n },\n };\n}\n\n// ─── applyCommitPlan ─────────────────────────────────────────────────────────\n\nexport async function applyCommitPlan(\n plan: CommitPlan,\n opts: ApplyCommitPlanOptions & { cwd: string },\n): Promise<void> {\n const { cwd, push: shouldPush = false, remote = \"origin\" } = opts;\n\n if (plan.kind === \"split\") {\n if (plan.commits.length === 0) {\n throw new GitwiseError({\n code: \"INVALID_INTENT\",\n message: \"Commit split plan has zero commits; cannot apply\",\n });\n }\n\n const stashName = `gitwise/split-${new Date().toISOString()}`;\n const releaseLock = await acquireRepoLock(cwd, { command: \"commit-split\" });\n\n try {\n const tx = new Transaction();\n const logger: Logger = { warn: logWarn };\n\n try {\n // Capture the fully-staged state as a tree object FIRST, while the index\n // still holds every staged change, so each group can be re-staged from it\n // via the index alone. This avoids re-running `git add` against the\n // working tree, which is fatal when a planned path no longer matches a\n // worktree file (see applyOneCommitStep and the single-commit note below).\n //\n // This MUST happen before takeNamedStashStep: that step runs\n // `git stash apply` without `--index`, which restores modifications to\n // already-tracked files to the WORKING TREE only, leaving the index equal\n // to HEAD. Capturing the tree after the stash would therefore snapshot an\n // empty (HEAD) index, so every per-group `git reset <tree> -- <path>`\n // would stage nothing and every group would be skipped — producing zero\n // commits while still reporting success.\n const stagedTree = await git.writeTree(cwd);\n\n // Root step: save pre-split state as a named stash backup,\n // then immediately re-apply so the working-tree files are still visible.\n await tx.run(takeNamedStashStep(cwd, stashName));\n\n // Unstage all files so per-commit staging can re-stage each group.\n await git.resetStaged(cwd);\n\n for (const entry of plan.commits) {\n await tx.run(applyOneCommitStep(entry, cwd, stagedTree));\n }\n\n // Happy path: drop the backup stash — it's no longer needed.\n await git.stashDropNamed(cwd, stashName);\n } catch (err) {\n const wrapped =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: { stderr: err instanceof Error ? err.message : String(err) },\n });\n await tx.rollback(wrapped, logger);\n throw wrapped;\n }\n } finally {\n await releaseLock();\n }\n } else {\n // Single commit — entry.files mirrors `git diff --cached --name-only`, so\n // every path is already staged. Re-running `git add` is both redundant\n // and fatal on staged deletions: once the deletion is in the index, the\n // file exists in neither the worktree nor the index, so pathspec\n // matching fails with \"pathspec did not match any files\".\n const entry = plan.commits[0];\n if (!entry) return;\n const msg = entry.description\n ? `${entry.message}\\n\\n${entry.description}`\n : entry.message;\n await git.applyCommit({ message: msg, files: [], cwd });\n }\n\n if (shouldPush) {\n const branch = await git.getBranch(cwd);\n await git.push(cwd, remote, branch);\n }\n}\n","import * as git from \"../infra/git.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ReviewFinding {\n file?: string;\n line?: string;\n description: string;\n suggestion?: string;\n}\n\nexport interface ReviewResult {\n critical: ReviewFinding[];\n suggestions: ReviewFinding[];\n nitpicks: ReviewFinding[];\n markdown: string;\n tokens: { input: number; output: number };\n}\n\nexport interface ReviewOptions {\n cwd: string;\n provider: LLMProvider;\n baseBranch?: string;\n prompt?: string;\n tier?: \"fast\" | \"balanced\" | \"powerful\";\n templatesPath?: string;\n repoRoot?: string;\n}\n\nconst MAX_DIFF_CHARS = 80_000;\n\n// Mirrors packages/core/templates/review.md so `gw review` stays functional\n// when a user-customized templates directory omits review.md or when the\n// bundled template is missing from a packaged build.\nconst DEFAULT_REVIEW_TEMPLATE = `You are a senior code reviewer. Analyze the diff and produce a code review with findings in these categories:\n\n## Critical\nIssues that must be fixed before merging (bugs, security, data loss).\n\n## Suggestions\nImprovements worth considering (performance, readability, patterns).\n\n## Nitpicks\nMinor style or convention issues.\n\nFor each finding, include:\n- File and line reference\n- Description of the issue\n- Suggested fix\n\nEnd with a summary: total findings count per category and overall recommendation (approve, request changes).\n\n{{diff}}\n`;\n\nfunction truncateDiff(diff: string): string {\n if (diff.length <= MAX_DIFF_CHARS) return diff;\n return diff.slice(0, MAX_DIFF_CHARS) + \"\\n\\n[diff truncated — too large for context window]\";\n}\n\n// ─── Response parsing ────────────────────────────────────────────────────────\n\ninterface ParsedReviewResponse {\n critical: ReviewFinding[];\n suggestions: ReviewFinding[];\n nitpicks: ReviewFinding[];\n}\n\nfunction extractSection(markdown: string, heading: string): string[] {\n const headingRegex = new RegExp(`##\\\\s*${heading}\\\\b([\\\\s\\\\S]*?)(?=##|$)`, \"i\");\n const match = markdown.match(headingRegex);\n if (!match || !match[1]) return [];\n return match[1]\n .split(\"\\n\")\n .map((l) => l.replace(/^[-*•]\\s*/, \"\").trim())\n .filter((l) => l.length > 0);\n}\n\nfunction linesToFindings(lines: string[]): ReviewFinding[] {\n return lines.map((line) => ({\n description: line,\n }));\n}\n\nfunction parseReviewMarkdown(text: string): ParsedReviewResponse {\n return {\n critical: linesToFindings(extractSection(text, \"Critical\")),\n suggestions: linesToFindings(extractSection(text, \"Suggestions\")),\n nitpicks: linesToFindings(extractSection(text, \"Nitpicks\")),\n };\n}\n\nfunction buildMarkdown(parsed: ParsedReviewResponse): string {\n const sections: string[] = [];\n\n sections.push(\"## Critical\");\n if (parsed.critical.length > 0) {\n sections.push(...parsed.critical.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No critical issues found._\");\n }\n\n sections.push(\"\\n## Suggestions\");\n if (parsed.suggestions.length > 0) {\n sections.push(...parsed.suggestions.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No suggestions._\");\n }\n\n sections.push(\"\\n## Nitpicks\");\n if (parsed.nitpicks.length > 0) {\n sections.push(...parsed.nitpicks.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No nitpicks._\");\n }\n\n return sections.join(\"\\n\");\n}\n\n// ─── Core review function ────────────────────────────────────────────────────\n\nexport async function review(opts: ReviewOptions): Promise<ReviewResult> {\n const { cwd, provider, prompt, tier: requestedTier } = opts;\n\n // Resolve base branch\n const baseBranch = opts.baseBranch ?? await resolveBaseBranch(cwd);\n\n // Get diff\n let diff: string;\n try {\n diff = await git.getDiff(cwd, baseBranch);\n } catch (err: unknown) {\n if (isUnknownRevisionError(err)) {\n // Base branch is unknown locally (not fetched, typo, fresh clone). Fall back to\n // the working-tree diff so the caller still gets a review of pending edits.\n diff = await git.getDiff(cwd);\n } else {\n const reason = errorMessage(err);\n throw new GitwiseError({\n code: \"DIFF_FAILED\",\n message: `Failed to compute diff against ${baseBranch}: ${reason}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n }\n }\n\n if (!diff) {\n throw new GitwiseError({\n code: \"EMPTY_DIFF\",\n message: `No changes found between current branch and ${baseBranch}`,\n exitCode: EXIT_CODES.NOTHING_STAGED,\n });\n }\n\n // Load review template, falling back to the embedded default when the\n // resolved templates directory does not provide review.md.\n let templateContent: string;\n try {\n templateContent = await loadTemplate(\"review\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n } catch {\n templateContent = DEFAULT_REVIEW_TEMPLATE;\n }\n\n // Build system prompt from template (review.md is used as the user message with diff injected)\n const truncated = truncateDiff(diff);\n const userMessage = interpolate(templateContent, { diff: truncated })\n + (prompt ? `\\n\\nAdditional context: ${prompt}` : \"\");\n\n // Use default system prompt for the review\n const systemPrompt = \"You are a senior code reviewer. Analyze the provided diff carefully and return findings.\";\n\n const defaultTier = resolveModelTier(\"review\") as \"fast\" | \"balanced\" | \"powerful\";\n const activeTier = requestedTier ?? defaultTier;\n\n debug(\"Calling LLM for code review\", { tier: activeTier, diffLength: truncated.length });\n\n const response = await provider.chat({ systemPrompt, userMessage, tier: activeTier });\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n\n // Parse findings from response\n const parsed = parseReviewMarkdown(response.content);\n const markdown = buildMarkdown(parsed);\n\n return {\n critical: parsed.critical,\n suggestions: parsed.suggestions,\n nitpicks: parsed.nitpicks,\n markdown,\n tokens,\n };\n}\n\nasync function resolveBaseBranch(cwd: string): Promise<string> {\n try {\n return await git.detectBaseBranch(cwd);\n } catch {\n return \"main\";\n }\n}\n\nfunction errorMessage(err: unknown): string {\n if (err && typeof err === \"object\" && typeof (err as { message?: unknown }).message === \"string\") {\n return (err as { message: string }).message;\n }\n return String(err);\n}\n\n// Duck-typed instead of `instanceof Error` because jest's --experimental-vm-modules\n// can run modules in separate VM realms, where the Error constructor differs.\nfunction isUnknownRevisionError(err: unknown): boolean {\n if (err === null || typeof err !== \"object\") return false;\n const errObj = err as { message?: unknown; stderr?: unknown };\n const message = typeof errObj.message === \"string\" ? errObj.message : \"\";\n const stderr = typeof errObj.stderr === \"string\" ? errObj.stderr : \"\";\n const text = `${message}\\n${stderr}`;\n return /unknown revision|bad revision|not a valid object name|ambiguous argument/i.test(text);\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as git from \"../infra/git.js\";\nimport { isGhAvailable, createPR, updatePR } from \"../infra/github.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PrDraft {\n title: string;\n body: string;\n existingPrNumber?: number;\n tokens: { input: number; output: number };\n}\n\nexport interface PrOptions {\n cwd: string;\n provider: LLMProvider;\n baseBranch?: string;\n prompt?: string;\n templatesPath?: string;\n repoRoot?: string;\n}\n\nexport interface ApplyPrOptions {\n cwd: string;\n draft?: boolean;\n baseBranch?: string;\n}\n\nexport interface ApplyPrResult {\n url: string;\n}\n\n// ─── PR response parsing ─────────────────────────────────────────────────────\n\nfunction parsePrResponse(content: string): { title: string; body: string } {\n const titleMatch = content.match(/^TITLE:\\s*(.+)$/m);\n const title = titleMatch ? titleMatch[1]!.trim() : \"Update\";\n const separatorIdx = content.indexOf(\"---\");\n const body = separatorIdx >= 0 ? content.slice(separatorIdx + 3).trim() : content;\n return { title, body };\n}\n\n// ─── Detect existing PR ──────────────────────────────────────────────────────\n\nasync function detectExistingPr(cwd: string): Promise<number | undefined> {\n try {\n const result = await exec(\"gh\", [\"pr\", \"view\", \"--json\", \"number\", \"--jq\", \".number\"], { cwd });\n const numberStr = result.stdout.trim();\n if (numberStr) {\n const n = parseInt(numberStr, 10);\n if (!isNaN(n)) return n;\n }\n } catch {\n // No existing PR or gh not available\n }\n return undefined;\n}\n\n// ─── Core pr function ────────────────────────────────────────────────────────\n\nconst PR_SYSTEM_PROMPT = `You are a developer creating a pull request. Based on the commit log, generate a PR title and description.\n\nOutput format (nothing else):\nTITLE: <concise title, max 70 chars>\n---\n## Summary\n<1-3 bullet points>\n\n## Changes\n<changelog based on commits>\n\n## Test Plan\n<testing checklist>`;\n\nexport async function pr(opts: PrOptions): Promise<PrDraft> {\n const { cwd, provider, prompt } = opts;\n\n const baseBranch = opts.baseBranch ?? await resolveBaseBranch(cwd);\n const currentBranch = await git.getBranch(cwd);\n const commits = await git.getLog(cwd, `${baseBranch}..HEAD`);\n\n if (!commits) {\n throw new GitwiseError({\n code: \"NO_COMMITS\",\n message: `No commits found on this branch relative to ${baseBranch}`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // Load PR template or use built-in\n let systemPrompt = PR_SYSTEM_PROMPT;\n let userMessageFromTemplate = `Branch: ${currentBranch}\\n\\nCommits:\\n${commits}`;\n\n try {\n const templateContent = await loadTemplate(\"pr\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n // If template contains placeholders, use it as user message template\n if (templateContent && templateContent.includes(\"{{\")) {\n userMessageFromTemplate = interpolate(templateContent, {\n branch: currentBranch,\n commits,\n summary: \"\",\n changelog: \"\",\n test_plan: \"\",\n });\n }\n } catch {\n // Use built-in prompt\n }\n\n const userMessage = userMessageFromTemplate\n + (prompt ? `\\n\\nAdditional context: ${prompt}` : \"\");\n\n // Detect existing PR\n const existingPrNumber = await detectExistingPr(cwd);\n\n debug(\"Calling LLM for PR draft\", { tier: \"fast\", branch: currentBranch, existingPrNumber });\n\n const tier = resolveModelTier(\"pr\");\n const response = await provider.chat({ systemPrompt, userMessage, tier });\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n\n const { title, body } = parsePrResponse(response.content);\n\n return {\n title,\n body,\n existingPrNumber,\n tokens,\n };\n}\n\n// ─── applyPr ────────────────────────────────────────────────────────────────\n\nexport async function applyPr(draft: PrDraft, opts: ApplyPrOptions): Promise<ApplyPrResult> {\n const { cwd, draft: isDraft = false, baseBranch } = opts;\n\n const ghAvailable = await isGhAvailable();\n if (!ghAvailable) {\n throw new GitwiseError({\n code: \"GH_UNAVAILABLE\",\n message: \"gh CLI is not installed — cannot create or update a PR\",\n exitCode: EXIT_CODES.GH_FAILED,\n details: { draft },\n });\n }\n\n if (draft.existingPrNumber !== undefined) {\n const updated = await updatePR({\n prNumber: draft.existingPrNumber,\n title: draft.title,\n body: draft.body,\n cwd,\n });\n return { url: updated.url };\n }\n\n const created = await createPR({\n title: draft.title,\n body: draft.body,\n base: baseBranch,\n cwd,\n draft: isDraft,\n });\n return { url: created.url };\n}\n\nasync function resolveBaseBranch(cwd: string): Promise<string> {\n try {\n return await git.detectBaseBranch(cwd);\n } catch {\n return \"main\";\n }\n}\n","import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport * as git from \"../infra/git.js\";\nimport { isGhAvailable, createGitHubRelease } from \"../infra/github.js\";\nimport { fileExists, readJSON, writeJSON, ensureDir } from \"../infra/filesystem.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug, warn as logWarn } from \"../infra/logger.js\";\nimport { readRepoConfig } from \"../config/repo.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport { Transaction, type Logger, type Step } from \"../infra/transaction.js\";\nimport { acquireRepoLock } from \"../infra/lockfile.js\";\nimport {\n createReleaseStrategy,\n type ReleaseStrategyName,\n} from \"../strategies/release.js\";\nimport {\n applyGitignoreEntry,\n deleteReleasePlan,\n ensureGitignored,\n loadReleasePlan,\n saveReleasePlan,\n type PersistedReleasePlan,\n} from \"./release-plan.js\";\n\nconst RELEASE_PLAN_REL_PATH = \".gitwise/release-plan.json\";\n// ADR-003 preserves the notes file (.gitwise/release-<v>.md) after finish so the\n// user can keep editing or archiving it. Gitignoring the glob keeps every past\n// version's notes file out of the next prepare's clean-tree check without\n// touching the file itself.\nconst RELEASE_NOTES_GLOB_REL_PATH = \".gitwise/release-*.md\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type BumpType = \"major\" | \"minor\" | \"patch\";\n\nexport interface ReleasePlan {\n suggestedBump: BumpType;\n newVersion: string;\n currentVersion: string;\n changelog: string;\n notes: string;\n commits: string;\n tokens: { input: number; output: number };\n}\n\nexport interface ReleaseOptions {\n cwd: string;\n provider: LLMProvider;\n bump?: BumpType;\n language?: string;\n templatesPath?: string;\n repoRoot?: string;\n workspacePropagation?: boolean;\n}\n\nexport interface ApplyReleaseOptions {\n cwd: string;\n tagAndPush?: boolean;\n createGhRelease?: boolean;\n workspacePropagation?: boolean;\n /** Forwarded to finishRelease. Default true. Set false only for testing. */\n signTags?: boolean;\n}\n\n// ─── Version utilities ────────────────────────────────────────────────────────\n\nconst STRICT_SEMVER_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/;\n\nexport function bumpVersion(current: string, type: BumpType): string {\n const match = STRICT_SEMVER_RE.exec(current);\n if (!match) {\n throw new GitwiseError({\n code: \"INVALID_VERSION\",\n message: `Invalid current version: ${current}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n const major = Number(match[1]);\n const minor = Number(match[2]);\n const patch = Number(match[3]);\n switch (type) {\n case \"major\": return `${major + 1}.0.0`;\n case \"minor\": return `${major}.${minor + 1}.0`;\n case \"patch\": return `${major}.${minor}.${patch + 1}`;\n // Belt-and-suspenders: TS makes this unreachable for typed callers, but\n // any JS caller (or a cast like `parseVersionSuggestion`'s former one)\n // could smuggle in a bogus value. Surface it as INVALID_VERSION instead\n // of silently returning undefined and minting `release/undefined` /\n // `vundefined` artifacts downstream.\n default: throw new GitwiseError({\n code: \"INVALID_VERSION\",\n message: `Invalid bump type: ${String(type)}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n}\n\ninterface VersionSuggestion {\n suggestion: BumpType;\n reasoning: string;\n}\n\nfunction parseVersionSuggestion(raw: string): VersionSuggestion | null {\n try {\n const cleaned = raw.replace(/```(?:json)?\\n?/g, \"\").trim();\n const parsed = JSON.parse(cleaned) as Record<string, unknown>;\n const { suggestion, reasoning } = parsed;\n // Constrain `suggestion` to the BumpType union — a `typeof === \"string\"`\n // check used to accept \"huge\" / \"feature\" / \"\" and cast them straight to\n // BumpType, after which bumpVersion's switch fell through and returned\n // undefined. Returning null on garbage routes release() to heuristicBump,\n // its existing safety net.\n if (\n (suggestion === \"major\" || suggestion === \"minor\" || suggestion === \"patch\") &&\n typeof reasoning === \"string\"\n ) {\n return { suggestion, reasoning };\n }\n } catch { /* fallback */ }\n return null;\n}\n\n/**\n * Heuristic bump from commit log strings.\n * BREAKING CHANGE / ! marker → major\n * feat: → minor\n * fix:/chore:/etc → patch\n */\nexport function heuristicBump(commits: string): BumpType {\n if (/BREAKING CHANGE|!:/.test(commits)) return \"major\";\n if (/^feat[:(]/m.test(commits)) return \"minor\";\n return \"patch\";\n}\n\nconst CHANGELOG_HEADER = `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com),\nand this project adheres to [Semantic Versioning](https://semver.org/).\n\n`;\n\n// ─── Core release function ────────────────────────────────────────────────────\n\n/**\n * @deprecated Prefer the explicit two-phase lifecycle ({@link prepareRelease}\n * → caller-supplied confirm → {@link finishRelease}) or the unified\n * {@link runReleaseInProcess} helper. Kept exported so the legacy skill script\n * and any external callers using `release()` + `applyRelease()` keep working;\n * a future task may collapse it into `prepareRelease`.\n */\nexport async function release(opts: ReleaseOptions): Promise<ReleasePlan> {\n const { cwd, provider, language = \"en\" } = opts;\n\n const pkgPath = join(cwd, \"package.json\");\n if (!(await fileExists(pkgPath))) {\n throw new GitwiseError({\n code: \"NO_PACKAGE_JSON\",\n message: \"No package.json found\",\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n const pkg = await readJSON<{ version: string; name?: string }>(pkgPath);\n const currentVersion = pkg.version;\n const projectName = pkg.name ?? \"project\";\n\n const lastTag = await git.getLatestTag(cwd);\n const logRange = lastTag ? `${lastTag}..HEAD` : undefined;\n const commits = await git.getLog(cwd, logRange);\n\n if (!commits) {\n throw new GitwiseError({\n code: \"NO_COMMITS\",\n message: \"No new commits since last release\",\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n const templateOpts = {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n };\n\n const tier = resolveModelTier(\"release\");\n let totalInput = 0;\n let totalOutput = 0;\n\n // 1. Determine bump type\n let suggestedBump: BumpType;\n if (opts.bump) {\n suggestedBump = opts.bump;\n } else {\n const versionTemplate = await loadTemplate(\"release-version\", templateOpts);\n const versionPrompt = interpolate(versionTemplate, { currentVersion });\n\n debug(\"Calling LLM for version suggestion\");\n const versionResponse = await provider.chat({\n systemPrompt: \"You are a release engineer. Respond with JSON only.\",\n userMessage: `${versionPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += versionResponse.tokens.input;\n totalOutput += versionResponse.tokens.output;\n\n const suggestion = parseVersionSuggestion(versionResponse.content);\n suggestedBump = suggestion?.suggestion ?? heuristicBump(commits);\n }\n\n const newVersion = bumpVersion(currentVersion, suggestedBump);\n\n // 2. Generate changelog\n const changelogTemplate = await loadTemplate(\"release-changelog\", templateOpts);\n const changelogPrompt = interpolate(changelogTemplate, { projectName });\n\n debug(\"Calling LLM for changelog generation\");\n const changelogResponse = await provider.chat({\n systemPrompt: \"You are a technical writer generating a changelog. Follow Keep a Changelog format.\",\n userMessage: `${changelogPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += changelogResponse.tokens.input;\n totalOutput += changelogResponse.tokens.output;\n const changelog = changelogResponse.content;\n\n // 3. Generate release notes\n const notesTemplate = await loadTemplate(\"release-notes\", templateOpts);\n const notesPrompt = interpolate(notesTemplate, {\n version: newVersion,\n projectName,\n language,\n });\n\n debug(\"Calling LLM for release notes generation\");\n const notesResponse = await provider.chat({\n systemPrompt: \"You are a product communications specialist writing release notes.\",\n userMessage: `${notesPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += notesResponse.tokens.input;\n totalOutput += notesResponse.tokens.output;\n const notes = notesResponse.content;\n\n return {\n suggestedBump,\n newVersion,\n currentVersion,\n changelog,\n notes,\n commits,\n tokens: { input: totalInput, output: totalOutput },\n };\n}\n\n// ─── prepareRelease ──────────────────────────────────────────────────────────\n\nexport interface PrepareReleaseOptions extends ReleaseOptions {\n /** Strategy override; if omitted, resolved from RepoConfig (default \"github-flow\"). */\n strategy?: ReleaseStrategyName;\n /** Develop branch name override; if omitted, resolved from RepoConfig (default \"develop\"). */\n developBranch?: string;\n}\n\n/**\n * Step factory: create a gitflow release branch off `startPoint` and capture\n * the previously-checked-out branch so compensate can return there.\n *\n * Compensate: force-checkout the previously-checked-out branch (discards any\n * working-tree dirt accumulated on the release branch from later steps that\n * failed before their own compensate could fire) then `git branch -D` the\n * release branch. ADR-004 §Decision item 1 names this exact compensate.\n */\nexport function createReleaseBranchStep(\n cwd: string,\n branchName: string,\n startPoint: string,\n): Step<{ branchName: string; previousBranch: string }> {\n return {\n name: `create-branch:${branchName}`,\n apply: async () => {\n const previousBranch = await git.getBranch(cwd);\n await git.createBranch(cwd, branchName, startPoint);\n return { branchName, previousBranch };\n },\n compensate: async ({ branchName: branch, previousBranch }) => {\n // Force-checkout so any uncommitted working-tree mutation that the\n // catch path could not undo (e.g. a per-file compensate that itself\n // threw and is now reported as ROLLBACK_PARTIAL) is still discarded\n // before we try to delete the branch.\n await git.checkoutForce(cwd, previousBranch);\n await git.deleteBranch(cwd, branch, true);\n },\n };\n}\n\n/**\n * Step factory: write `contents` to `filePath` and capture any pre-existing\n * file's prior bytes so compensate can restore byte-for-byte.\n *\n * The captured state is either the original `Buffer` (file existed) or\n * `null` (file did not exist). Compensate restores the original bytes or\n * `fs.unlink`s the file accordingly. Mirrors the contract of\n * {@link writeWorkspaceVersionStep} for non-JSON payloads.\n */\nexport function writeFileStep(\n filePath: string,\n contents: string | Buffer,\n): Step<Buffer | null> {\n return {\n name: `write-file:${filePath}`,\n apply: async () => {\n const priorBytes = (await fileExists(filePath))\n ? await readFile(filePath)\n : null;\n await writeFile(filePath, contents);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(filePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(filePath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: apply both `ensureGitignored` calls (plan file + notes glob)\n * to `.gitignore`, capturing the file's prior bytes (or `null` when it did\n * not exist) so compensate can restore the original state.\n *\n * Compensate writes the captured bytes back, or unlinks the file when it\n * did not exist pre-apply. ADR-004 §Decision item 1 names \"gitignore\n * mutation → revert original bytes\" as the canonical compensate.\n */\nexport function mutateGitignoreStep(cwd: string): Step<Buffer | null> {\n const gitignorePath = join(cwd, \".gitignore\");\n return {\n name: \"mutate-gitignore\",\n apply: async () => {\n const priorBytes = (await fileExists(gitignorePath))\n ? await readFile(gitignorePath)\n : null;\n await ensureGitignored(cwd, RELEASE_PLAN_REL_PATH);\n await ensureGitignored(cwd, RELEASE_NOTES_GLOB_REL_PATH);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(gitignorePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(gitignorePath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: prepend a Keep-a-Changelog entry for `newVersion` to\n * `CHANGELOG.md`, creating the file with the standard header if it does not\n * exist. Captures the file's prior bytes (or `null`) so compensate can\n * restore it byte-for-byte.\n */\nexport function writeChangelogStep(\n cwd: string,\n newVersion: string,\n entryBody: string,\n): Step<Buffer | null> {\n const changelogPath = join(cwd, \"CHANGELOG.md\");\n return {\n name: \"write-changelog\",\n apply: async () => {\n const priorBytes = (await fileExists(changelogPath))\n ? await readFile(changelogPath)\n : null;\n const date = new Date().toISOString().split(\"T\")[0];\n const versionHeader = `## [${newVersion}] - ${date}\\n\\n${entryBody}\\n\\n`;\n if (priorBytes !== null) {\n const existing = priorBytes.toString(\"utf-8\");\n const headerEnd = existing.indexOf(\"## [\");\n if (headerEnd > 0) {\n await writeFile(\n changelogPath,\n existing.slice(0, headerEnd) + versionHeader + existing.slice(headerEnd),\n \"utf-8\",\n );\n } else {\n const body = existing.startsWith(CHANGELOG_HEADER)\n ? existing.slice(CHANGELOG_HEADER.length)\n : existing;\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader + body,\n \"utf-8\",\n );\n }\n } else {\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader,\n \"utf-8\",\n );\n }\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(changelogPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(changelogPath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: stage `files` and create the release commit on the current\n * branch. Captures the pre-commit `HEAD` SHA so compensate can `git reset\n * --hard` back to it, undoing the commit AND any staged/working-tree\n * changes that the prior file-write compensates have not yet reverted.\n *\n * The hard reset is intentional: when the release branch is later deleted\n * by {@link createReleaseBranchStep}'s compensate, a force-checkout would\n * need to be tolerant of working-tree dirt. Resetting the commit also\n * cleans the working tree so subsequent compensates run from a known state.\n */\nexport function commitReleaseStep(\n cwd: string,\n message: string,\n files: string[],\n): Step<string> {\n return {\n name: \"commit-release-bump\",\n apply: async () => {\n const preSha = await git.headSha(cwd);\n await git.applyCommit({ message, files, cwd });\n return preSha;\n },\n compensate: async (preSha) => {\n await git.resetHard(cwd, preSha);\n },\n };\n}\n\n/**\n * Step factory: persist the release plan JSON. ADR-004 §Decision item 1\n * names this as the LAST step of `prepareRelease` so a partial run never\n * leaves a plan file referencing a half-prepared branch.\n *\n * Compensate `fs.unlink`s the plan file (idempotent: ENOENT is swallowed).\n */\nexport function savePlanStep(\n cwd: string,\n plan: PersistedReleasePlan,\n): Step<void> {\n return {\n name: \"save-plan\",\n apply: async () => {\n await saveReleasePlan(cwd, plan);\n },\n compensate: async () => {\n await deleteReleasePlan(cwd);\n },\n };\n}\n\n/**\n * Run the planning half of the two-phase release lifecycle (ADR-001).\n *\n * Resolves the active strategy, validates preconditions (clean tree, develop\n * exists for gitflow, no pre-existing release branch), runs the LLM planner\n * via {@link release}, then drives every side-effectful step inside a single\n * {@link Transaction} under a `.gitwise/.lock`. On any failure the\n * transaction rolls back in LIFO order: plan file unlinked, release commit\n * reset (gitflow), `.gitignore` reverted, CHANGELOG.md reverted, workspace\n * + root manifests reverted, notes file unlinked, release branch deleted.\n * Compensate failures surface as a `ROLLBACK_PARTIAL` log warning emitted by\n * the transaction itself; the original cause is always rethrown unchanged.\n *\n * ADR-004 §Decision item 1: the release plan file is written LAST so its\n * presence on disk is a contract that every preceding step succeeded.\n */\nexport async function prepareRelease(\n opts: PrepareReleaseOptions,\n): Promise<PersistedReleasePlan> {\n const { cwd } = opts;\n\n // 1. Resolve strategy + develop branch from opts → repo config → defaults.\n const repoConfig = await readRepoConfig(cwd);\n const strategyName: ReleaseStrategyName =\n opts.strategy ?? repoConfig?.releaseStrategy ?? \"github-flow\";\n const developBranch =\n opts.developBranch ?? repoConfig?.developBranch ?? \"develop\";\n const strategy = createReleaseStrategy(strategyName);\n\n debug(\"release.prepare.start\", { strategy: strategyName, cwd });\n\n const releaseLock = await acquireRepoLock(cwd, {\n command: \"release prepare\",\n });\n\n try {\n // 2. Preflight — refuse to start if the working tree is dirty. Runs\n // before any LLM call so we don't pay tokens on a doomed run.\n //\n // Filter `.gitignore` from the dirty set: prepare mutates it via\n // `ensureGitignored`, and on github-flow that change is intentionally\n // deferred to finish (no commit happens in prepare). After a prior\n // github-flow `prepare → abort`, the leftover ` M .gitignore` (or\n // `?? .gitignore`) would otherwise block the next prepare and force the\n // user to manually `git checkout -- .gitignore`. Matches the symmetric\n // tolerance in finishRelease's step 2c.\n //\n // Also filter the `.gitwise/` directory entry: `acquireRepoLock` (just\n // above) writes `.gitwise/.lock` to coordinate concurrent gitwise runs,\n // which would otherwise surface here as `?? .gitwise/` on a fresh repo\n // and reject every prepare. The lockfile is gitwise's own scratch, not\n // user state.\n const dirtyEntries = (await git.status(cwd))\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+$/, \"\"))\n .filter((line) => line.length >= 3)\n .filter((line) => {\n const path = line.slice(3).trim();\n if (path === \".gitignore\") return false;\n if (path === \".gitwise/\" || path === \".gitwise\") return false;\n if (path.startsWith(\".gitwise/\")) return false;\n return true;\n });\n if (dirtyEntries.length > 0) {\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before preparing a release — commit or stash first.\\n${dirtyEntries.join(\"\\n\")}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n\n // 3. Refuse to clobber an in-flight plan (ADR-003 plan-first delete\n // invariant). Running this before the LLM call keeps retries cheap.\n const existingPlan = await loadReleasePlan(cwd);\n if (existingPlan) {\n throw new GitwiseError({\n code: \"RELEASE_PLAN_EXISTS\",\n message: `An in-flight release plan already exists at .gitwise/release-plan.json for v${existingPlan.newVersion} (${existingPlan.strategy}). Finish it with \"gw release finish\" or discard it with \"gw release abort\" before preparing a new release.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n\n // 4. Strategy preconditions — gitflow requires the develop branch to exist.\n if (strategy.requiresDevelop()) {\n if (!(await git.branchExists(cwd, developBranch))) {\n throw new GitwiseError({\n code: \"STRATEGY_DEVELOP_MISSING\",\n message: `GitFlow requires a \"${developBranch}\" branch but it does not exist. Create it first (e.g. git checkout -b ${developBranch}).`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n }\n\n // 5. Capture the user's HEAD before any mutation so the plan records\n // where prepare started — useful for stale-plan diagnostics in finish.\n const baseCommit = await git.headSha(cwd);\n\n // 6. Run the LLM planner (also raises NO_PACKAGE_JSON / NO_COMMITS /\n // INVALID_VERSION before we touch the filesystem).\n const plan = await release(opts);\n\n // 7. Now that newVersion is known, derive the release branch name and\n // check it doesn't already exist (gitflow only). Failure here predates\n // any transactional mutation; raise the typed `RELEASE_BRANCH_CONFLICT`\n // surfaced by ADR-004 with a docs/recovery.md hint.\n const releaseBranch = strategy.releaseBranchFor(plan.newVersion);\n if (releaseBranch && (await git.branchExists(cwd, releaseBranch))) {\n throw new GitwiseError({\n code: \"RELEASE_BRANCH_CONFLICT\",\n message: `Release branch \"${releaseBranch}\" already exists. Delete it or pick a different version — see docs/recovery.md if a prior prepare crashed before its rollback finished.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n details: { releaseBranch, newVersion: plan.newVersion },\n });\n }\n\n // 8. Drive every side-effectful mutation through a single Transaction\n // so a failure at any step rolls every prior step back in LIFO order.\n const tx = new Transaction();\n await ensureDir(join(cwd, \".gitwise\"));\n let targetBranch: string;\n\n try {\n if (releaseBranch) {\n await tx.run(\n createReleaseBranchStep(cwd, releaseBranch, developBranch),\n );\n debug(\"release.prepare.branch.created\", {\n branch: releaseBranch,\n from: developBranch,\n });\n targetBranch = releaseBranch;\n } else {\n targetBranch = await git.getBranch(cwd);\n }\n\n const notesPath = join(cwd, \".gitwise\", `release-${plan.newVersion}.md`);\n await tx.run(writeFileStep(notesPath, plan.notes));\n\n let propagatedManifests: string[] = [];\n if (releaseBranch) {\n const pkgPath = join(cwd, \"package.json\");\n await tx.run(writeWorkspaceVersionStep(pkgPath, plan.newVersion));\n\n if (opts.workspacePropagation) {\n propagatedManifests = await runWorkspaceVersionStepsInto(\n tx,\n cwd,\n plan.newVersion,\n );\n }\n\n await tx.run(writeChangelogStep(cwd, plan.newVersion, plan.changelog));\n }\n\n await tx.run(mutateGitignoreStep(cwd));\n\n if (releaseBranch) {\n const stagePaths = [\"package.json\", \"CHANGELOG.md\"];\n if (await fileExists(join(cwd, \".gitignore\"))) {\n stagePaths.push(\".gitignore\");\n }\n stagePaths.push(...propagatedManifests);\n await tx.run(\n commitReleaseStep(\n cwd,\n `chore(release): v${plan.newVersion}`,\n stagePaths,\n ),\n );\n }\n\n const persistedPlan: PersistedReleasePlan = {\n schema: 1,\n strategy: strategyName,\n currentVersion: plan.currentVersion,\n newVersion: plan.newVersion,\n suggestedBump: plan.suggestedBump,\n changelog: plan.changelog,\n notes: plan.notes,\n commits: plan.commits,\n preparedAt: new Date().toISOString(),\n baseCommit,\n targetBranch,\n releaseBranchCreated: releaseBranch !== null,\n tokens: plan.tokens,\n };\n\n await tx.run(savePlanStep(cwd, persistedPlan));\n debug(\"release.prepare.plan.saved\", {\n newVersion: plan.newVersion,\n targetBranch,\n releaseBranchCreated: persistedPlan.releaseBranchCreated,\n });\n\n return persistedPlan;\n } catch (err) {\n const reason =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"RELEASE_PREPARE_FAILED\",\n message: `Failed to prepare release: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n debug(\"release.prepare.rollback.start\", {\n appliedSteps: tx.size,\n code: reason.code,\n });\n await tx.rollback(reason, txLogger);\n throw reason;\n }\n } finally {\n await releaseLock();\n }\n}\n\n// ─── applyRelease ────────────────────────────────────────────────────────────\n\n/**\n * @deprecated Prefer the explicit two-phase lifecycle ({@link prepareRelease}\n * → caller-supplied confirm → {@link finishRelease}) or the unified\n * {@link runReleaseInProcess} helper.\n *\n * Apply an in-memory {@link ReleasePlan} to the repository. Kept exported as\n * a thin adapter so the legacy skill script and any external callers that\n * still pair `release()` with `applyRelease()` keep working. Internally this\n * builds a {@link PersistedReleasePlan} from the in-memory plan, writes it\n * (and the user-editable notes file) to disk so {@link finishRelease} can\n * consume it, and then delegates the mutation pipeline to\n * {@link finishRelease}. Both phases now travel through the same code path.\n *\n * Preflight: throws `WORKING_TREE_DIRTY` if the working tree has uncommitted\n * changes, and (when `tagAndPush` is enabled) `TAG_EXISTS` if the target\n * `v<newVersion>` ref already exists. Both checks run before any file or git\n * mutation so a failed run leaves the repo untouched.\n */\nexport async function applyRelease(\n plan: ReleasePlan,\n opts: ApplyReleaseOptions,\n): Promise<void> {\n const { cwd, tagAndPush = true, createGhRelease = true, workspacePropagation = false, signTags } = opts;\n\n // Preflight — refuse to start if the repo isn't in a state where a release\n // commit + tag can be applied atomically. Unconditional tag check matches\n // finishRelease's stale-plan invariant: a pre-existing v<newVersion> tag\n // means the plan is inconsistent regardless of tagAndPush.\n const dirty = (await git.status(cwd)).trim();\n if (dirty) {\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before releasing — commit or stash first.\\n${dirty}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n const tag = `v${plan.newVersion}`;\n if (await git.tagExists(cwd, tag)) {\n throw new GitwiseError({\n code: \"TAG_EXISTS\",\n message: `Tag ${tag} already exists. Bump to a new version or delete the tag.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n\n // Build a PersistedReleasePlan equivalent of the in-memory plan and hand it\n // off to finishRelease. The legacy contract has only ever been exercised on\n // single-branch (github-flow) repos, so `strategy: \"github-flow\"` and\n // `releaseBranchCreated: false` are the correct fixed values here.\n await ensureDir(join(cwd, \".gitwise\"));\n await writeFile(\n join(cwd, \".gitwise\", `release-${plan.newVersion}.md`),\n plan.notes,\n \"utf-8\",\n );\n\n const persistedPlan: PersistedReleasePlan = {\n schema: 1,\n strategy: \"github-flow\",\n currentVersion: plan.currentVersion,\n newVersion: plan.newVersion,\n suggestedBump: plan.suggestedBump,\n changelog: plan.changelog,\n notes: plan.notes,\n commits: plan.commits,\n preparedAt: new Date().toISOString(),\n baseCommit: await git.headSha(cwd),\n targetBranch: await git.getBranch(cwd),\n releaseBranchCreated: false,\n tokens: plan.tokens,\n };\n\n await ensureGitignored(cwd, RELEASE_PLAN_REL_PATH);\n await ensureGitignored(cwd, RELEASE_NOTES_GLOB_REL_PATH);\n await saveReleasePlan(cwd, persistedPlan);\n\n await finishRelease({ cwd, tagAndPush, createGhRelease, workspacePropagation, signTags });\n}\n\n/**\n * Build the `FINISH_PUSH_FAILED` error thrown by step 9 of {@link finishRelease}.\n * Called after the plan file (step 6) and, for github-flow, the release commit\n * (step 5) already exist — so the message spells out reconciliation via fetch\n * + merge rather than pointing back at \"gw release prepare\"/\"gw release finish\",\n * neither of which can recover this state.\n */\nfunction finishPushFailure(opts: {\n stage: \"tag\" | \"push-main\" | \"push-develop\";\n tag: string;\n mainBranch: string;\n developBranch?: string;\n newVersion: string;\n err: unknown;\n}): GitwiseError {\n const { stage, tag, mainBranch, developBranch, newVersion, err } = opts;\n const cause = err instanceof Error ? err.message : String(err);\n const action =\n stage === \"tag\"\n ? `create the tag \"${tag}\"`\n : stage === \"push-main\"\n ? `push \"${mainBranch}\" (with tags) to origin`\n : `push \"${developBranch}\" to origin`;\n const recoverySteps =\n stage === \"tag\"\n ? [\n `git tag -a ${tag} -F .gitwise/release-${newVersion}.md`,\n `git push origin ${mainBranch} --follow-tags`,\n ]\n : [\n `git ls-remote --tags origin ${tag} # check whether the tag already reached origin`,\n `git fetch origin`,\n `git merge origin/${mainBranch} # do NOT rebase — that changes the hash ${tag} points to`,\n `git push origin ${mainBranch} --follow-tags`,\n ];\n return new GitwiseError({\n code: \"FINISH_PUSH_FAILED\",\n message: `Failed to ${action} while finishing v${newVersion}: ${cause}. The release plan file has already been deleted, so \"gw release finish\" cannot be re-run, and the release commit already exists locally, so \"gw release prepare\" will refuse with NO_COMMITS. Recover manually:\\n${recoverySteps.map((s) => ` ${s}`).join(\"\\n\")}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: { stage, tag, mainBranch, developBranch, newVersion },\n });\n}\n\n// ─── finishRelease ───────────────────────────────────────────────────────────\n\nexport interface FinishReleaseOptions {\n cwd: string;\n /** Tag locally and push (with `--follow-tags`); default true. */\n tagAndPush?: boolean;\n /** Invoke `gh release create` after the tag is pushed; default true. */\n createGhRelease?: boolean;\n /** Delete the local release branch after gitflow merges; default true. Ignored for github-flow. */\n deleteReleaseBranch?: boolean;\n /**\n * Propagate the new root version into every workspace package's\n * `package.json` (and sibling `plugin.json`) before the github-flow release\n * commit, then stage exactly those manifests alongside the root files so\n * they all land in the same commit. Workspace layout is read from\n * `package.json.workspaces` (array or yarn-style `{ packages: [...] }`);\n * falls back to `packages/*` when the field is missing. Default false.\n * Ignored for gitflow because prepare already committed manifests on the\n * release branch.\n */\n workspacePropagation?: boolean;\n /**\n * Sign the release tag with the local GPG key (`git tag -s`). Default true.\n * Set to false only for testing or environments without a GPG key — a\n * warning is emitted to stderr when signing is skipped.\n */\n signTags?: boolean;\n}\n\n/**\n * Consume a persisted release plan and finalize the release (ADR-001 / ADR-003).\n *\n * Lifecycle: load plan → validate against live repo state → reload notes from\n * `.gitwise/release-<version>.md` → on github-flow, bump `package.json` and\n * prepend the CHANGELOG entry then commit on the current branch → delete the\n * plan file (BEFORE any irreversible operation — merges, tags, pushes — so a\n * downstream failure cannot trigger a second `finish`; on gitflow this is\n * effectively the same as deleting first because the github-flow block is\n * skipped) → merge `plan.targetBranch` into every `strategy.mergeTargets`\n * entry that isn't `targetBranch` itself → annotate the tag with the reloaded\n * notes, push with `--follow-tags`, and on gitflow also push the develop\n * branch → optionally create the GitHub release (graceful: failure logs but\n * does not roll back) → on gitflow, delete the now fully-merged release\n * branch unless `deleteReleaseBranch === false`.\n *\n * Throws typed errors before mutating anything: `NO_RELEASE_PLAN`,\n * `STALE_PLAN_TAG_EXISTS`, `STALE_PLAN_BRANCH_MISMATCH`, `WORKING_TREE_DIRTY`,\n * `STRATEGY_DEVELOP_MISSING`, plus `INVALID_PLAN_SCHEMA` / `INVALID_PLAN_JSON`\n * surfaced by `loadReleasePlan`. On the github-flow path, a pre-commit hook\n * failure during step 5's release commit surfaces as `COMMIT_HOOK_FAILURE`\n * with the plan file STILL on disk — recover by resolving the hook issue\n * and running `git reset --hard HEAD` to clear the partial manifest/CHANGELOG\n * writes before re-running `gw release finish`, or run `gw release abort` to\n * discard the in-flight release. Once the plan file is deleted at step 6, a\n * failed strategy merge (typically gitflow's develop merge when develop has\n * advanced) surfaces as `FINISH_MERGE_CONFLICT` — the repo is left mid-merge\n * for manual recovery (`git merge --continue` then tag + push by hand) since\n * the plan can no longer be re-run. A failure in step 9 (tag creation, or a\n * rejected push — typically a non-fast-forward because origin's mainBranch\n * advanced, e.g. a CI bot commit, while this release was being prepared or\n * finished) surfaces as `FINISH_PUSH_FAILED` with the exact fetch/merge/push\n * recovery commands embedded in the message; merge (never rebase) is required\n * because the tag, if already created, is pinned to the release commit's hash.\n */\nexport async function finishRelease(opts: FinishReleaseOptions): Promise<void> {\n const {\n cwd,\n tagAndPush = true,\n createGhRelease = true,\n deleteReleaseBranch = true,\n workspacePropagation = false,\n signTags = true,\n } = opts;\n\n if (signTags === false) {\n process.stderr.write(\n \"[gitwise] WARNING: --no-sign / signTags:false is a testing-only escape hatch. Release tags will NOT be GPG-signed. Do not use in production releases.\\n\",\n );\n }\n\n // 1. Load the persisted plan (also raises INVALID_PLAN_SCHEMA / INVALID_PLAN_JSON).\n const plan = await loadReleasePlan(cwd);\n if (!plan) {\n throw new GitwiseError({\n code: \"NO_RELEASE_PLAN\",\n message: `No release plan found at .gitwise/release-plan.json. Run \"gw release prepare\" first.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n debug(\"release.finish.start\", {\n strategy: plan.strategy,\n newVersion: plan.newVersion,\n targetBranch: plan.targetBranch,\n });\n\n const strategy = createReleaseStrategy(plan.strategy);\n const tag = `v${plan.newVersion}`;\n\n // 2. Validate the plan against live repo state. All checks run before any\n // mutation so a stale-plan rejection leaves the file in place for `abort`.\n\n // 2a. Tag must not already exist (checked unconditionally — the plan is\n // stale even if the user opted out of pushing).\n if (await git.tagExists(cwd, tag)) {\n debug(\"release.finish.validate.failed\", {\n code: \"STALE_PLAN_TAG_EXISTS\",\n tag,\n });\n throw new GitwiseError({\n code: \"STALE_PLAN_TAG_EXISTS\",\n message: `Tag ${tag} already exists — the saved plan is stale. Run \"gw release abort\" or delete the tag before retrying.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // 2b. Current branch must match the plan's target branch.\n const currentBranch = await git.getBranch(cwd);\n if (currentBranch !== plan.targetBranch) {\n debug(\"release.finish.validate.failed\", {\n code: \"STALE_PLAN_BRANCH_MISMATCH\",\n expected: plan.targetBranch,\n actual: currentBranch,\n });\n throw new GitwiseError({\n code: \"STALE_PLAN_BRANCH_MISMATCH\",\n message: `Release plan targets \"${plan.targetBranch}\" but the current branch is \"${currentBranch}\". Check out the target branch before running finish.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // 2c. Working tree must be clean of user changes. Filter out paths prepare\n // legitimately leaves dirty: the notes file (user is meant to edit it), the\n // plan file itself (gitignored after the first prepare but still surfaces as\n // untracked the very first time), and the .gitwise/ directory entry (git\n // collapses fully-untracked dirs).\n const expectedDirtyPaths = new Set<string>([\n \".gitwise/\",\n \".gitwise/release-plan.json\",\n `.gitwise/release-${plan.newVersion}.md`,\n ]);\n // `.gitignore` is conditionally tolerated. Prepare's `ensureGitignored`\n // mutates it on github-flow (the change is deferred to step 6 here because\n // prepare cannot commit on a trunk-based flow). Any *other* user edit to\n // `.gitignore` between prepare and finish would otherwise ride silently\n // into the release commit. Predict the exact bytes `ensureGitignored`\n // would have written from HEAD's `.gitignore` and only tolerate the dirty\n // entry when the working-tree file matches that prediction byte-for-byte\n // — mismatches fall through to WORKING_TREE_DIRTY so the surprise surfaces.\n if (await gitignoreMatchesPrepareOutput(cwd)) {\n expectedDirtyPaths.add(\".gitignore\");\n }\n const dirtyEntries = (await git.status(cwd))\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+$/, \"\"))\n .filter((line) => line.length >= 3)\n .filter((line) => !expectedDirtyPaths.has(line.slice(3).trim()));\n if (dirtyEntries.length > 0) {\n debug(\"release.finish.validate.failed\", {\n code: \"WORKING_TREE_DIRTY\",\n });\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before finishing a release — commit or stash first.\\n${dirtyEntries.join(\"\\n\")}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n\n // 2d. Gitflow requires a develop branch to merge into and push.\n const repoConfig = await readRepoConfig(cwd);\n const developBranch = repoConfig?.developBranch ?? \"develop\";\n if (strategy.requiresDevelop()) {\n if (!(await git.branchExists(cwd, developBranch))) {\n debug(\"release.finish.validate.failed\", {\n code: \"STRATEGY_DEVELOP_MISSING\",\n developBranch,\n });\n throw new GitwiseError({\n code: \"STRATEGY_DEVELOP_MISSING\",\n message: `GitFlow requires a \"${developBranch}\" branch but it does not exist.`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n }\n\n // 3. Resolve the main branch. For github-flow the plan's targetBranch IS\n // main; for gitflow we auto-detect it via the same helper used elsewhere.\n const mainBranch = strategy.requiresDevelop()\n ? await git.detectBaseBranch(cwd)\n : plan.targetBranch;\n\n // 4. Reload notes from disk so any user edits between prepare and finish\n // make it into the tag annotation and gh release body. If the file is\n // missing (user deleted it, moved it out for editing, CI cleaned `.gitwise`,\n // …), fall back to the in-memory notes captured at prepare time so the tag\n // is still annotated with the LLM output rather than blowing up with a raw\n // ENOENT. Other read failures (permissions, I/O) surface as a typed\n // NOTES_READ_FAILED so `formatReleaseError` can show an actionable hint.\n const notesPath = join(cwd, \".gitwise\", `release-${plan.newVersion}.md`);\n let notes: string;\n try {\n notes = await readFile(notesPath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n debug(\"release.finish.notes.missing\", { path: notesPath });\n notes = plan.notes;\n } else {\n const cause = err instanceof Error ? err.message : String(err);\n debug(\"release.finish.notes.read.failed\", { path: notesPath, error: cause });\n throw new GitwiseError({\n code: \"NOTES_READ_FAILED\",\n message: `Failed to read release notes at ${notesPath}: ${cause}. Recreate the file from the plan or run \"gw release abort\" to discard the in-flight release.`,\n cause: err,\n });\n }\n }\n\n // 5. For github-flow, prepare did NOT mutate package.json / CHANGELOG.md;\n // do those writes now on the current branch and commit. The plan file is\n // intentionally NOT deleted yet: the release commit is local and reversible\n // (`git reset --hard HEAD`), so if a pre-commit hook rejects the commit\n // (`COMMIT_HOOK_FAILURE`) or any write here fails mid-way, the user can\n // recover by clearing the partial state and re-running `gw release finish`,\n // or by running `gw release abort`. ADR-003's \"plan gone before any\n // irreversible op\" invariant is still honored — the plan delete moves to\n // step 6, before merges/tags/pushes.\n if (!plan.releaseBranchCreated) {\n const pkgPath = join(cwd, \"package.json\");\n const pkg = await readJSON<Record<string, unknown>>(pkgPath);\n pkg[\"version\"] = plan.newVersion;\n await writeJSON(pkgPath, pkg);\n\n // Workspace propagation runs after the root bump and before the commit so\n // every package + sibling plugin.json lands in the same release commit.\n // The helper returns the exact list of manifests it touched so we can\n // stage them explicitly below — no `git add packages` sweep.\n let propagatedManifests: string[] = [];\n if (workspacePropagation) {\n propagatedManifests = await propagateVersionToWorkspaces(cwd, plan.newVersion);\n }\n\n const changelogPath = join(cwd, \"CHANGELOG.md\");\n const date = new Date().toISOString().split(\"T\")[0];\n const versionHeader = `## [${plan.newVersion}] - ${date}\\n\\n${plan.changelog}\\n\\n`;\n\n if (await fileExists(changelogPath)) {\n const existing = await readFile(changelogPath, \"utf-8\");\n const headerEnd = existing.indexOf(\"## [\");\n if (headerEnd > 0) {\n await writeFile(\n changelogPath,\n existing.slice(0, headerEnd) + versionHeader + existing.slice(headerEnd),\n \"utf-8\",\n );\n } else {\n const body = existing.startsWith(CHANGELOG_HEADER)\n ? existing.slice(CHANGELOG_HEADER.length)\n : existing;\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader + body,\n \"utf-8\",\n );\n }\n } else {\n await writeFile(changelogPath, CHANGELOG_HEADER + versionHeader, \"utf-8\");\n }\n\n const stagePaths = [\"package.json\", \"CHANGELOG.md\"];\n // prepare's ensureGitignored leaves .gitignore dirty on github-flow (no\n // commit happens there). Fold it into the release commit here so the\n // working tree is clean afterward — otherwise the next prepare trips\n // WORKING_TREE_DIRTY on a leftover ` M .gitignore`.\n if (await fileExists(join(cwd, \".gitignore\"))) {\n stagePaths.push(\".gitignore\");\n }\n // Stage the exact manifests propagation modified — never a broad\n // `git add <workspace-root>`, which would also pick up unrelated\n // untracked work in the same directory.\n stagePaths.push(...propagatedManifests);\n // Route through `applyCommit` (not raw `git.commit`) so a pre-commit hook\n // rejection surfaces as a typed `COMMIT_HOOK_FAILURE` — `formatReleaseError`\n // maps that to a recovery hint instead of the generic `UNKNOWN_HINT`.\n await git.applyCommit({\n message: `chore(release): v${plan.newVersion}`,\n files: stagePaths,\n cwd,\n });\n }\n\n // 6. Delete the plan file (ADR-003 invariant) BEFORE any irreversible\n // operation — merges, tags, pushes, gh release. Past this point a downstream\n // failure cannot trigger a second `finish` against this plan. For gitflow\n // (`releaseBranchCreated === true`) step 5 above is a no-op, so the delete\n // here lands in the same spot as the pre-fix ordering. For github-flow it\n // lands AFTER the now-successful release commit, shrinking the partial-\n // mutation window so a pre-commit hook failure leaves the plan recoverable.\n await deleteReleasePlan(cwd);\n\n // 7. Merge into each strategy target. Skip self-merges (github-flow's only\n // target is `plan.targetBranch` itself; nothing to merge there). A merge\n // failure here surfaces as a typed FINISH_MERGE_CONFLICT so the CLI can show\n // an actionable recovery hint. The plan file is already gone at this point\n // (step 6, ADR-003), so the repo is intentionally left mid-merge for the\n // user to resolve with `git merge --continue` and then tag + push manually.\n const mergeTargets = strategy.mergeTargets(mainBranch, developBranch);\n for (const target of mergeTargets) {\n if (target === plan.targetBranch) continue;\n await git.checkout(cwd, target);\n try {\n await git.mergeNoFf(cwd, plan.targetBranch);\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n debug(\"release.finish.merge.failed\", {\n target,\n source: plan.targetBranch,\n error: cause,\n });\n throw new GitwiseError({\n code: \"FINISH_MERGE_CONFLICT\",\n message: `Failed to merge \"${plan.targetBranch}\" into \"${target}\" while finishing v${plan.newVersion}. The release plan file has already been deleted, so finish cannot be re-run. Resolve the conflicts, run \"git merge --continue\", then tag and push manually: git tag -a v${plan.newVersion} -F .gitwise/release-${plan.newVersion}.md && git push --follow-tags origin ${mainBranch}.\\n${cause}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: {\n target,\n source: plan.targetBranch,\n newVersion: plan.newVersion,\n },\n });\n }\n debug(\"release.finish.merge.target\", {\n target,\n source: plan.targetBranch,\n });\n }\n\n // 8. Move to the main branch so the tag lives on the release commit there.\n // For github-flow we're already there (we never left). For gitflow we end\n // the merge loop on the last target — usually develop — and need to swap.\n if ((await git.getBranch(cwd)) !== mainBranch) {\n await git.checkout(cwd, mainBranch);\n }\n\n // 9. Tag and push. Tag annotation = the (possibly edited) notes. The plan\n // file is already gone (step 6), so any failure here — most commonly a\n // rejected non-fast-forward push because origin/mainBranch advanced while\n // this release was being prepared/finished (e.g. a CI bot commit) — leaves\n // \"gw release finish\" unable to re-run. Wrap each git call so the thrown\n // FINISH_PUSH_FAILED error spells out the exact manual recovery: the\n // release commit (and possibly the tag) already exist locally, so the fix\n // is to fetch + merge (never rebase, which would change the hash the tag\n // points to) and push again — not to re-run prepare or recreate the commit.\n if (tagAndPush) {\n try {\n await git.createTag(cwd, tag, notes, { signed: signTags !== false });\n } catch (err) {\n throw finishPushFailure({ stage: \"tag\", tag, mainBranch, newVersion: plan.newVersion, err });\n }\n try {\n await git.pushWithTags(cwd, \"origin\", mainBranch);\n } catch (err) {\n throw finishPushFailure({ stage: \"push-main\", tag, mainBranch, newVersion: plan.newVersion, err });\n }\n debug(\"release.finish.tag.pushed\", {\n tag,\n branch: mainBranch,\n remote: \"origin\",\n });\n if (strategy.requiresDevelop()) {\n try {\n await git.push(cwd, \"origin\", developBranch);\n } catch (err) {\n throw finishPushFailure({ stage: \"push-develop\", tag, mainBranch, developBranch, newVersion: plan.newVersion, err });\n }\n }\n }\n\n // 10. Optional GitHub release. Failure here is non-fatal: the tag is\n // already pushed and the user can `gh release create` manually.\n if (createGhRelease) {\n if (await isGhAvailable()) {\n try {\n await createGitHubRelease({\n tag,\n title: tag,\n body: notes,\n cwd,\n });\n } catch (err) {\n debug(\"release.finish.gh.failed\", {\n tag,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n } else {\n debug(\"gh not available, skipping GitHub release creation\");\n }\n }\n\n // 11. Gitflow only: delete the release branch. `-d` (safe delete) refuses\n // unless the branch is fully merged into HEAD — and by now it has been\n // merged into both mainBranch and developBranch, so this succeeds.\n if (plan.releaseBranchCreated && deleteReleaseBranch) {\n try {\n await git.deleteBranch(cwd, plan.targetBranch);\n } catch (err) {\n debug(\"release.finish.branch.delete.failed\", {\n branch: plan.targetBranch,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n}\n\n// ─── abortRelease ────────────────────────────────────────────────────────────\n\nexport interface AbortReleaseOptions {\n cwd: string;\n /** When true, also delete the release branch (gitflow only). Default false. */\n deleteBranch?: boolean;\n}\n\n/**\n * Discard an in-flight release (ADR-001 / ADR-003).\n *\n * Loads the persisted plan and removes it from disk. When `deleteBranch` is\n * true AND prepare created a release branch, verifies that branch is fully\n * merged into every strategy merge target (main, and develop for gitflow)\n * BEFORE deleting the plan. If the branch still has unmerged commits, throws\n * `RELEASE_BRANCH_UNMERGED` and leaves both the plan file and the branch in\n * place so the user can recover. Notes (`.gitwise/release-<v>.md`) are never\n * touched — the user may still want them.\n */\nexport async function abortRelease(opts: AbortReleaseOptions): Promise<void> {\n const { cwd, deleteBranch = false } = opts;\n\n debug(\"release.abort.start\", { cwd, deleteBranch });\n\n // 1. Load the plan; absent plan is the only fatal precondition.\n const plan = await loadReleasePlan(cwd);\n if (!plan) {\n throw new GitwiseError({\n code: \"NO_RELEASE_PLAN\",\n message: `No release plan found at .gitwise/release-plan.json. Nothing to abort.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n const shouldDeleteBranch = deleteBranch && plan.releaseBranchCreated;\n\n // 2. Safety check FIRST — refuse upfront if the release branch has commits\n // not yet merged into every strategy target. This runs before the plan file\n // is deleted so the user can investigate or retry the abort.\n let mainBranch = \"\";\n if (shouldDeleteBranch) {\n const strategy = createReleaseStrategy(plan.strategy);\n const repoConfig = await readRepoConfig(cwd);\n const developBranch = repoConfig?.developBranch ?? \"develop\";\n mainBranch = strategy.requiresDevelop()\n ? await git.detectBaseBranch(cwd)\n : plan.targetBranch;\n\n for (const target of strategy.mergeTargets(mainBranch, developBranch)) {\n if (target === plan.targetBranch) continue;\n if (!(await git.isBranchMerged(cwd, plan.targetBranch, target))) {\n throw new GitwiseError({\n code: \"RELEASE_BRANCH_UNMERGED\",\n message: `Refusing to delete release branch \"${plan.targetBranch}\" — it has commits not present in \"${target}\". Merge or cherry-pick them first, or remove the branch manually.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n }\n }\n\n // 3. Delete the plan file (idempotent: ENOENT is swallowed by the helper).\n await deleteReleasePlan(cwd);\n\n // 4. Optionally delete the release branch. `git branch -d` refuses to delete\n // the currently checked-out branch, so move to main first if we're still\n // sitting on the release branch (the usual state right after `prepare`).\n if (shouldDeleteBranch) {\n if ((await git.getBranch(cwd)) === plan.targetBranch) {\n await git.checkout(cwd, mainBranch);\n }\n await git.deleteBranch(cwd, plan.targetBranch);\n debug(\"release.abort.branch.deleted\", { branch: plan.targetBranch });\n }\n}\n\n// ─── runReleaseInProcess ─────────────────────────────────────────────────────\n\nexport interface RunReleaseInProcessOptions extends PrepareReleaseOptions {\n /**\n * Resolved with the persisted plan after `prepareRelease` writes it. Return\n * `false` (or have the promise reject with `p.isCancel`-style cancellation)\n * to abort: the helper calls {@link abortRelease} which removes the plan\n * file (and any gitflow release branch when `confirmAbortDeletesBranch` is\n * true). The on-disk notes file is always preserved.\n */\n confirm: (plan: PersistedReleasePlan) => Promise<boolean> | boolean;\n /** Forwarded to {@link finishRelease} when `confirm` returns true. */\n finishOptions?: Omit<FinishReleaseOptions, \"cwd\">;\n /**\n * When `confirm` returns false on a gitflow plan, also delete the release\n * branch that prepare created. Default false. Ignored for github-flow.\n *\n * Pass a callback to decide after the plan exists — useful for CLIs that\n * want to ask \"Also delete the release branch?\" only when a gitflow\n * release branch was actually created. The callback runs inside the abort\n * paths (post-confirm-false and confirm-threw); errors thrown from it are\n * treated as \"do not delete\" so the abort itself still completes.\n */\n confirmAbortDeletesBranch?:\n | boolean\n | ((plan: PersistedReleasePlan) => Promise<boolean> | boolean);\n}\n\n/**\n * Drive the two-phase release lifecycle inside a single process against the\n * same plan written to and read from `.gitwise/release-plan.json`.\n *\n * Runs {@link prepareRelease}, awaits the caller-supplied `confirm` callback\n * (which receives the persisted plan), and either calls {@link finishRelease}\n * (confirm true) or {@link abortRelease} (confirm false / throws). On\n * confirmed completion the plan file is deleted by `finishRelease`; on abort\n * it is removed by `abortRelease`. The on-disk notes file\n * (`.gitwise/release-<version>.md`) is preserved either way.\n *\n * This is the unified path used by both the legacy `applyRelease` adapter\n * (auto-confirms) and the upcoming `gw release` CLI root action (task_09).\n * Decoupling the prompt into a `confirm` callback keeps core free of CLI UI\n * dependencies (e.g. `@clack/prompts`).\n *\n * Returns the persisted plan when the release was applied, or `null` if the\n * caller declined via `confirm`.\n */\nexport async function runReleaseInProcess(\n opts: RunReleaseInProcessOptions,\n): Promise<PersistedReleasePlan | null> {\n const plan = await prepareRelease(opts);\n\n const resolveDeleteBranch = async (): Promise<boolean> => {\n const setting = opts.confirmAbortDeletesBranch;\n if (typeof setting !== \"function\") return setting ?? false;\n try {\n return (await setting(plan)) === true;\n } catch {\n // Never let a CLI-side prompt failure block the abort cleanup.\n return false;\n }\n };\n\n let confirmed: boolean;\n try {\n confirmed = await opts.confirm(plan);\n } catch (err) {\n await abortRelease({\n cwd: opts.cwd,\n deleteBranch: await resolveDeleteBranch(),\n });\n throw err;\n }\n\n if (!confirmed) {\n await abortRelease({\n cwd: opts.cwd,\n deleteBranch: await resolveDeleteBranch(),\n });\n return null;\n }\n\n await finishRelease({ cwd: opts.cwd, ...opts.finishOptions });\n return plan;\n}\n\n/**\n * Update the root `version` field of every workspace package's `package.json`\n * (and any sibling `plugin.json`) to match the new release version.\n *\n * The workspace layout is taken from `package.json.workspaces` so repos using\n * `apps/*` / `libs/*` / specific paths all work — not just the historical\n * `packages/*` convention. Both the npm/pnpm array form\n * (`workspaces: [\"apps/*\", \"libs/foo\"]`) and the legacy yarn object form\n * (`workspaces: { packages: [\"apps/*\"] }`) are supported. Missing or empty\n * workspaces falls back to `packages/*` so existing single-layout repos that\n * never declared the field keep working.\n *\n * Returns the cwd-relative paths of every manifest the function actually\n * modified so the caller can stage exactly those files (never a directory\n * sweep, which would also pick up unrelated untracked work).\n */\n/**\n * Predict whether the current `.gitignore` equals exactly what prepare's two\n * `ensureGitignored` calls would produce from HEAD's `.gitignore`. Used by\n * `finishRelease`'s working-tree check to tolerate prepare's expected\n * leftover while rejecting unrelated user edits that would otherwise ride\n * silently into the `chore(release): vX.Y.Z` commit.\n *\n * Returns true when the file on disk byte-matches the prediction (so the\n * caller can add `.gitignore` to its allow-list); false when it differs (so\n * the caller falls through to WORKING_TREE_DIRTY).\n *\n * Treats `.gitignore` missing from HEAD as an empty baseline (e.g. a brand-\n * new repo where prepare created the file). Treats `.gitignore` missing from\n * the working tree the same way and lets the equality check decide.\n */\nasync function gitignoreMatchesPrepareOutput(cwd: string): Promise<boolean> {\n const headContent = (await git.showFileAtHead(cwd, \".gitignore\")) ?? \"\";\n const gitignorePath = join(cwd, \".gitignore\");\n const currentContent = (await fileExists(gitignorePath))\n ? await readFile(gitignorePath, \"utf-8\")\n : \"\";\n let expected = applyGitignoreEntry(headContent, RELEASE_PLAN_REL_PATH);\n expected = applyGitignoreEntry(expected, RELEASE_NOTES_GLOB_REL_PATH);\n return currentContent === expected;\n}\n\n/**\n * Step factory for atomically bumping the `version` field of a single\n * manifest (package.json or sibling plugin.json) under a {@link Transaction}.\n *\n * `apply` reads the manifest's prior bytes (Buffer, not parsed JSON, so any\n * trailing newline or formatting in the on-disk file is preserved verbatim\n * for rollback), rewrites the `version` field in place via `writeJSON`, and\n * returns the captured prior bytes as the step result. `compensate` writes\n * those bytes back, restoring the file byte-for-byte regardless of what the\n * apply path produced. Steps are intended to run sequentially so ordering is\n * deterministic per ADR-004.\n */\nexport function writeWorkspaceVersionStep(\n manifestPath: string,\n newVersion: string,\n): Step<Buffer> {\n return {\n name: `write-version:${manifestPath}`,\n apply: async () => {\n const priorBytes = await readFile(manifestPath);\n const parsed = JSON.parse(priorBytes.toString(\"utf-8\")) as Record<\n string,\n unknown\n >;\n parsed[\"version\"] = newVersion;\n await writeJSON(manifestPath, parsed);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n await writeFile(manifestPath, priorBytes);\n },\n };\n}\n\nconst txLogger: Logger = {\n warn(message, context) {\n logWarn(`[gitwise] ${message}`, context);\n },\n};\n\n/**\n * Propagate `version` into every workspace manifest under a {@link Transaction}\n * so that a write failure on `packages[N]/package.json` reliably restores the\n * bytes of every previously-written manifest (ADR-004 §Decision item 2).\n *\n * Acquires `.gitwise/.lock` for the duration of the flow and releases it in a\n * `finally` block so a concurrent gitwise invocation fails fast with\n * `REPO_LOCKED`. Writes are sequential (not concurrent) to keep ordering\n * deterministic. On any apply failure, runs `Transaction.rollback` BEFORE\n * propagating the error so callers always see the original cause; a partial\n * rollback (compensate itself fails) surfaces as a single `ROLLBACK_PARTIAL`\n * warning emitted by `Transaction.rollback`.\n *\n * Returns the cwd-relative paths of every manifest the function actually\n * modified so the caller can stage exactly those files (never a directory\n * sweep, which would also pick up unrelated untracked work).\n */\nexport async function propagateVersionToWorkspaces(\n cwd: string,\n version: string,\n): Promise<string[]> {\n const releaseLock = await acquireRepoLock(cwd, {\n command: \"release propagate-version\",\n });\n\n try {\n const tx = new Transaction();\n try {\n return await runWorkspaceVersionStepsInto(tx, cwd, version);\n } catch (err) {\n const reason =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"WORKSPACE_VERSION_WRITE_FAILED\",\n message: `Failed to propagate version ${version} to workspaces: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n await tx.rollback(reason, txLogger);\n throw reason;\n }\n } finally {\n await releaseLock();\n }\n}\n\n/**\n * Inner variant of {@link propagateVersionToWorkspaces} that runs the\n * workspace version-bump steps inside a caller-provided {@link Transaction}.\n *\n * Use this when a larger flow (e.g. {@link prepareRelease}) already holds the\n * repo lock and owns its own transaction — calling\n * {@link propagateVersionToWorkspaces} from inside such a flow would\n * deadlock on `acquireRepoLock` (same-pid is treated as alive).\n *\n * Sorts workspace directories alphabetically so iteration order is\n * deterministic across filesystems — ADR-004 §Decision requires sequential\n * writes \"so ordering is deterministic.\" Without this, `readdir` order leaks\n * platform-specific behavior into both the rollback boundary AND the list\n * returned to callers (which drives `git add` order in the release commit).\n *\n * Returns the cwd-relative paths of every manifest the function modified so\n * the caller can stage exactly those files.\n */\nexport async function runWorkspaceVersionStepsInto(\n tx: Transaction,\n cwd: string,\n version: string,\n): Promise<string[]> {\n const patterns = await readWorkspacePatterns(cwd);\n const workspaceDirs = (await expandWorkspacePatterns(cwd, patterns)).sort();\n const modified: string[] = [];\n for (const dir of workspaceDirs) {\n const pkgPath = join(dir, \"package.json\");\n if (await fileExists(pkgPath)) {\n await tx.run(writeWorkspaceVersionStep(pkgPath, version));\n modified.push(relative(cwd, pkgPath));\n }\n // Keep a sibling plugin.json (Claude Code plugin manifest) in lockstep\n // with package.json so its surfaced version doesn't drift after release.\n const pluginPath = join(dir, \"plugin.json\");\n if (await fileExists(pluginPath)) {\n await tx.run(writeWorkspaceVersionStep(pluginPath, version));\n modified.push(relative(cwd, pluginPath));\n }\n }\n return modified;\n}\n\n/**\n * Detect whether `cwd` is the root of an npm/pnpm/yarn workspaces monorepo\n * (or otherwise uses a `packages/*` layout with at least one nested\n * `package.json`). Single source of truth for the CLI and the skills runner\n * when auto-defaulting `workspacePropagation` per ADR-005.\n *\n * Returns `true` exactly when {@link propagateVersionToWorkspaces} would have\n * at least one manifest to rewrite — i.e. some workspace pattern in the root\n * `package.json` (array form, yarn-object `{ packages: [...] }` form, or the\n * `packages/*` fallback) resolves to a directory containing a `package.json`.\n */\nexport async function detectWorkspaceRoot(cwd: string): Promise<boolean> {\n const patterns = await readWorkspacePatterns(cwd);\n const dirs = await expandWorkspacePatterns(cwd, patterns);\n for (const dir of dirs) {\n if (await fileExists(join(dir, \"package.json\"))) return true;\n }\n return false;\n}\n\nasync function readWorkspacePatterns(cwd: string): Promise<string[]> {\n const pkgPath = join(cwd, \"package.json\");\n if (!(await fileExists(pkgPath))) return [\"packages/*\"];\n let parsed: { workspaces?: unknown };\n try {\n parsed = await readJSON<{ workspaces?: unknown }>(pkgPath);\n } catch {\n return [\"packages/*\"];\n }\n const ws = parsed.workspaces;\n const fromArray = Array.isArray(ws)\n ? ws.filter((p): p is string => typeof p === \"string\" && p.length > 0)\n : [];\n if (fromArray.length > 0) return fromArray;\n if (ws && typeof ws === \"object\" && !Array.isArray(ws)) {\n const inner = (ws as { packages?: unknown }).packages;\n if (Array.isArray(inner)) {\n const fromObject = inner.filter(\n (p): p is string => typeof p === \"string\" && p.length > 0,\n );\n if (fromObject.length > 0) return fromObject;\n }\n }\n return [\"packages/*\"];\n}\n\nasync function expandWorkspacePatterns(\n cwd: string,\n patterns: string[],\n): Promise<string[]> {\n const { readdir } = await import(\"node:fs/promises\");\n const readdirFn = readdir as unknown as ReaddirWithTypes;\n const matched = new Set<string>();\n for (const pattern of patterns) {\n // npm/yarn workspaces support `!`-prefixed negations to exclude paths.\n // Skipping them is a strict superset of the previous packages/* behavior\n // (which had no notion of exclusion at all) and is safe: we never delete,\n // we only bump versions inside matched directories.\n if (pattern.startsWith(\"!\")) continue;\n const segments = pattern.split(\"/\").filter((s) => s.length > 0);\n if (segments.length === 0) continue;\n await walkWorkspaceSegments(cwd, segments, 0, matched, readdirFn);\n }\n return Array.from(matched);\n}\n\ntype ReaddirWithTypes = (\n path: string,\n options: { withFileTypes: true },\n) => Promise<Array<{ name: string; isDirectory(): boolean }>>;\n\nasync function walkWorkspaceSegments(\n current: string,\n segments: string[],\n index: number,\n out: Set<string>,\n readdirFn: ReaddirWithTypes,\n): Promise<void> {\n if (index >= segments.length) {\n out.add(current);\n return;\n }\n const segment = segments[index] ?? \"\";\n if (!segment.includes(\"*\")) {\n await walkWorkspaceSegments(\n join(current, segment),\n segments,\n index + 1,\n out,\n readdirFn,\n );\n return;\n }\n let entries: Array<{ name: string; isDirectory(): boolean }>;\n try {\n entries = await readdirFn(current, { withFileTypes: true });\n } catch {\n return;\n }\n const regex = segmentToRegex(segment);\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (!regex.test(entry.name)) continue;\n await walkWorkspaceSegments(\n join(current, entry.name),\n segments,\n index + 1,\n out,\n readdirFn,\n );\n }\n}\n\nfunction segmentToRegex(segment: string): RegExp {\n // `*` matches any run of non-slash chars; `?` matches a single non-slash char.\n // Other regex metacharacters are escaped so a literal `.` in a workspace name\n // (e.g. `pkg.v2`) matches literally rather than as a wildcard.\n const escaped = segment\n .split(/(\\*|\\?)/)\n .map((part) => {\n if (part === \"*\") return \"[^/]*\";\n if (part === \"?\") return \"[^/]\";\n return part.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"\");\n return new RegExp(`^${escaped}$`);\n}\n","/**\n * Release-lifecycle strategy abstraction. Narrow on purpose (per ADR-002):\n * it covers branch creation, merge targets, and the develop-branch requirement\n * — nothing else from the broader FlowStrategy design lives here yet.\n */\n\nexport type ReleaseStrategyName = \"github-flow\" | \"gitflow\";\n\nexport interface ReleaseStrategy {\n readonly name: ReleaseStrategyName;\n /** Optional release branch to create during prepare; null = no branch. */\n releaseBranchFor(version: string): string | null;\n /** Branches to merge the release into during finish, in order. */\n mergeTargets(mainBranch: string, developBranch?: string): string[];\n /** True if a develop branch must exist for this strategy to run. */\n requiresDevelop(): boolean;\n}\n\nconst githubFlow: ReleaseStrategy = Object.freeze({\n name: \"github-flow\",\n releaseBranchFor(_version: string): string | null {\n return null;\n },\n mergeTargets(mainBranch: string, _developBranch?: string): string[] {\n return [mainBranch];\n },\n requiresDevelop(): boolean {\n return false;\n },\n});\n\nconst gitflow: ReleaseStrategy = Object.freeze({\n name: \"gitflow\",\n releaseBranchFor(version: string): string {\n return `release/${version}`;\n },\n mergeTargets(mainBranch: string, developBranch?: string): string[] {\n return developBranch ? [mainBranch, developBranch] : [mainBranch];\n },\n requiresDevelop(): boolean {\n return true;\n },\n});\n\nconst STRATEGIES: Readonly<Record<ReleaseStrategyName, ReleaseStrategy>> = Object.freeze({\n \"github-flow\": githubFlow,\n gitflow,\n});\n\nexport function createReleaseStrategy(name: ReleaseStrategyName): ReleaseStrategy {\n return STRATEGIES[name];\n}\n","import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileExists, writeJSON } from \"../infra/filesystem.js\";\nimport { info } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type { ReleaseStrategyName } from \"../strategies/release.js\";\nimport type { BumpType } from \"./release.js\";\n\n/**\n * On-disk handoff between `gw release prepare` and `gw release finish`.\n * Lifecycle and validation rules are defined in ADR-003 — written last in\n * prepare, deleted first in finish; never edit by hand.\n */\nexport interface PersistedReleasePlan {\n schema: 1;\n strategy: ReleaseStrategyName;\n currentVersion: string;\n newVersion: string;\n suggestedBump: BumpType;\n changelog: string;\n notes: string;\n commits: string;\n preparedAt: string;\n baseCommit: string;\n targetBranch: string;\n releaseBranchCreated: boolean;\n tokens: { input: number; output: number };\n}\n\nconst PLAN_REL_PATH = \".gitwise/release-plan.json\";\n\nfunction planPath(cwd: string): string {\n return join(cwd, PLAN_REL_PATH);\n}\n\nexport async function saveReleasePlan(cwd: string, plan: PersistedReleasePlan): Promise<void> {\n await writeJSON(planPath(cwd), plan);\n}\n\nexport async function loadReleasePlan(cwd: string): Promise<PersistedReleasePlan | null> {\n const filePath = planPath(cwd);\n if (!(await fileExists(filePath))) return null;\n\n const raw = await readFile(filePath, \"utf-8\");\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_JSON\",\n message: `Release plan at ${filePath} is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n cause: err,\n });\n }\n\n const schema = (parsed as { schema?: unknown } | null)?.schema;\n if (schema !== 1) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_SCHEMA\",\n message: `Release plan schema ${String(schema)} is not supported by this gitwise binary (expected 1).`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n if (!isPersistedReleasePlan(parsed)) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_SCHEMA\",\n message: `Release plan at ${filePath} is missing or has wrong-typed required fields for schema 1.`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n return parsed;\n}\n\nfunction isPersistedReleasePlan(value: unknown): value is PersistedReleasePlan {\n if (!value || typeof value !== \"object\") return false;\n const p = value as Record<string, unknown>;\n if (p.schema !== 1) return false;\n if (p.strategy !== \"gitflow\" && p.strategy !== \"github-flow\") return false;\n if (p.suggestedBump !== \"major\" && p.suggestedBump !== \"minor\" && p.suggestedBump !== \"patch\") {\n return false;\n }\n if (typeof p.currentVersion !== \"string\") return false;\n if (typeof p.newVersion !== \"string\") return false;\n if (typeof p.changelog !== \"string\") return false;\n if (typeof p.notes !== \"string\") return false;\n if (typeof p.commits !== \"string\") return false;\n if (typeof p.preparedAt !== \"string\") return false;\n if (typeof p.baseCommit !== \"string\") return false;\n if (typeof p.targetBranch !== \"string\") return false;\n if (typeof p.releaseBranchCreated !== \"boolean\") return false;\n if (!p.tokens || typeof p.tokens !== \"object\") return false;\n const tokens = p.tokens as Record<string, unknown>;\n if (typeof tokens.input !== \"number\" || !Number.isFinite(tokens.input)) return false;\n if (typeof tokens.output !== \"number\" || !Number.isFinite(tokens.output)) return false;\n return true;\n}\n\nexport async function deleteReleasePlan(cwd: string): Promise<void> {\n try {\n await unlink(planPath(cwd));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n}\n\n/**\n * Pure string transform behind {@link ensureGitignored}: returns the\n * `.gitignore` content that would result from ensuring `entry` is covered.\n * If `entry` is already covered by an exact-match line or a wildcard for its\n * directory (`dir/` or `dir/*`), `content` is returned unchanged. Exported so\n * callers outside the filesystem layer (e.g. `finishRelease`'s working-tree\n * validator) can predict the exact bytes `ensureGitignored` writes without\n * touching disk — keeping the writer and the validator in single-source-of-\n * truth alignment.\n */\nexport function applyGitignoreEntry(content: string, entry: string): string {\n if (isCovered(content, entry)) return content;\n const needsLeadingNewline = content.length > 0 && !content.endsWith(\"\\n\");\n return `${content}${needsLeadingNewline ? \"\\n\" : \"\"}${entry}\\n`;\n}\n\n/**\n * Ensure `entry` is covered by the repo's `.gitignore`. Coverage is detected\n * by an exact-match line OR a wildcard for the entry's directory (`dir/` or\n * `dir/*`). When appending, prints a one-line notice and preserves the file's\n * existing trailing-newline behavior.\n */\nexport async function ensureGitignored(cwd: string, entry: string): Promise<void> {\n const gitignorePath = join(cwd, \".gitignore\");\n const exists = await fileExists(gitignorePath);\n const original = exists ? await readFile(gitignorePath, \"utf-8\") : \"\";\n const next = applyGitignoreEntry(original, entry);\n if (next === original) return;\n await writeFile(gitignorePath, next, \"utf-8\");\n info(`Added ${entry} to .gitignore`);\n}\n\nfunction isCovered(content: string, entry: string): boolean {\n const candidates = new Set<string>([entry]);\n const dir = dirname(entry);\n if (dir && dir !== \".\" && dir !== \"/\") {\n candidates.add(`${dir}/`);\n candidates.add(`${dir}/*`);\n }\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith(\"#\")) continue;\n if (candidates.has(line)) return true;\n }\n return false;\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport { debug } from \"../infra/logger.js\";\nimport { GitwiseError } from \"../errors.js\";\nimport type { LLMChatRequest, LLMChatResponse, LLMProvider, ModelConfig, ModelTier } from \"./types.js\";\n\nconst DEFAULT_MAX_TOKENS = 4096;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 1000;\n\nexport class AnthropicProvider implements LLMProvider {\n private readonly client: Anthropic;\n private readonly models: ModelConfig;\n\n constructor(apiKey: string | undefined, models: ModelConfig) {\n this.client = new Anthropic({\n apiKey: apiKey ?? process.env[\"ANTHROPIC_API_KEY\"],\n timeout: DEFAULT_TIMEOUT_MS,\n });\n this.models = models;\n }\n\n async chat(req: LLMChatRequest): Promise<LLMChatResponse> {\n const modelId = this.resolveModel(req.tier);\n debug(\"Calling Anthropic API\", { model: modelId, tier: req.tier });\n return this.callWithRetry(req, modelId);\n }\n\n private async callWithRetry(\n req: LLMChatRequest,\n modelId: string,\n ): Promise<LLMChatResponse> {\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n return await this.callApi(req, modelId);\n } catch (err: unknown) {\n lastError = err instanceof Error ? err : new Error(String(err));\n if (this.isRetryable(err)) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n debug(\"Retrying after error\", { attempt, delay, error: lastError.message });\n await this.sleep(delay);\n continue;\n }\n throw lastError;\n }\n }\n throw new GitwiseError({\n code: \"API_RATE_LIMITED\",\n message: lastError?.message ?? \"Max retries exceeded\",\n cause: lastError,\n });\n }\n\n private async callApi(\n req: LLMChatRequest,\n modelId: string,\n ): Promise<LLMChatResponse> {\n const response = await this.client.messages.create({\n model: modelId,\n max_tokens: DEFAULT_MAX_TOKENS,\n system: req.systemPrompt,\n messages: [{ role: \"user\", content: req.userMessage }],\n });\n const text = response.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n return {\n content: text,\n tokens: {\n input: response.usage.input_tokens,\n output: response.usage.output_tokens,\n },\n };\n }\n\n private resolveModel(tier: ModelTier): string {\n return this.models[tier];\n }\n\n private isRetryable(err: unknown): boolean {\n if (err instanceof Anthropic.APIError) {\n return err.status === 429 || err.status === 529;\n }\n return false;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import { AnthropicProvider } from \"./anthropic.js\";\nimport { ClaudeCodeProvider } from \"./claude-code.js\";\nimport type { LLMProvider, ProviderConfig } from \"./types.js\";\n\nexport function createProvider(config: ProviderConfig): LLMProvider {\n if (config.kind === \"claude-code\") {\n return new ClaudeCodeProvider(config.models, config.claudeCliPath);\n }\n // kind: \"api\" — uses Anthropic SDK\n return new AnthropicProvider(config.apiKey, config.models);\n}\n","// Inline the version so bundled runner scripts don't need a package.json on disk.\n// (createRequire(\"../package.json\") would fail after git-clone with no node_modules.)\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const version: string = packageJson.version;\n\nexport const __placeholder__ = Symbol.for(\"@denisvieiradev/gitwise-core#placeholder\");\n\n// Error exports\nexport { GitwiseError, EXIT_CODES, wrapError } from \"./errors.js\";\nexport type { GitwiseErrorArgs } from \"./errors.js\";\n\n// Infra exports\nexport * from \"./infra/logger.js\";\nexport * from \"./infra/filesystem.js\";\nexport { git } from \"./infra/index.js\";\nexport { github } from \"./infra/index.js\";\nexport { env } from \"./infra/index.js\";\nexport type { ChangedFile, ApplyCommitParams } from \"./infra/git.js\";\nexport { stashList } from \"./infra/git.js\";\nexport type { CreatePRParams, PRResult, UpdatePRParams, CreateReleaseParams } from \"./infra/github.js\";\nexport { Transaction } from \"./infra/transaction.js\";\nexport type {\n Step,\n Logger,\n RollbackFailure,\n RollbackResult,\n} from \"./infra/transaction.js\";\nexport { acquireRepoLock, STALE_LOCK_MS } from \"./infra/lockfile.js\";\nexport type { LockPayload, AcquireRepoLockOptions } from \"./infra/lockfile.js\";\n// Export resolveClaudeBinary for CLI use\nexport { resolveClaudeBinary } from \"./providers/claude-code.js\";\n\n// Config exports — note: ModelConfig here is the config-layer version\nexport type { UserConfig, RepoConfig, MergedConfig, Language, CommitConvention } from \"./config/types.js\";\nexport type { ModelConfig as ConfigModelConfig } from \"./config/types.js\";\nexport { DEFAULT_USER_CONFIG } from \"./config/types.js\";\nexport { getMergedConfig, getApiKey } from \"./config/merge.js\";\nexport { readUserConfig, writeUserConfig, writeApiKey } from \"./config/user.js\";\nexport { readRepoConfig } from \"./config/repo.js\";\n\n// Template exports\nexport { loadTemplate, loadAndInterpolate, interpolate } from \"./template/index.js\";\nexport type { LoadTemplateOptions } from \"./template/index.js\";\n\n// Command exports\nexport {\n commit,\n applyCommitPlan,\n parseCommitResponse,\n takeNamedStashStep,\n applyOneCommitStep,\n} from \"./commands/commit.js\";\nexport { review } from \"./commands/review.js\";\nexport { pr, applyPr } from \"./commands/pr.js\";\nexport {\n release,\n prepareRelease,\n applyRelease,\n finishRelease,\n abortRelease,\n runReleaseInProcess,\n bumpVersion,\n heuristicBump,\n detectWorkspaceRoot,\n propagateVersionToWorkspaces,\n writeWorkspaceVersionStep,\n} from \"./commands/release.js\";\nexport type {\n ReleaseOptions,\n ReleasePlan,\n PrepareReleaseOptions,\n ApplyReleaseOptions,\n FinishReleaseOptions,\n AbortReleaseOptions,\n RunReleaseInProcessOptions,\n BumpType,\n} from \"./commands/release.js\";\nexport { createReleaseStrategy } from \"./strategies/release.js\";\nexport type { ReleaseStrategy, ReleaseStrategyName } from \"./strategies/release.js\";\nexport {\n saveReleasePlan,\n loadReleasePlan,\n deleteReleasePlan,\n ensureGitignored,\n} from \"./commands/release-plan.js\";\nexport type { PersistedReleasePlan } from \"./commands/release-plan.js\";\nexport type { PrOptions, PrDraft, ApplyPrOptions, ApplyPrResult } from \"./commands/pr.js\";\nexport type { ReviewOptions, ReviewResult, ReviewFinding } from \"./commands/review.js\";\nexport type { CommitOptions, CommitPlan, CommitEntry, SplitMode, ApplyCommitPlanOptions, CommitStepResult, CommitAlternatives } from \"./commands/commit.js\";\n\n// Provider exports — ModelConfig here is the provider-layer version\nexport type { LLMProvider, LLMChatRequest, LLMChatResponse, ModelTier, ModelConfig, ProviderConfig } from \"./providers/types.js\";\nexport { createProvider } from \"./providers/factory.js\";\nexport { resolveModelTier, SUPPORTED_COMMANDS } from \"./providers/model-router.js\";\n"],"mappings":"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","names":["path","execFile","promisify","exec","promisify","execFile","readFile","readFile","mkdir","open","readFile","unlink","mkdir","unlink","open","readFile","path","path","version","os","join","os","join","os","join","join","os","readFile","dirname","join","os","__dirname","dirname","join","os","readFile","commit","message","MAX_DIFF_CHARS","truncateDiff","execFile","promisify","exec","promisify","execFile","resolveBaseBranch","readFile","unlink","writeFile","join","version","readFile","unlink","writeFile","dirname","join","join","readFile","unlink","writeFile","dirname","join","readFile","writeFile","unlink","path","deleteBranch","version","DEFAULT_TIMEOUT_MS"]}
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/errors.ts","../src/infra/logger.ts","../src/infra/filesystem.ts","../src/infra/git.ts","../src/infra/github.ts","../src/infra/env.ts","../src/infra/transaction.ts","../src/infra/lockfile.ts","../src/providers/claude-code.ts","../src/providers/cli-subprocess.ts","../src/providers/codex.ts","../src/providers/copilot.ts","../src/providers/kiro.ts","../src/config/types.ts","../src/config/merge.ts","../src/config/user.ts","../src/providers/types.ts","../src/config/repo.ts","../src/template/loader.ts","../src/template/interpolate.ts","../src/providers/model-router.ts","../src/commands/commit.ts","../src/commands/review.ts","../src/commands/pr.ts","../src/commands/release.ts","../src/strategies/release.ts","../src/commands/release-plan.ts","../src/commands/token-format.ts","../src/providers/anthropic.ts","../src/providers/factory.ts","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@denisvieiradev/gitwise-core\",\n \"version\": \"1.3.0\",\n \"description\": \"Shared logic for gitwise: non-interactive commit/review/pr/release commands, LLM providers, git/github primitives, prompt templates.\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./testing\": {\n \"types\": \"./dist/testing/index.d.ts\",\n \"import\": \"./dist/testing/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"node --experimental-vm-modules ../../node_modules/.bin/jest --passWithNoTests\",\n \"lint\": \"tsc --noEmit\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"keywords\": [\n \"gitwise\",\n \"git\",\n \"ai\",\n \"claude\",\n \"commit\",\n \"pull-request\",\n \"release\",\n \"code-review\"\n ],\n \"author\": \"Denis Vieira <denisvieira05@gmail.com> (https://github.com/denisvieiradev)\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/denisvieiradev/gitwise.git\",\n \"directory\": \"packages/core\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/denisvieiradev/gitwise/issues\"\n },\n \"homepage\": \"https://github.com/denisvieiradev/gitwise#readme\",\n \"engines\": {\n \"node\": \">=22.12.0\"\n },\n \"dependencies\": {\n \"@anthropic-ai/sdk\": \"^0.127.0\"\n }\n}\n","export const EXIT_CODES: Readonly<Record<string, number>> = Object.freeze({\n OK: 0,\n UNKNOWN: 1,\n NOTHING_STAGED: 10,\n INVALID_INTENT: 11,\n GIT_FAILED: 20,\n GH_FAILED: 21,\n REPO_STATE_INVALID: 22,\n API_FAILED: 30,\n API_KEY_MISSING: 31,\n API_RATE_LIMITED: 32,\n USER_ABORT: 40,\n CONFIG_INVALID: 50,\n RELEASE_PLAN_STALE: 60,\n RELEASE_BRANCH_CONFLICT: 61,\n SENSITIVE_FILE_BLOCKED: 70,\n REPO_LOCKED: 80,\n ROLLBACK_PARTIAL: 81,\n});\n\nexport interface GitwiseErrorArgs {\n code: string;\n message: string;\n exitCode?: number;\n cause?: unknown;\n details?: Record<string, unknown>;\n}\n\nexport class GitwiseError extends Error {\n readonly code: string;\n readonly exitCode: number;\n override readonly cause?: unknown;\n readonly details?: Record<string, unknown>;\n\n constructor(args: GitwiseErrorArgs) {\n super(args.message);\n this.name = \"GitwiseError\";\n this.code = args.code;\n this.exitCode = args.exitCode ?? EXIT_CODES[args.code] ?? 1;\n this.cause = args.cause;\n this.details = args.details;\n }\n\n toJSON(): {\n name: string;\n code: string;\n exitCode: number;\n message: string;\n details?: Record<string, unknown>;\n } {\n return {\n name: this.name,\n code: this.code,\n exitCode: this.exitCode,\n message: this.message,\n ...(this.details !== undefined ? { details: this.details } : {}),\n };\n }\n}\n\nexport function wrapError(err: unknown): GitwiseError {\n if (err instanceof GitwiseError) return err;\n if (err instanceof Error) {\n return new GitwiseError({\n code: \"UNKNOWN\",\n message: err.message,\n cause: err,\n });\n }\n return new GitwiseError({\n code: \"UNKNOWN\",\n message: typeof err === \"string\" ? err : \"Unknown error\",\n cause: err,\n });\n}\n","let verboseEnabled = false;\n\n// Support GITWISE_DEBUG=1 env variable to enable debug output\nif (process.env[\"GITWISE_DEBUG\"] === \"1\") {\n verboseEnabled = true;\n}\n\nexport function setVerbose(enabled: boolean): void {\n verboseEnabled = enabled;\n}\n\nexport function isVerbose(): boolean {\n return verboseEnabled;\n}\n\nexport function info(message: string, context?: Record<string, unknown>): void {\n if (context) {\n console.log(message, context);\n } else {\n console.log(message);\n }\n}\n\nexport function error(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (context) {\n console.error(message, context);\n } else {\n console.error(message);\n }\n}\n\nexport function warn(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (context) {\n console.warn(message, context);\n } else {\n console.warn(message);\n }\n}\n\nexport function debug(\n message: string,\n context?: Record<string, unknown>,\n): void {\n if (!verboseEnabled) return;\n if (context) {\n process.stderr.write(`[debug] ${message} ${JSON.stringify(context)}\\n`);\n } else {\n process.stderr.write(`[debug] ${message}\\n`);\n }\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T> {\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\nexport async function writeJSON<T>(filePath: string, data: T): Promise<void> {\n await ensureDir(dirname(filePath));\n const content = JSON.stringify(data, null, 2) + \"\\n\";\n await writeFile(filePath, content, \"utf-8\");\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true });\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { debug } from \"./logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\nconst GIT_TIMEOUT_MS = 30_000;\nconst GIT_MAX_BUFFER = 10 * 1024 * 1024;\n\ninterface ExecResult {\n stdout: string;\n stderr: string;\n}\n\nfunction execStderr(err: unknown): string | undefined {\n const stderr = (err as { stderr?: unknown } | null)?.stderr;\n if (typeof stderr === \"string\" && stderr.length > 0) return stderr;\n return undefined;\n}\n\nasync function run(args: string[], cwd: string): Promise<string> {\n debug(\"git command\", { args, cwd });\n try {\n const result: ExecResult = await exec(\"git\", args, { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER });\n return result.stdout.trim();\n } catch (err: unknown) {\n if (err instanceof Error && \"killed\" in err && (err as { killed: boolean }).killed) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Git command timed out after ${GIT_TIMEOUT_MS / 1000}s: git ${args.join(\" \")}`,\n cause: err,\n details: { command: `git ${args.join(\" \")}`, timedOut: true },\n });\n }\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: {\n command: `git ${args.join(\" \")}`,\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n}\n\nexport async function getBranch(cwd: string): Promise<string> {\n return run([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n}\n\nexport async function createBranch(\n cwd: string,\n branchName: string,\n startPoint?: string,\n): Promise<void> {\n const args = [\"checkout\", \"-b\", branchName];\n if (startPoint) args.push(startPoint);\n await run(args, cwd);\n}\n\nexport async function checkout(cwd: string, branchName: string): Promise<void> {\n await run([\"checkout\", branchName], cwd);\n}\n\nexport async function checkoutForce(\n cwd: string,\n branchName: string,\n): Promise<void> {\n await run([\"checkout\", \"-f\", branchName], cwd);\n}\n\nexport async function resetHard(cwd: string, ref: string): Promise<void> {\n await run([\"reset\", \"--hard\", ref], cwd);\n}\n\nexport async function getDiff(cwd: string, base?: string): Promise<string> {\n const args = base ? [\"diff\", `${base}...HEAD`] : [\"diff\"];\n return run(args, cwd);\n}\n\nexport async function getStagedDiff(cwd: string): Promise<string> {\n return run([\"diff\", \"--cached\"], cwd);\n}\n\nexport async function getLog(\n cwd: string,\n range?: string,\n maxCount?: number,\n): Promise<string> {\n const args = [\"log\", \"--oneline\"];\n if (maxCount) args.push(`-${maxCount}`);\n if (range) args.push(range);\n return run(args, cwd);\n}\n\nexport async function add(cwd: string, files: string[]): Promise<void> {\n await run([\"add\", ...files], cwd);\n}\n\n/**\n * Write the current index to a tree object and return its SHA. Captures the\n * fully-staged state so individual paths can later be re-staged from it via\n * the index alone, without ever reading the working tree.\n */\nexport async function writeTree(cwd: string): Promise<string> {\n return run([\"write-tree\"], cwd);\n}\n\n/**\n * Stage the given paths into the index from a tree object, without touching\n * the working tree. Unlike `git add`, this never reads the worktree, so a path\n * that no longer matches a worktree file — a staged-then-deleted file, a staged\n * deletion, or a planned path that was never staged — is handled by the index\n * (added, removed, or no-op'd) instead of aborting with\n * \"pathspec did not match any files\". No-ops when `files` is empty.\n */\nexport async function stagePathsFromTree(\n cwd: string,\n tree: string,\n files: string[],\n): Promise<void> {\n if (files.length === 0) return;\n await run([\"reset\", \"-q\", tree, \"--\", ...files], cwd);\n}\n\nexport async function commit(cwd: string, message: string): Promise<string> {\n return run([\"commit\", \"-m\", message], cwd);\n}\n\nexport async function status(cwd: string): Promise<string> {\n // Bypass `run()`'s `stdout.trim()` because porcelain status lines start with\n // a leading space when the file is unstaged-modified (e.g. \" M .gitignore\").\n // Trimming the outer whitespace strips that space and downstream parsers\n // that rely on the fixed 3-char `XY ` prefix would misread the path.\n debug(\"git command\", { args: [\"status\", \"--porcelain\"], cwd });\n try {\n const result: ExecResult = await exec(\n \"git\",\n [\"status\", \"--porcelain\"],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return result.stdout.replace(/\\n+$/, \"\");\n } catch (err: unknown) {\n if (err instanceof Error && \"killed\" in err && (err as { killed: boolean }).killed) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Git command timed out after ${GIT_TIMEOUT_MS / 1000}s: git status --porcelain`,\n cause: err,\n details: { command: \"git status --porcelain\", timedOut: true },\n });\n }\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: {\n command: \"git status --porcelain\",\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n}\n\nexport async function push(\n cwd: string,\n remote: string,\n branch: string,\n): Promise<void> {\n await run([\"push\", remote, branch], cwd);\n}\n\nexport async function fetch(cwd: string, remote: string): Promise<void> {\n await run([\"fetch\", remote], cwd);\n}\n\nexport async function getChangedFiles(cwd: string): Promise<string[]> {\n const files = await parseStatus(cwd);\n return files.map((f) => f.file);\n}\n\nexport interface ChangedFile {\n file: string;\n indexStatus: string;\n workTreeStatus: string;\n}\n\nexport async function parseStatus(cwd: string): Promise<ChangedFile[]> {\n let result: { stdout: string };\n try {\n result = await exec(\"git\", [\"status\", \"--porcelain\"], { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER });\n } catch (err) {\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Failed to read git status: ${err instanceof Error ? err.message : String(err)}`,\n cause: err,\n details: {\n command: \"git status --porcelain\",\n ...(stderr !== undefined ? { stderr } : {}),\n },\n });\n }\n // Use raw stdout (no trim) — leading spaces in porcelain format are meaningful\n const output = result.stdout;\n if (!output || !output.trim()) return [];\n return output\n .split(\"\\n\")\n .filter((line) => line.length >= 3)\n .map((line) => {\n const indexStatus = line[0] as string;\n const workTreeStatus = line[1] as string;\n let file = line.slice(3).trim();\n // Handle renamed/copied files: \"R old -> new\" or \"C old -> new\"\n if (\n (indexStatus === \"R\" || indexStatus === \"C\") &&\n file.includes(\" -> \")\n ) {\n file = file.split(\" -> \").pop()!;\n }\n return { file, indexStatus, workTreeStatus };\n })\n .filter((entry) => entry.file.length > 0);\n}\n\nexport async function getStagedFiles(cwd: string): Promise<ChangedFile[]> {\n const files = await parseStatus(cwd);\n return files.filter((f) => f.indexStatus !== \" \" && f.indexStatus !== \"?\");\n}\n\nexport async function resetStaged(cwd: string): Promise<void> {\n await run([\"reset\", \"HEAD\"], cwd);\n}\n\nexport async function getStagedFilesList(cwd: string): Promise<string[]> {\n const output = await run([\"diff\", \"--cached\", \"--name-only\"], cwd);\n if (!output) return [];\n return output.split(\"\\n\").filter((f) => f.length > 0);\n}\n\nexport async function getUnstagedFiles(cwd: string): Promise<ChangedFile[]> {\n const files = await parseStatus(cwd);\n return files.filter(\n (f) =>\n (f.indexStatus === \"?\" && f.workTreeStatus === \"?\") ||\n f.workTreeStatus !== \" \",\n );\n}\n\nexport async function getLatestTag(cwd: string): Promise<string | null> {\n try {\n return await run([\"describe\", \"--tags\", \"--abbrev=0\"], cwd);\n } catch {\n return null;\n }\n}\n\nexport async function createTag(\n cwd: string,\n tag: string,\n message: string,\n options?: { signed?: boolean },\n): Promise<void> {\n const flag = options?.signed === true ? \"-s\" : \"-a\";\n await run([\"tag\", flag, tag, \"-m\", message], cwd);\n}\n\nexport async function tagExists(cwd: string, tag: string): Promise<boolean> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"--quiet\", `refs/tags/${tag}`], {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function pushWithTags(\n cwd: string,\n remote: string,\n branch: string,\n): Promise<void> {\n await run([\"push\", remote, branch, \"--follow-tags\"], cwd);\n}\n\nexport async function mergeNoFf(cwd: string, source: string): Promise<void> {\n await run([\"merge\", \"--no-ff\", source], cwd);\n}\n\nexport async function branchExists(cwd: string, branch: string): Promise<boolean> {\n try {\n await exec(\n \"git\",\n [\"show-ref\", \"--verify\", \"--quiet\", `refs/heads/${branch}`],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function headSha(cwd: string): Promise<string> {\n return run([\"rev-parse\", \"HEAD\"], cwd);\n}\n\nexport async function resetSoft(cwd: string, ref: string): Promise<void> {\n await run([\"reset\", \"--soft\", ref], cwd);\n}\n\nexport async function stashPushNamed(cwd: string, message: string): Promise<void> {\n await run([\"stash\", \"push\", \"--include-untracked\", \"-m\", message], cwd);\n}\n\nexport async function stashList(cwd: string): Promise<string> {\n return run([\"stash\", \"list\"], cwd);\n}\n\nasync function findStashRef(cwd: string, stashName: string): Promise<string> {\n const list = await stashList(cwd);\n const line = list.split(\"\\n\").find((l) => l.includes(stashName));\n if (!line) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Named stash not found in stash list: ${stashName}`,\n details: { stashName },\n });\n }\n const match = /^(stash@\\{\\d+\\})/.exec(line);\n if (!match?.[1]) {\n throw new GitwiseError({\n code: \"GIT_FAILED\",\n message: `Cannot parse stash ref from stash list line: ${line}`,\n details: { stashName, line },\n });\n }\n return match[1];\n}\n\nexport async function stashApplyNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n // Do not use --index: stashes created with --include-untracked are incompatible\n // with --index restoration for newly-staged (never committed) files.\n await run([\"stash\", \"apply\", ref], cwd);\n}\n\nexport async function stashPopNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n await run([\"stash\", \"pop\", ref], cwd);\n}\n\nexport async function stashDropNamed(cwd: string, stashName: string): Promise<void> {\n const ref = await findStashRef(cwd, stashName);\n await run([\"stash\", \"drop\", ref], cwd);\n}\n\n/**\n * Force-remove all untracked files and directories from the working tree.\n * Used before stash pop in compensate paths to avoid \"would be overwritten\"\n * conflicts from files that were left untracked after reset --hard.\n */\nexport async function cleanForced(cwd: string): Promise<void> {\n await run([\"clean\", \"-fd\"], cwd);\n}\n\n/**\n * Read a file's contents at `HEAD` via `git show HEAD:<path>`. Returns `null`\n * when the path does not exist in the HEAD tree (so callers can distinguish\n * \"missing in HEAD\" from \"exists but empty\"). Bypasses the helper `run()` to\n * preserve trailing newlines, which the working-tree validators compare\n * byte-for-byte.\n */\nexport async function showFileAtHead(\n cwd: string,\n path: string,\n): Promise<string | null> {\n debug(\"git command\", { args: [\"show\", `HEAD:${path}`], cwd });\n try {\n const result: ExecResult = await exec(\"git\", [\"show\", `HEAD:${path}`], {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n maxBuffer: GIT_MAX_BUFFER,\n });\n return result.stdout;\n } catch {\n return null;\n }\n}\n\nexport async function deleteBranch(\n cwd: string,\n branch: string,\n force = false,\n): Promise<void> {\n await run([\"branch\", force ? \"-D\" : \"-d\", branch], cwd);\n}\n\n/**\n * Is `branch` fully reachable from `target`? Resolves to true when every commit\n * on `branch` is already in `target` (i.e., the merge would be a no-op). Used\n * by abortRelease to refuse deleting a release branch that still has commits\n * not present in main/develop.\n */\nexport async function isBranchMerged(\n cwd: string,\n branch: string,\n target: string,\n): Promise<boolean> {\n try {\n await exec(\n \"git\",\n [\"merge-base\", \"--is-ancestor\", branch, target],\n { cwd, timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER },\n );\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detect the base branch of the repository (main or master).\n * Returns 'main' if both exist, falls back to 'master', throws if neither exists.\n */\nexport async function detectBaseBranch(cwd: string): Promise<string> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"main\"], { cwd, timeout: GIT_TIMEOUT_MS });\n return \"main\";\n } catch {\n // main doesn't exist, try master\n }\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"master\"], { cwd, timeout: GIT_TIMEOUT_MS });\n return \"master\";\n } catch {\n // master doesn't exist either\n }\n throw new GitwiseError({\n code: \"NO_BASE_BRANCH\",\n message: \"No base branch found: neither main nor master exists\",\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n}\n\nexport interface ApplyCommitParams {\n message: string;\n files: string[];\n cwd: string;\n}\n\n/**\n * Stage the given files and create a commit.\n * Throws a typed error on hook failure or other git errors.\n */\nexport async function applyCommit(params: ApplyCommitParams): Promise<void> {\n const { message, files, cwd } = params;\n try {\n if (files.length > 0) {\n await add(cwd, files);\n }\n await commit(cwd, message);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const stderr = execStderr(err);\n throw new GitwiseError({\n code: \"COMMIT_HOOK_FAILURE\",\n message: `Git commit failed: ${msg}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: stderr !== undefined ? { stderr } : undefined,\n });\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { debug } from \"./logger.js\";\nimport { GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\n\nexport interface CreatePRParams {\n title: string;\n body: string;\n base?: string;\n cwd: string;\n draft?: boolean;\n}\n\nexport interface PRResult {\n url: string;\n}\n\nexport async function isGhAvailable(): Promise<boolean> {\n try {\n await exec(\"gh\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function getGhVersion(): Promise<string | null> {\n try {\n const result = await exec(\"gh\", [\"--version\"]);\n const firstLine = result.stdout.split(\"\\n\")[0] ?? \"\";\n return firstLine.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport async function createPR(params: CreatePRParams): Promise<PRResult> {\n debug(\"Creating PR via gh\", { title: params.title });\n const args = [\"pr\", \"create\", \"--title\", params.title, \"--body\", params.body];\n if (params.base) {\n args.push(\"--base\", params.base);\n }\n if (params.draft) {\n args.push(\"--draft\");\n }\n const result = await exec(\"gh\", args, { cwd: params.cwd });\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: \"gh pr create returned empty output — check gh auth status\",\n details: { command: \"gh pr create\" },\n });\n }\n return { url };\n}\n\nexport interface UpdatePRParams {\n prNumber: string | number;\n title?: string;\n body?: string;\n cwd: string;\n}\n\nexport async function updatePR(params: UpdatePRParams): Promise<PRResult> {\n debug(\"Updating PR via gh\", { prNumber: params.prNumber });\n const args = [\"pr\", \"edit\", String(params.prNumber)];\n if (params.title) args.push(\"--title\", params.title);\n if (params.body) args.push(\"--body\", params.body);\n await exec(\"gh\", args, { cwd: params.cwd });\n const url = await getPrUrl(params.prNumber, params.cwd);\n return { url };\n}\n\nexport async function getPrUrl(prNumber: string | number, cwd: string): Promise<string> {\n const result = await exec(\n \"gh\",\n [\"pr\", \"view\", String(prNumber), \"--json\", \"url\", \"-q\", \".url\"],\n { cwd },\n );\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: `gh pr view ${prNumber} returned empty output — check gh auth status`,\n details: { command: `gh pr view ${prNumber}` },\n });\n }\n return url;\n}\n\nexport interface CreateReleaseParams {\n tag: string;\n title: string;\n body: string;\n cwd: string;\n}\n\nexport async function createGitHubRelease(\n params: CreateReleaseParams,\n): Promise<PRResult> {\n debug(\"Creating GitHub release via gh\", { tag: params.tag });\n const args = [\n \"release\",\n \"create\",\n params.tag,\n \"--title\",\n params.title,\n \"--notes\",\n params.body,\n ];\n const result = await exec(\"gh\", args, { cwd: params.cwd });\n const url = result.stdout?.trim();\n if (!url) {\n throw new GitwiseError({\n code: \"GH_FAILED\",\n message: \"gh release create returned empty output — check gh auth status\",\n details: { command: \"gh release create\" },\n });\n }\n return { url };\n}\n\n// Alias: openPr — used by downstream tasks expecting this name\nexport const openPr = createPR;\n","import { readFile, open, rename, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists, ensureDir } from \"./filesystem.js\";\n\nconst ENV_DIR = \".gitwise\";\nconst ENV_FILE = \".env\";\n\nfunction getEnvPath(projectRoot: string): string {\n return join(projectRoot, ENV_DIR, ENV_FILE);\n}\n\nfunction parseLine(line: string): [string, string] | null {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) return null;\n const eq = trimmed.indexOf(\"=\");\n if (eq < 1) return null;\n return [trimmed.slice(0, eq).trim(), trimmed.slice(eq + 1).trim()];\n}\n\nexport async function loadEnv(projectRoot: string): Promise<void> {\n const envPath = getEnvPath(projectRoot);\n if (!(await fileExists(envPath))) return;\n const content = await readFile(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const parsed = parseLine(line);\n if (!parsed) continue;\n const [key, value] = parsed;\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\nexport async function writeEnvVar(\n projectRoot: string,\n key: string,\n value: string,\n): Promise<void> {\n const envPath = getEnvPath(projectRoot);\n await ensureDir(join(projectRoot, ENV_DIR));\n\n let lines: string[] = [];\n if (await fileExists(envPath)) {\n const content = await readFile(envPath, \"utf-8\");\n lines = content.split(\"\\n\");\n }\n\n const prefix = `${key}=`;\n const idx = lines.findIndex((l) => l.trim().startsWith(prefix));\n const entry = `${key}=${value}`;\n\n if (idx >= 0) {\n lines[idx] = entry;\n } else {\n if (lines.length === 1 && lines[0] === \"\") {\n lines[0] = entry;\n } else {\n lines.push(entry);\n }\n }\n\n const final = lines.join(\"\\n\").replace(/\\n{3,}/g, \"\\n\\n\");\n const payload = final.endsWith(\"\\n\") ? final : final + \"\\n\";\n\n const tmpPath = `${envPath}.${process.pid}.${Date.now()}.tmp`;\n const fd = await open(tmpPath, \"w\", 0o600);\n try {\n await fd.writeFile(payload, \"utf-8\");\n } finally {\n await fd.close();\n }\n try {\n await rename(tmpPath, envPath);\n } catch (err) {\n await unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n\nexport async function readEnvVar(\n projectRoot: string,\n key: string,\n): Promise<string | undefined> {\n const envPath = getEnvPath(projectRoot);\n if (!(await fileExists(envPath))) return undefined;\n const content = await readFile(envPath, \"utf-8\");\n for (const line of content.split(\"\\n\")) {\n const parsed = parseLine(line);\n if (parsed && parsed[0] === key) return parsed[1];\n }\n return undefined;\n}\n\n/**\n * Read a key from process.env, with optional fallback to the project .env file.\n */\nexport async function read(\n key: string,\n projectRoot?: string,\n): Promise<string | undefined> {\n if (process.env[key] !== undefined) {\n return process.env[key];\n }\n if (projectRoot) {\n return readEnvVar(projectRoot, key);\n }\n return undefined;\n}\n","import { GitwiseError } from \"../errors.js\";\n\nexport interface Step<T> {\n name: string;\n apply: () => Promise<T>;\n compensate: (result: T) => Promise<void>;\n}\n\nexport interface Logger {\n warn(message: string, context?: Record<string, unknown>): void;\n}\n\nexport interface RollbackFailure {\n step: string;\n error: unknown;\n}\n\nexport interface RollbackResult {\n partial: boolean;\n failures: RollbackFailure[];\n}\n\ninterface AppliedStep {\n step: Step<unknown>;\n result: unknown;\n}\n\nexport class Transaction {\n private readonly applied: AppliedStep[] = [];\n\n async run<T>(step: Step<T>): Promise<T> {\n const result = await step.apply();\n this.applied.push({ step: step as Step<unknown>, result });\n return result;\n }\n\n get size(): number {\n return this.applied.length;\n }\n\n async rollback(reason: GitwiseError, logger: Logger): Promise<RollbackResult> {\n const failures: RollbackFailure[] = [];\n for (const { step, result } of [...this.applied].reverse()) {\n try {\n await step.compensate(result);\n } catch (err) {\n failures.push({ step: step.name, error: err });\n logger.warn(\"compensate-failed\", {\n step: step.name,\n reason: serializeError(err),\n });\n }\n }\n if (failures.length > 0) {\n logger.warn(\"rollback partial: one or more compensate actions failed\", {\n code: \"ROLLBACK_PARTIAL\",\n originalCode: reason.code,\n failures: failures.map((f) => ({\n step: f.step,\n error: serializeError(f.error),\n })),\n });\n }\n return { partial: failures.length > 0, failures };\n }\n}\n\nfunction serializeError(err: unknown): unknown {\n if (err instanceof Error) {\n return { name: err.name, message: err.message };\n }\n return err;\n}\n","import { mkdir, open, readFile, unlink } from \"node:fs/promises\";\nimport { hostname } from \"node:os\";\nimport path from \"node:path\";\nimport { GitwiseError } from \"../errors.js\";\n\nexport const STALE_LOCK_MS = 10 * 60 * 1000;\n\nexport interface LockPayload {\n pid: number;\n host: string;\n command: string;\n acquiredAt: string;\n}\n\nexport interface AcquireRepoLockOptions {\n command?: string;\n staleMs?: number;\n isProcessAlive?: (pid: number) => boolean;\n now?: () => Date;\n /**\n * Test seam: invoked once, awaited, immediately after a stale lock is\n * unlinked and immediately before the re-acquire attempt. Lets tests\n * deterministically simulate another process re-creating the lock inside\n * the reclaim window (the `EEXIST` on `attempt >= 1` → REPO_LOCKED path).\n * Unset in production (no-op).\n */\n onReclaim?: () => void | Promise<void>;\n}\n\nexport async function acquireRepoLock(\n repoPath: string,\n options: AcquireRepoLockOptions = {},\n): Promise<() => Promise<void>> {\n const command = options.command ?? \"unknown\";\n const staleMs = options.staleMs ?? STALE_LOCK_MS;\n const isAlive = options.isProcessAlive ?? defaultIsProcessAlive;\n const now = options.now ?? (() => new Date());\n\n const dir = path.join(repoPath, \".gitwise\");\n const lockPath = path.join(dir, \".lock\");\n await mkdir(dir, { recursive: true });\n\n const payload: LockPayload = {\n pid: process.pid,\n host: hostname(),\n command,\n acquiredAt: now().toISOString(),\n };\n\n await tryAcquire(lockPath, payload, staleMs, isAlive, now, 0, options.onReclaim);\n\n let released = false;\n return async () => {\n if (released) return;\n released = true;\n try {\n await unlink(lockPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n };\n}\n\nasync function tryAcquire(\n lockPath: string,\n payload: LockPayload,\n staleMs: number,\n isAlive: (pid: number) => boolean,\n now: () => Date,\n attempt: number,\n onReclaim?: () => void | Promise<void>,\n): Promise<void> {\n try {\n const handle = await open(lockPath, \"wx\");\n try {\n await handle.writeFile(JSON.stringify(payload, null, 2) + \"\\n\", \"utf-8\");\n } finally {\n await handle.close();\n }\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n\n if (attempt >= 1) {\n throw new GitwiseError({\n code: \"REPO_LOCKED\",\n message: \"Another gitwise process holds the lock on this repository\",\n details: { lockPath },\n });\n }\n\n const existing = await readExisting(lockPath);\n if (existing && !isStale(existing, staleMs, isAlive, now)) {\n throw new GitwiseError({\n code: \"REPO_LOCKED\",\n message: `gitwise lock held by pid ${existing.pid} (command: ${existing.command}) since ${existing.acquiredAt}`,\n details: { existing, lockPath },\n });\n }\n\n try {\n await unlink(lockPath);\n } catch (unlinkErr) {\n if ((unlinkErr as NodeJS.ErrnoException).code !== \"ENOENT\") throw unlinkErr;\n }\n // Reclaim window: a competing process may re-create the lock here. Tests\n // drive that deterministically via onReclaim; production leaves it unset.\n if (onReclaim) await onReclaim();\n return tryAcquire(lockPath, payload, staleMs, isAlive, now, attempt + 1, onReclaim);\n}\n\nasync function readExisting(lockPath: string): Promise<LockPayload | null> {\n try {\n const content = await readFile(lockPath, \"utf-8\");\n const parsed = JSON.parse(content) as Partial<LockPayload>;\n if (\n typeof parsed.pid !== \"number\" ||\n typeof parsed.host !== \"string\" ||\n typeof parsed.command !== \"string\" ||\n typeof parsed.acquiredAt !== \"string\"\n ) {\n return null;\n }\n return {\n pid: parsed.pid,\n host: parsed.host,\n command: parsed.command,\n acquiredAt: parsed.acquiredAt,\n };\n } catch {\n return null;\n }\n}\n\nfunction isStale(\n existing: LockPayload,\n staleMs: number,\n isAlive: (pid: number) => boolean,\n now: () => Date,\n): boolean {\n if (!isAlive(existing.pid)) return true;\n const acquiredAt = Date.parse(existing.acquiredAt);\n if (Number.isNaN(acquiredAt)) return true;\n const age = now().getTime() - acquiredAt;\n return age > staleMs;\n}\n\nfunction defaultIsProcessAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") return false;\n if (code === \"EPERM\") return true;\n return false;\n }\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { CliSubprocessProvider, resolveCliBinary } from \"./cli-subprocess.js\";\nimport type { CliProviderSpec, ModelConfig } from \"./types.js\";\n\nconst COMMON_CLAUDE_PATHS = [\n // Native installs (Homebrew, manual) — preferred over npm\n \"/opt/homebrew/bin/claude\",\n \"/usr/local/bin/claude\",\n path.join(os.homedir(), \".claude\", \"local\", \"claude\"),\n // npm global installs — fallback\n path.join(os.homedir(), \".npm-global\", \"bin\", \"claude\"),\n];\n\n// Same precedence as the other CLI providers (see resolveCliBinary).\nexport function resolveClaudeBinary(customPath?: string): string | null {\n return resolveCliBinary(\"claude\", COMMON_CLAUDE_PATHS, customPath);\n}\n\nexport const claudeCodeSpec: CliProviderSpec = {\n toolName: \"Claude Code CLI\",\n installHint: \"Re-run `gw config` to reconfigure.\",\n defaultCommand: \"claude\",\n foldSystemPrompt: false,\n resolveBinary: resolveClaudeBinary,\n\n buildArgs({ prompt, systemPrompt, modelId, large }) {\n return [\n \"-p\",\n ...(large ? [] : [prompt]),\n \"--system-prompt\",\n systemPrompt,\n \"--model\",\n modelId,\n \"--output-format\",\n \"json\",\n ];\n },\n\n parseOutput(stdout) {\n const parsed = JSON.parse(stdout);\n\n if (parsed.is_error) {\n throw new Error(`Claude CLI returned error: ${parsed.result}`);\n }\n\n return {\n content: parsed.result ?? \"\",\n tokens: {\n input: parsed.usage?.input_tokens ?? 0,\n output: parsed.usage?.output_tokens ?? 0,\n },\n };\n },\n\n formatExitError(code, stdout, stderr) {\n if (stdout) {\n try {\n const parsed = JSON.parse(stdout);\n if (parsed.is_error) return `Claude CLI error: ${parsed.result}`;\n } catch {\n // stdout wasn't valid JSON\n }\n }\n const filteredStderr = stderr.replace(/Warning: no stdin data.*\\n?/g, \"\").trim();\n return `Claude CLI exited with code ${code}${filteredStderr ? `: ${filteredStderr}` : \"\"}`;\n },\n};\n\nexport class ClaudeCodeProvider extends CliSubprocessProvider {\n constructor(models: ModelConfig, claudeCliPath?: string) {\n super(claudeCodeSpec, models, claudeCliPath);\n }\n}\n","import { execSync, spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport { clearTimeout, setTimeout } from \"node:timers\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type {\n CliProviderSpec,\n LLMChatRequest,\n LLMChatResponse,\n LLMProvider,\n ModelConfig,\n} from \"./types.js\";\n\nfunction isExecutable(filePath: string): boolean {\n try {\n fs.accessSync(filePath, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Shared CLI binary resolution (resolveClaudeBinary's original precedence):\n * explicit path (no fallback when it is not executable) → common install\n * paths → `which <name>` → nvm global installs.\n */\nexport function resolveCliBinary(\n name: string,\n commonPaths: readonly string[],\n customPath?: string,\n): string | null {\n if (customPath) return isExecutable(customPath) ? customPath : null;\n\n for (const candidate of commonPaths) {\n if (isExecutable(candidate)) return candidate;\n }\n\n try {\n const found = execSync(`which ${name}`, { stdio: \"pipe\" }).toString().trim();\n if (found && isExecutable(found)) return found;\n } catch {\n // not in PATH\n }\n\n const nvmDir = path.join(os.homedir(), \".nvm\", \"versions\", \"node\");\n try {\n for (const version of fs.readdirSync(nvmDir)) {\n const candidate = path.join(nvmDir, version, \"bin\", name);\n if (isExecutable(candidate)) return candidate;\n }\n } catch {\n // nvm not installed\n }\n\n return null;\n}\n\nexport const LARGE_PROMPT_THRESHOLD = 100_000;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst KILL_GRACE_MS = 5_000;\n\n// AD-001: one spawn/timeout/stderr-capture/ENOENT-wrapping implementation\n// shared by every CLI-backed LLM provider. Tool specifics live in the spec.\nexport class CliSubprocessProvider implements LLMProvider {\n protected readonly binaryPath: string;\n\n constructor(\n private readonly spec: CliProviderSpec,\n private readonly models: ModelConfig,\n cliPath?: string,\n ) {\n // `||` (not `??`): an empty configured path means \"not configured\".\n this.binaryPath = cliPath || spec.resolveBinary() || spec.defaultCommand;\n }\n\n async chat(req: LLMChatRequest): Promise<LLMChatResponse> {\n const modelId = this.models[req.tier];\n debug(`Calling ${this.spec.toolName}`, { model: modelId, tier: req.tier, binary: this.binaryPath });\n\n const prompt = this.spec.foldSystemPrompt\n ? `${req.systemPrompt}\\n\\n${req.userMessage}`\n : req.userMessage;\n // Bytes, not UTF-16 units: OS argv limits (e.g. Linux MAX_ARG_STRLEN) are byte-based.\n const large = Buffer.byteLength(prompt, \"utf8\") > LARGE_PROMPT_THRESHOLD;\n const args = this.spec.buildArgs({ prompt, systemPrompt: req.systemPrompt, modelId, large });\n\n // Small prompts travel via argv; stdin is still closed immediately so a\n // CLI that treats non-TTY stdin as piped input does not wait on it.\n const stdout = await this.spawnCli(args, large ? prompt : \"\");\n const parsed = this.spec.parseOutput(stdout);\n return {\n content: parsed.content,\n tokens: parsed.tokens ?? { input: 0, output: 0 },\n tokensAvailable: parsed.tokens !== null,\n };\n }\n\n private spawnCli(args: string[], input: string): Promise<string> {\n return new Promise((resolve, reject) => {\n // Agentic CLIs run tool calls as child processes. Own process group (POSIX)\n // so a timeout can signal the whole tree; Node's `timeout` option would\n // only signal the direct child and orphan any tool subprocess.\n const ownGroup = process.platform !== \"win32\";\n const child = spawn(this.binaryPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n detached: ownGroup,\n });\n\n const killTree = (sig: NodeJS.Signals): void => {\n try {\n if (ownGroup && child.pid !== undefined) process.kill(-child.pid, sig);\n else child.kill(sig);\n } catch {\n // group already gone\n }\n };\n\n const timeoutMs = this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n let timedOut = false;\n let escalation: NodeJS.Timeout | undefined;\n const timer = setTimeout(() => {\n timedOut = true;\n killTree(\"SIGTERM\");\n escalation = setTimeout(() => killTree(\"SIGKILL\"), KILL_GRACE_MS);\n escalation.unref();\n }, timeoutMs);\n\n // A detached group no longer receives the terminal's Ctrl-C, so reap it on exit and on SIGINT/SIGTERM.\n const reapOnExit = (): void => killTree(\"SIGKILL\");\n let interruptedBy: NodeJS.Signals | undefined;\n const onSignal = (sig: NodeJS.Signals): void => {\n interruptedBy = sig;\n killTree(\"SIGKILL\");\n reject(new Error(`${this.spec.toolName} was interrupted by ${sig}`));\n cleanup();\n if (process.listenerCount(sig) === 0) process.kill(process.pid, sig);\n };\n const onSigint = (): void => onSignal(\"SIGINT\");\n const onSigterm = (): void => onSignal(\"SIGTERM\");\n process.once(\"exit\", reapOnExit);\n process.on(\"SIGINT\", onSigint);\n process.on(\"SIGTERM\", onSigterm);\n function cleanup(): void {\n clearTimeout(timer);\n clearTimeout(escalation);\n process.off(\"exit\", reapOnExit);\n process.off(\"SIGINT\", onSigint);\n process.off(\"SIGTERM\", onSigterm);\n }\n\n let stdout = \"\";\n let stderr = \"\";\n\n // StringDecoder keeps multi-byte UTF-8 characters intact across chunk boundaries.\n const outDecoder = new StringDecoder(\"utf8\");\n const errDecoder = new StringDecoder(\"utf8\");\n child.stdout.on(\"data\", (data: Buffer) => {\n stdout += outDecoder.write(data);\n });\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr += errDecoder.write(data);\n });\n\n child.on(\"close\", (code, signal) => {\n // Sweep tool subprocesses that outlived the CLI itself after a timeout.\n if (timedOut) killTree(\"SIGKILL\");\n cleanup();\n stdout += outDecoder.end();\n stderr += errDecoder.end();\n if (interruptedBy) return;\n if (timedOut) {\n reject(new Error(`${this.spec.toolName} timed out after ${Math.round(timeoutMs / 1000)}s`));\n return;\n }\n if (code === null && signal) {\n reject(new Error(`${this.spec.toolName} was terminated by signal ${signal}`));\n return;\n }\n if (code !== 0) {\n reject(new Error(this.exitErrorMessage(code, stdout, stderr)));\n return;\n }\n resolve(stdout);\n });\n\n child.on(\"error\", (err) => {\n cleanup();\n reject(this.wrapError(err));\n });\n\n // A CLI that exits before draining stdin raises EPIPE here; the close\n // handler already reports the real failure, so the write error is ignored.\n child.stdin.on(\"error\", () => undefined);\n child.stdin.write(input);\n child.stdin.end();\n });\n }\n\n private exitErrorMessage(code: number | null, stdout: string, stderr: string): string {\n if (this.spec.formatExitError) return this.spec.formatExitError(code, stdout, stderr);\n const trimmed = stderr.trim();\n return `${this.spec.toolName} exited with code ${code}${trimmed ? `: ${trimmed}` : \"\"}`;\n }\n\n private wrapError(err: unknown): Error {\n // Duck-typed rather than `instanceof Error`: spawn errors can originate in\n // another realm (e.g. Node internals under a VM sandbox).\n const message = err instanceof Error ? err.message : String((err as { message?: unknown })?.message ?? err);\n const code = (err as { code?: unknown } | null)?.code;\n if (code === \"ENOENT\" || message.includes(\"ENOENT\")) {\n return new GitwiseError({\n code: \"PROVIDER_UNAVAILABLE\",\n message: `${this.spec.toolName} not found at \"${this.binaryPath}\". ${this.spec.installHint}`,\n exitCode: EXIT_CODES.API_FAILED,\n cause: err,\n });\n }\n return err instanceof Error ? err : new Error(message);\n }\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { resolveCliBinary } from \"./cli-subprocess.js\";\nimport type { CliProviderSpec } from \"./types.js\";\n\n// CLI contract verified 2026-09-22 against the installed codex-cli 0.155.1\n// (`codex exec --help` plus live `codex exec --json` runs):\n// - `codex exec [OPTIONS] [PROMPT]` runs non-interactively; PROMPT `-` reads\n// the prompt from stdin; `--` before PROMPT is accepted.\n// - `--json` prints JSONL events. The final answer is the last\n// `{\"type\":\"item.completed\",\"item\":{\"type\":\"agent_message\",\"text\":...}}`.\n// `item.type: \"error\"` items are non-fatal warnings.\n// - Usage IS reported: `{\"type\":\"turn.completed\",\"usage\":{\"input_tokens\":N,\n// \"output_tokens\":M,...}}`, so tokensAvailable is true whenever it is present.\n// - Failures exit 1 with the error on stdout as `{\"type\":\"error\",\"message\"}` /\n// `{\"type\":\"turn.failed\",\"error\":{\"message\"}}`; stderr only carries\n// \"Reading additional input from stdin...\".\n// - `--model`, `--sandbox read-only`, `--ephemeral` (no session files) and\n// `--skip-git-repo-check` exist as used below.\n// - Codex has no system-prompt flag, so the system prompt is folded in.\n\nconst COMMON_CODEX_PATHS = [\n // Native installs (Homebrew cask, manual, standalone installer) — preferred over npm\n \"/opt/homebrew/bin/codex\",\n \"/usr/local/bin/codex\",\n path.join(os.homedir(), \".local\", \"bin\", \"codex\"),\n // npm global installs (`npm install -g @openai/codex`) — fallback\n path.join(os.homedir(), \".npm-global\", \"bin\", \"codex\"),\n];\n\n// Same precedence as the other CLI providers (see resolveCliBinary).\nexport function resolveCodexBinary(customPath?: string): string | null {\n return resolveCliBinary(\"codex\", COMMON_CODEX_PATHS, customPath);\n}\n\ninterface CodexEvent {\n type?: string;\n message?: string;\n item?: { type?: string; text?: string };\n usage?: { input_tokens?: number; output_tokens?: number };\n error?: { message?: string };\n}\n\nfunction parseEvents(stdout: string): CodexEvent[] {\n const events: CodexEvent[] = [];\n for (const line of stdout.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n events.push(JSON.parse(trimmed) as CodexEvent);\n } catch {\n // non-JSON noise line\n }\n }\n return events;\n}\n\nexport const codexSpec: CliProviderSpec = {\n toolName: \"Codex CLI\",\n installHint: \"Install it (`npm install -g @openai/codex`) or re-run `gw provider` to choose another provider.\",\n defaultCommand: \"codex\",\n foldSystemPrompt: true,\n // Full agent turns take longer than a single Claude Code completion.\n timeoutMs: 300_000,\n resolveBinary: resolveCodexBinary,\n\n buildArgs({ prompt, modelId, large }) {\n return [\n \"exec\",\n \"--json\",\n \"--ephemeral\",\n \"--skip-git-repo-check\",\n \"--sandbox\",\n \"read-only\",\n \"--model\",\n modelId,\n \"--\",\n large ? \"-\" : prompt,\n ];\n },\n\n parseOutput(stdout) {\n let content: string | undefined;\n let tokens: { input: number; output: number } | null = null;\n for (const event of parseEvents(stdout)) {\n if (event.type === \"item.completed\" && event.item?.type === \"agent_message\") {\n content = event.item.text ?? \"\";\n } else if (event.type === \"turn.completed\" && event.usage) {\n tokens = {\n input: event.usage.input_tokens ?? 0,\n output: event.usage.output_tokens ?? 0,\n };\n }\n }\n if (content === undefined) {\n throw new Error(\"Codex CLI returned no final agent message\");\n }\n return { content, tokens };\n },\n\n // The CLI's own error text lives in the JSONL stream, not stderr; surface it\n // verbatim, falling back to stderr when the stream carries none.\n formatExitError(code, stdout, stderr) {\n const messages = new Set<string>();\n for (const event of parseEvents(stdout)) {\n if (event.type === \"error\" && event.message) messages.add(event.message);\n if (event.type === \"turn.failed\" && event.error?.message) messages.add(event.error.message);\n }\n const detail = messages.size > 0 ? [...messages].join(\"\\n\") : stderr.trim();\n return `Codex CLI exited with code ${code}${detail ? `: ${detail}` : \"\"}`;\n },\n};\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { resolveCliBinary } from \"./cli-subprocess.js\";\nimport type { CliProviderSpec } from \"./types.js\";\n\n// CLI contract verified 2026-09-22 against the installed GitHub Copilot CLI\n// 1.0.82 (`copilot --help` plus live runs):\n// - `-p, --prompt <text>` runs one prompt non-interactively and exits. The\n// `--prompt=<text>` form is used so a prompt starting with \"-\" is never\n// parsed as an option.\n// - With no `--prompt` and a piped stdin, the stdin text is the prompt. This is\n// how large prompts travel. (`-p -` is NOT stdin: \"-\" is taken literally.)\n// - `--no-ask-user` disables the ask_user tool; `-s, --silent` prints only the\n// agent response (no stats footer); `--model <model>` selects the model.\n// - Failures exit 1 with the error on stderr, e.g.\n// `Error: Model \"x\" from --model flag is not available.`\n// - Silent stdout carries no token usage, so tokens are always null.\n// `--usage-output-file <file>` (\"Write final usage statistics as JSON\",\n// `copilot --help` 1.0.88) is not read: its format is undocumented on\n// docs.github.com, and the one observed file (a run that failed before any\n// model call) only showed the shape, with zeroed `lastCallInputTokens` /\n// `lastCallOutputTokens` and an empty `modelMetrics`. How those fields\n// behave for a multi-call agent turn is unverified (checked 2026-09-23).\n// - Copilot has no system-prompt flag, so the system prompt is folded in.\n\nconst COMMON_COPILOT_PATHS = [\n // Native/Homebrew installs — preferred over npm\n \"/opt/homebrew/bin/copilot\",\n \"/usr/local/bin/copilot\",\n // Copilot's install script (non-root) target\n path.join(os.homedir(), \".local\", \"bin\", \"copilot\"),\n // npm global installs (`npm install -g @github/copilot`) — fallback\n path.join(os.homedir(), \".npm-global\", \"bin\", \"copilot\"),\n];\n\n// Same precedence as the other CLI providers (see resolveCliBinary).\nexport function resolveCopilotBinary(customPath?: string): string | null {\n return resolveCliBinary(\"copilot\", COMMON_COPILOT_PATHS, customPath);\n}\n\nexport const copilotSpec: CliProviderSpec = {\n toolName: \"Copilot CLI\",\n installHint: \"Install it (`npm install -g @github/copilot`) or re-run `gw provider` to choose another provider.\",\n defaultCommand: \"copilot\",\n foldSystemPrompt: true,\n // Full agent turns take longer than a single Claude Code completion.\n timeoutMs: 300_000,\n resolveBinary: resolveCopilotBinary,\n\n buildArgs({ prompt, modelId, large }) {\n return [...(large ? [] : [`--prompt=${prompt}`]), \"--no-ask-user\", \"--silent\", \"--model\", modelId];\n },\n\n parseOutput(stdout) {\n const content = stdout.trim();\n if (!content) throw new Error(\"Copilot CLI returned an empty response\");\n return { content, tokens: null };\n },\n};\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { stripVTControlCharacters } from \"node:util\";\nimport { resolveCliBinary } from \"./cli-subprocess.js\";\nimport type { CliProviderSpec } from \"./types.js\";\n\n// CLI contract, verified 2026-09-22 without a live Kiro account (spec\n// assumption: no paid subscription, so tests use mocked subprocess I/O):\n// - Installed kiro-cli 2.23.0 `kiro-cli chat --help`: `chat [OPTIONS] [INPUT]`,\n// `--no-interactive`, `--model <MODEL>`, `--trust-tools=` (trust no tools),\n// `--wrap never` (raw output), `--output-format text|stream-json`.\n// - https://kiro.dev/docs/cli/headless/ : the prompt is the positional INPUT,\n// or, \"When stdin is piped and no positional argument is given, Kiro reads\n// the full stream as the instruction\" (used for large prompts).\n// - Neither source documents a token-usage field for text output, so tokens\n// are always null. The docs also leave the text-mode stdout shape and the\n// error channel unspecified, so terminal escapes are stripped from the\n// response defensively, and a failure surfaces stderr verbatim, or stdout\n// when stderr is empty.\n// - Kiro has no system-prompt flag, so the system prompt is folded in.\n\nconst COMMON_KIRO_PATHS = [\n // macOS app bundle and the installer's symlink location — preferred\n \"/Applications/Kiro CLI.app/Contents/MacOS/kiro-cli\",\n path.join(os.homedir(), \".local\", \"bin\", \"kiro-cli\"),\n \"/opt/homebrew/bin/kiro-cli\",\n \"/usr/local/bin/kiro-cli\",\n];\n\n// Same precedence as the other CLI providers (see resolveCliBinary).\nexport function resolveKiroBinary(customPath?: string): string | null {\n return resolveCliBinary(\"kiro-cli\", COMMON_KIRO_PATHS, customPath);\n}\n\nexport const kiroSpec: CliProviderSpec = {\n toolName: \"Kiro CLI\",\n installHint: \"Install it from https://kiro.dev/docs/cli/ or re-run `gw provider` to choose another provider.\",\n defaultCommand: \"kiro-cli\",\n foldSystemPrompt: true,\n // Full agent turns take longer than a single Claude Code completion.\n timeoutMs: 300_000,\n resolveBinary: resolveKiroBinary,\n\n buildArgs({ prompt, modelId, large }) {\n return [\n \"chat\",\n \"--no-interactive\",\n \"--trust-tools=\",\n \"--wrap\",\n \"never\",\n \"--model\",\n modelId,\n // `--` (standard for kiro-cli's clap-style parser) keeps a prompt that\n // starts with \"-\" from being read as an option.\n ...(large ? [] : [\"--\", prompt]),\n ];\n },\n\n parseOutput(stdout) {\n const content = stripVTControlCharacters(stdout).trim();\n if (!content) throw new Error(\"Kiro CLI returned an empty response\");\n return { content, tokens: null };\n },\n\n formatExitError(code, stdout, stderr) {\n const detail = stderr.trim() || stripVTControlCharacters(stdout).trim();\n return `Kiro CLI exited with code ${code}${detail ? `: ${detail}` : \"\"}`;\n },\n};\n","import type { ReleaseStrategyName } from \"../strategies/release.js\";\nimport type { ProviderKind } from \"../providers/types.js\";\n\nexport type ModelTier = \"fast\" | \"balanced\" | \"powerful\";\nexport type Language = \"en\" | \"pt-br\" | \"es\" | \"fr\" | \"de\" | \"zh\" | \"ja\" | \"ko\";\nexport type CommitConvention = \"conventional\" | \"gitmoji\" | \"angular\" | \"kernel\" | \"custom\";\n\nexport interface ModelConfig {\n fast: string;\n balanced: string;\n powerful: string;\n}\n\n/** MDL-01: per-provider model map — each ProviderKind keeps its own tier IDs. */\nexport type ModelsByProvider = Record<ProviderKind, ModelConfig>;\n\n/** Persisted in ~/.gitwise/config.json */\nexport interface UserConfig {\n provider: ProviderKind;\n claudeCliPath?: string;\n codexCliPath?: string;\n copilotCliPath?: string;\n kiroCliPath?: string;\n models: ModelsByProvider;\n language: Language;\n defaultBaseBranch?: string;\n commitConvention: CommitConvention;\n}\n\n/** Loaded from <cwd>/.gitwise.json — all fields are optional */\nexport interface RepoConfig {\n /** Flat tiers apply to the active provider; provider-keyed blocks target that provider and win over flat tiers. */\n models?: Partial<ModelConfig> & Partial<Record<ProviderKind, Partial<ModelConfig>>>;\n language?: Language;\n defaultBaseBranch?: string;\n commitConvention?: CommitConvention;\n templatesPath?: string;\n /** When true, applyRelease() propagates the new version to all packages/* */\n workspacePropagation?: boolean;\n /** Release lifecycle strategy. Unset = \"github-flow\" at the consumer level. */\n releaseStrategy?: ReleaseStrategyName;\n /** Develop branch name for gitflow; consumers default to \"develop\" when unset. */\n developBranch?: string;\n}\n\n/** The merged result of UserConfig + RepoConfig overrides */\nexport interface MergedConfig extends UserConfig {\n templatesPath?: string;\n releaseStrategy?: ReleaseStrategyName;\n developBranch?: string;\n}\n\n// MDL-02: default model IDs per provider, checked 2026-09-23 against the\n// CLIs installed on the maintainer's machine (help text and model listings\n// only, no model calls). Every value is user-overridable via\n// `gw config models.<provider>.<tier>`.\n// - api / claude-code: gitwise's pre-existing Claude defaults, unchanged.\n// - codex: the model catalog of codex-cli 0.156.1 (`codex debug models`,\n// cached in ~/.codex/models_cache.json) lists gpt-6-luna (\"fast and\n// affordable\"), gpt-6-sol (\"workhorse model for coding\") and gpt-6-astra\n// (\"frontier intelligence\"). The old gpt-5.1-codex* IDs are not in it.\n// The catalog is fetched per account, so another plan may list more models.\n// - copilot: the `model` values listed by `copilot help config` in GitHub\n// Copilot CLI 1.0.88. The tiers mirror gitwise's Claude defaults\n// (haiku-4.5 / sonnet-4.6 / opus-4.7); claude-sonnet-4.5 and\n// claude-opus-4.1 are not in that list.\n// - kiro: `kiro-cli chat --list-models` in kiro-cli 2.23.1 (the only source\n// that lists exact --model IDs): claude-haiku-4.5 and claude-sonnet-4.5;\n// it has no Opus, so `powerful` also uses claude-sonnet-4.5. The docs\n// (https://kiro.dev/docs/cli/chat/model-selection/, checked 2026-09-23) name\n// Sonnet 4.6 and Opus 4.7 by display name only and give no exact ID, so those\n// IDs are unverified and not used. On a newer CLI, override a tier, e.g.\n// `gw config models.kiro.powerful claude-opus-4.7` after checking\n// `kiro-cli chat --list-models`.\nconst CLAUDE_MODELS: ModelConfig = {\n fast: \"claude-haiku-4-5-20251001\",\n balanced: \"claude-sonnet-4-6\",\n powerful: \"claude-opus-4-7\",\n};\n\nexport const DEFAULT_USER_CONFIG: UserConfig = {\n provider: \"api\",\n models: {\n api: { ...CLAUDE_MODELS },\n \"claude-code\": { ...CLAUDE_MODELS },\n codex: {\n fast: \"gpt-6-luna\",\n balanced: \"gpt-6-sol\",\n powerful: \"gpt-6-astra\",\n },\n copilot: {\n fast: \"claude-haiku-4.5\",\n balanced: \"claude-sonnet-4.6\",\n powerful: \"claude-opus-4.7\",\n },\n kiro: {\n fast: \"claude-haiku-4.5\",\n balanced: \"claude-sonnet-4.5\",\n powerful: \"claude-sonnet-4.5\",\n },\n },\n language: \"en\",\n commitConvention: \"conventional\",\n};\n","import os from \"node:os\";\nimport { read as readEnvValue } from \"../infra/env.js\";\nimport { readUserConfig } from \"./user.js\";\nimport { readRepoConfig } from \"./repo.js\";\nimport { PROVIDER_KINDS } from \"../providers/types.js\";\nimport type { MergedConfig, ModelConfig, ModelsByProvider, RepoConfig, UserConfig } from \"./types.js\";\n\nfunction isPlainObject(value: unknown): value is object {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nconst MODEL_TIERS: readonly (keyof ModelConfig)[] = [\"fast\", \"balanced\", \"powerful\"];\n\nfunction mergeRepoModels(base: UserConfig, override: RepoConfig[\"models\"]): ModelsByProvider {\n if (!isPlainObject(override)) return base.models;\n // MDL-06: flat tiers target the active provider; per-provider blocks target each named provider and win over flat tiers.\n const source = override as Record<string, unknown>;\n const flat: Partial<ModelConfig> = {};\n for (const tier of MODEL_TIERS) {\n const value = source[tier];\n if (typeof value === \"string\") flat[tier] = value;\n }\n const merged = { ...base.models };\n merged[base.provider] = { ...base.models[base.provider], ...flat };\n for (const kind of PROVIDER_KINDS) {\n const block = source[kind];\n if (isPlainObject(block)) merged[kind] = { ...merged[kind], ...block };\n }\n return merged;\n}\n\nexport function deepMerge(base: UserConfig, override: RepoConfig): MergedConfig {\n return {\n ...base,\n ...(override.language !== undefined && { language: override.language }),\n ...(override.defaultBaseBranch !== undefined && { defaultBaseBranch: override.defaultBaseBranch }),\n ...(override.commitConvention !== undefined && { commitConvention: override.commitConvention }),\n ...(override.templatesPath !== undefined && { templatesPath: override.templatesPath }),\n ...(override.releaseStrategy !== undefined && { releaseStrategy: override.releaseStrategy }),\n ...(override.developBranch !== undefined && { developBranch: override.developBranch }),\n models: mergeRepoModels(base, override.models),\n };\n}\n\nexport interface GetMergedConfigOptions {\n cwd: string;\n homeDir?: string;\n}\n\n/**\n * Load and merge config:\n * 1. Start from defaults\n * 2. Layer user config (~/.gitwise/config.json)\n * 3. Layer repo config (<cwd>/.gitwise.json)\n *\n * Note: the API key is NOT included in the returned config.\n */\nexport async function getMergedConfig(options: GetMergedConfigOptions): Promise<MergedConfig> {\n const { cwd, homeDir } = options;\n const userConfig = await readUserConfig(homeDir);\n const repoConfig = await readRepoConfig(cwd);\n if (!repoConfig) {\n return userConfig;\n }\n return deepMerge(userConfig, repoConfig);\n}\n\n/**\n * Read the Anthropic API key from process.env first, then ~/.gitwise/.env.\n * Returns undefined if not found anywhere.\n */\nexport async function getApiKey(homeDir?: string): Promise<string | undefined> {\n const home = homeDir ?? os.homedir();\n return readEnvValue(\"ANTHROPIC_API_KEY\", home);\n}\n","import { join } from \"node:path\";\nimport os from \"node:os\";\nimport { fileExists, readJSON, writeJSON } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { writeEnvVar } from \"../infra/env.js\";\nimport { DEFAULT_USER_CONFIG, type ModelConfig, type ModelsByProvider, type UserConfig } from \"./types.js\";\nimport { PROVIDER_KINDS, type ProviderKind } from \"../providers/types.js\";\n\nconst GITWISE_DIR = \".gitwise\";\nconst USER_CONFIG_FILE = \"config.json\";\n\nfunction getUserConfigPath(homeDir?: string): string {\n return join(homeDir ?? os.homedir(), GITWISE_DIR, USER_CONFIG_FILE);\n}\n\n/**\n * MDL-05: detects the pre-this-feature flat `models` shape\n * (`{fast, balanced, powerful}`), as opposed to the current per-provider map\n * (`{api: {...}, \"claude-code\": {...}, ...}`) — distinguished by whether\n * `.fast` itself is a string (legacy) or an object (current).\n */\nfunction isLegacyFlatModels(value: unknown): value is ModelConfig {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Record<string, unknown>;\n return typeof v[\"fast\"] === \"string\" && typeof v[\"balanced\"] === \"string\" && typeof v[\"powerful\"] === \"string\";\n}\n\n/**\n * MDL-05 / Edge Cases: migrates a legacy flat `models` block into\n * `models[<configured provider>]`, backfilling every other provider key from\n * defaults. When `provider` is itself unrecognized, every key — including\n * the one the flat block might have belonged to — is backfilled from\n * defaults instead of guessing which provider it was meant for. An absent\n * `provider` means the default provider.\n */\nfunction migrateFlatModels(flat: ModelConfig, provider: unknown): ModelsByProvider {\n const target = provider === undefined ? DEFAULT_USER_CONFIG.provider : provider;\n const migrated: ModelsByProvider = { ...DEFAULT_USER_CONFIG.models };\n if (typeof target === \"string\" && PROVIDER_KINDS.includes(target as ProviderKind)) {\n migrated[target as ProviderKind] = { ...flat };\n }\n return migrated;\n}\n\nexport function mergeWithDefaults(partial: Partial<UserConfig>): UserConfig {\n const models = {} as ModelsByProvider;\n for (const kind of PROVIDER_KINDS) {\n models[kind] = { ...DEFAULT_USER_CONFIG.models[kind], ...(partial.models?.[kind] ?? {}) };\n }\n return { ...DEFAULT_USER_CONFIG, ...partial, models };\n}\n\nexport async function readUserConfig(homeDir?: string): Promise<UserConfig> {\n const configPath = getUserConfigPath(homeDir);\n if (!(await fileExists(configPath))) {\n debug(\"User config not found, using defaults\", { path: configPath });\n return { ...DEFAULT_USER_CONFIG };\n }\n const raw = await readJSON<Partial<UserConfig>>(configPath);\n\n if (isLegacyFlatModels(raw.models)) {\n const migratedModels = migrateFlatModels(raw.models, raw.provider);\n const merged = mergeWithDefaults({ ...raw, models: migratedModels });\n debug(\"Migrated legacy flat models config to per-provider shape\", { path: configPath });\n try {\n await writeJSON(configPath, merged);\n } catch (err) {\n debug(\"Could not persist migrated config; using it in memory\", { path: configPath, error: String(err) });\n }\n return merged;\n }\n\n return mergeWithDefaults(raw);\n}\n\nexport async function writeUserConfig(\n partial: Partial<UserConfig>,\n homeDir?: string,\n): Promise<void> {\n const configPath = getUserConfigPath(homeDir);\n const existing = await readUserConfig(homeDir);\n const updated = mergeWithDefaults({ ...existing, ...partial });\n debug(\"Writing user config\", { path: configPath });\n await writeJSON(configPath, updated);\n}\n\n/**\n * Write ANTHROPIC_API_KEY to ~/.gitwise/.env with file mode 0600.\n * Keys MUST NOT be written to config.json.\n *\n * Note: writeEnvVar(root, key, val) writes to root/.gitwise/.env.\n * We pass homeDir (default: os.homedir()) so the file lands at ~/.gitwise/.env.\n */\nexport async function writeApiKey(value: string, homeDir?: string): Promise<void> {\n const home = homeDir ?? os.homedir();\n await writeEnvVar(home, \"ANTHROPIC_API_KEY\", value);\n}\n","export type ModelTier = \"fast\" | \"balanced\" | \"powerful\";\n\n// Single source of truth for provider kinds — import it, never redefine it.\nexport type ProviderKind = \"api\" | \"claude-code\" | \"codex\" | \"copilot\" | \"kiro\";\n\n// Runtime companion to ProviderKind, for code that needs to iterate/validate\n// against the actual value set (e.g. `gw config provider <value>` validation,\n// legacy-config migration). Import this instead of hand-rolling another\n// literal array of the same five values.\nexport const PROVIDER_KINDS: readonly ProviderKind[] = [\"api\", \"claude-code\", \"codex\", \"copilot\", \"kiro\"];\n\n// TechSpec \"Core Interfaces\" LLMProvider shape\nexport interface LLMChatRequest {\n systemPrompt: string;\n userMessage: string;\n tier: ModelTier;\n}\n\nexport interface LLMChatResponse {\n content: string;\n tokens: { input: number; output: number };\n /** AD-002: false when the provider does not report usage; `tokens` is then 0/0 and must not be shown as real. */\n tokensAvailable: boolean;\n}\n\nexport interface LLMProvider {\n chat(req: LLMChatRequest): Promise<LLMChatResponse>;\n}\n\nexport interface ModelConfig {\n fast: string;\n balanced: string;\n powerful: string;\n}\n\n// AD-001: everything that differs between CLI-subprocess providers.\n// SPEC_DEVIATION: design.md's `buildArgs({ combinedPrompt, modelId, large })`\n// is extended with `systemPrompt` + a `foldSystemPrompt` flag, and an optional\n// `formatExitError` hook is added.\n// Reason: Claude Code has a dedicated `--system-prompt` flag and a JSON\n// `is_error` exit contract that must stay byte-for-byte unchanged (T1\n// characterization tests); tools without a system-prompt flag fold it in.\nexport interface CliProviderSpec {\n toolName: string;\n installHint: string;\n /** Command name spawned when `resolveBinary()` finds nothing (ENOENT then maps to PROVIDER_UNAVAILABLE). */\n defaultCommand: string;\n /** false → CLI takes a separate system prompt; true → system prompt is folded into `prompt`. */\n foldSystemPrompt: boolean;\n resolveBinary(customPath?: string): string | null;\n /** `prompt` is omitted from argv by the spec when `large` is true — it is written to stdin instead. */\n buildArgs(input: { prompt: string; systemPrompt: string; modelId: string; large: boolean }): string[];\n parseOutput(stdout: string): { content: string; tokens: { input: number; output: number } | null };\n /** Optional override for the non-zero-exit error message; default surfaces stderr verbatim. */\n formatExitError?(code: number | null, stdout: string, stderr: string): string;\n // SPEC_DEVIATION: `timeoutMs` is not in design.md's CliProviderSpec.\n // Reason: Codex/Copilot/Kiro run full agent turns and need more than the\n // 120s Claude Code has always used (post-validation follow-up G4).\n /** Subprocess timeout in ms; omitted → the shared 120s default. */\n timeoutMs?: number;\n}\n\nexport interface ProviderConfig {\n kind: ProviderKind;\n models: ModelConfig;\n apiKey?: string;\n claudeCliPath?: string;\n codexCliPath?: string;\n copilotCliPath?: string;\n kiroCliPath?: string;\n}\n","import { join } from \"node:path\";\nimport { fileExists, readJSON } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type { RepoConfig } from \"./types.js\";\n\nconst REPO_CONFIG_FILE = \".gitwise.json\";\n\nexport async function readRepoConfig(cwd: string): Promise<RepoConfig | null> {\n const configPath = join(cwd, REPO_CONFIG_FILE);\n if (!(await fileExists(configPath))) {\n debug(\"Repo config not found\", { path: configPath });\n return null;\n }\n try {\n const raw = await readJSON<RepoConfig>(configPath);\n return raw;\n } catch (err) {\n throw new GitwiseError({\n code: \"INVALID_REPO_CONFIG\",\n message: `Invalid repo config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n cause: err,\n });\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport os from \"node:os\";\nimport { fileExists } from \"../infra/filesystem.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { interpolate } from \"./interpolate.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// Bundled templates live at packages/core/templates/. The relative ascent\n// differs depending on where this module ends up at runtime:\n// - source layout: packages/core/src/template/loader.ts → ../../templates\n// - bundled dist: packages/core/dist/index.js → ../templates\n// We probe both so the loader works whether consumers import the source via\n// ts-jest or the built dist via `node`.\nconst BUNDLED_TEMPLATES_CANDIDATES = [\n join(__dirname, \"..\", \"templates\"),\n join(__dirname, \"..\", \"..\", \"templates\"),\n];\n\nfunction validateTemplateName(name: string): void {\n if (!/^[a-zA-Z0-9_-]+$/.test(name)) {\n throw new GitwiseError({\n code: \"TEMPLATE_INVALID_NAME\",\n message: `Invalid template name: '${name}'. Only alphanumeric characters, hyphens, and underscores are allowed.`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n}\n\nexport interface LoadTemplateOptions {\n /** Override the user-global templates directory (default: ~/.gitwise/templates). */\n templatesPath?: string;\n /** Repo root for repo-level override lookup (default: process.cwd()). */\n repoRoot?: string;\n}\n\n/**\n * Load a template by name, applying 3-level precedence:\n * 1. <repoRoot>/.gitwise/templates/<name>.md (highest priority)\n * 2. templatesPath (default: ~/.gitwise/templates/<name>.md)\n * 3. packages/core/templates/<name>.md (bundled fallback)\n *\n * Returns the raw template string (not interpolated).\n * Throws TEMPLATE_NOT_FOUND if no file found at any level.\n */\nexport async function loadTemplate(\n name: string,\n options: LoadTemplateOptions = {},\n): Promise<string> {\n validateTemplateName(name);\n\n const repoRoot = options.repoRoot ?? process.cwd();\n const userTemplatesPath = options.templatesPath ?? join(os.homedir(), \".gitwise\", \"templates\");\n\n // Level 1: repo-level override\n const repoOverride = join(repoRoot, \".gitwise\", \"templates\", `${name}.md`);\n if (await fileExists(repoOverride)) {\n debug(\"Loading repo-level template override\", { path: repoOverride });\n return readFile(repoOverride, \"utf-8\");\n }\n\n // Level 2: user-global (or configured templatesPath)\n const userOverride = join(userTemplatesPath, `${name}.md`);\n if (await fileExists(userOverride)) {\n debug(\"Loading user-global template override\", { path: userOverride });\n return readFile(userOverride, \"utf-8\");\n }\n\n // Level 3: bundled (probe each candidate layout)\n for (const candidate of BUNDLED_TEMPLATES_CANDIDATES) {\n const bundled = join(candidate, `${name}.md`);\n if (await fileExists(bundled)) {\n debug(\"Loading bundled template\", { path: bundled });\n return readFile(bundled, \"utf-8\");\n }\n }\n\n throw new GitwiseError({\n code: \"TEMPLATE_NOT_FOUND\",\n message: `Template '${name}' not found`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n}\n\n/**\n * Load and interpolate a template in one call.\n */\nexport async function loadAndInterpolate(\n name: string,\n ctx: Record<string, string>,\n options: LoadTemplateOptions = {},\n): Promise<string> {\n const template = await loadTemplate(name, options);\n return interpolate(template, ctx);\n}\n","/**\n * Replace {{var}} placeholders in a template string with values from ctx.\n * Unknown placeholders are left untouched.\n */\nexport function interpolate(template: string, ctx: Record<string, string>): string {\n return template.replace(\n /\\{\\{(\\w+)\\}\\}/g,\n (_match, key: string) => ctx[key] ?? _match,\n );\n}\n","import type { ModelTier } from \"./types.js\";\n\n// Four supported commands and their default tiers\n// commit/pr/release default to fast; review defaults to powerful\nconst COMMAND_TIER_MAP: Record<string, ModelTier> = {\n commit: \"fast\",\n review: \"powerful\",\n pr: \"fast\",\n release: \"fast\",\n};\n\nexport function resolveModelTier(command: string): ModelTier {\n return COMMAND_TIER_MAP[command] ?? \"balanced\";\n}\n\nexport const SUPPORTED_COMMANDS = Object.keys(COMMAND_TIER_MAP) as (keyof typeof COMMAND_TIER_MAP)[];\n","import * as git from \"../infra/git.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug, warn as logWarn } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport { Transaction, type Logger, type Step } from \"../infra/transaction.js\";\nimport { acquireRepoLock } from \"../infra/lockfile.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface CommitEntry {\n message: string;\n description?: string;\n files: string[];\n}\n\nexport interface CommitPlan {\n kind: \"single\" | \"split\";\n commits: CommitEntry[];\n tokens: { input: number; output: number };\n /** AD-002: false when the active provider doesn't report token usage. */\n tokensAvailable: boolean;\n}\n\nexport type SplitMode = \"auto\" | \"never\" | \"always\";\n\nexport interface CommitOptions {\n cwd: string;\n provider: LLMProvider;\n prompt?: string;\n split?: SplitMode;\n push?: boolean;\n commitConvention?: string;\n templatesPath?: string;\n repoRoot?: string;\n feedbackHint?: string;\n generateAlternatives?: boolean;\n}\n\nexport interface CommitAlternatives {\n kind: \"alternatives\";\n options: string[];\n tokens: { input: number; output: number };\n /** AD-002: false when the active provider doesn't report token usage. */\n tokensAvailable: boolean;\n}\n\nexport interface ApplyCommitPlanOptions {\n push?: boolean;\n remote?: string;\n}\n\n// ─── Sensitive file detection ────────────────────────────────────────────────\n\nconst SENSITIVE_PATTERNS = [\n /^\\.env$/,\n /^\\.env\\./,\n /\\.pem$/,\n /\\.key$/,\n /^id_rsa/,\n /^id_dsa/,\n /^id_ecdsa/,\n /^id_ed25519/,\n /credentials\\.json$/,\n /secrets\\.json$/,\n /auth\\.json$/,\n /service-account\\.json$/,\n /\\.p12$/,\n /\\.pfx$/,\n /\\.pkcs12$/,\n];\n\n// Env template files are conventionally committed: they contain only\n// placeholder values, not real secrets. Exclude them from the `.env.` block.\nconst SAFE_ENV_TEMPLATE_SUFFIXES = [\n \".example\",\n \".sample\",\n \".template\",\n \".dist\",\n \".defaults\",\n];\n\nfunction isSafeEnvTemplate(basename: string): boolean {\n if (!basename.startsWith(\".env\")) return false;\n return SAFE_ENV_TEMPLATE_SUFFIXES.some((suffix) => basename.endsWith(suffix));\n}\n\nfunction isSensitiveFile(filePath: string): boolean {\n const basename = filePath.split(\"/\").pop() ?? filePath;\n if (isSafeEnvTemplate(basename)) return false;\n return SENSITIVE_PATTERNS.some((pattern) => pattern.test(basename));\n}\n\n// ─── JSON parser strategies ──────────────────────────────────────────────────\n\ninterface LLMSingleResponse {\n type: \"single\";\n message: string;\n}\n\ninterface LLMPlanResponse {\n type: \"plan\";\n commits: Array<{ message: string; description?: string; files: string[] }>;\n}\n\ntype LLMCommitResponse = LLMSingleResponse | LLMPlanResponse;\n\nfunction tryParseJson(text: string): LLMCommitResponse | null {\n try {\n const parsed = JSON.parse(text.trim()) as Record<string, unknown>;\n if (parsed.type === \"plan\" && Array.isArray(parsed.commits)) {\n return parsed as unknown as LLMPlanResponse;\n }\n if (parsed.type === \"single\" && typeof parsed.message === \"string\") {\n return parsed as unknown as LLMSingleResponse;\n }\n } catch { /* not valid JSON */ }\n return null;\n}\n\n// Scans `raw` for balanced-brace substrings using a stack so each `}` emits the\n// substring opened by its matching `{`. String literals (with `\\\\` escapes) are\n// honored so braces inside JSON string values don't skew matching. An unclosed\n// outer `{` does not prevent inner balanced objects from being emitted, which\n// matters when an LLM emits a malformed draft followed by a valid object.\n// Candidates are returned in completion order (inner objects before their\n// enclosing parents).\nfunction extractBalancedJsonCandidates(raw: string): string[] {\n const candidates: string[] = [];\n const stack: number[] = [];\n let inString = false;\n let escape = false;\n for (let i = 0; i < raw.length; i++) {\n const c = raw[i];\n if (inString) {\n if (escape) {\n escape = false;\n } else if (c === \"\\\\\") {\n escape = true;\n } else if (c === '\"') {\n inString = false;\n }\n continue;\n }\n if (c === '\"') {\n inString = true;\n continue;\n }\n if (c === \"{\") {\n stack.push(i);\n } else if (c === \"}\") {\n const start = stack.pop();\n if (start !== undefined) {\n candidates.push(raw.slice(start, i + 1));\n }\n }\n }\n return candidates;\n}\n\nfunction parseAlternativesResponse(raw: string): string[] | null {\n const isValid = (p: unknown): p is { type: string; options: string[] } =>\n typeof p === \"object\" &&\n p !== null &&\n (p as Record<string, unknown>)[\"type\"] === \"alternatives\" &&\n Array.isArray((p as Record<string, unknown>)[\"options\"]) &&\n ((p as Record<string, unknown>)[\"options\"] as unknown[]).length > 0 &&\n ((p as Record<string, unknown>)[\"options\"] as unknown[]).every((o) => typeof o === \"string\");\n\n // Strategy 1: direct JSON\n try {\n const parsed = JSON.parse(raw.trim());\n if (isValid(parsed)) return parsed.options;\n } catch { /* fall through */ }\n\n // Strategy 2: fenced code block\n const fence = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n if (fence?.[1]) {\n try {\n const parsed = JSON.parse(fence[1].trim());\n if (isValid(parsed)) return parsed.options;\n } catch { /* fall through */ }\n }\n\n // Strategy 3: first line starting with {\n for (const line of raw.split(\"\\n\")) {\n const t = line.trim();\n if (!t.startsWith(\"{\")) continue;\n try {\n const parsed = JSON.parse(t);\n if (isValid(parsed)) return parsed.options;\n } catch { /* skip */ }\n }\n\n return null;\n}\n\nexport function parseCommitResponse(raw: string): LLMCommitResponse {\n // Strategy 1: pure JSON\n const direct = tryParseJson(raw);\n if (direct) return direct;\n\n // Strategy 2: fenced code block\n const fenceMatch = raw.match(/```json?\\s*\\n?([\\s\\S]*?)```/);\n if (fenceMatch?.[1]) {\n const fromFence = tryParseJson(fenceMatch[1]);\n if (fromFence) return fromFence;\n }\n\n // Strategy 3: balanced-brace candidate extraction. Prefer a \"plan\" candidate\n // when present so multi-context output is not silently downgraded to the\n // first \"single\" object the model emits alongside it.\n const candidates = extractBalancedJsonCandidates(raw);\n const parsedCandidates = candidates\n .map(tryParseJson)\n .filter((p): p is LLMCommitResponse => p !== null);\n const plan = parsedCandidates.find((p) => p.type === \"plan\");\n if (plan) return plan;\n if (parsedCandidates[0]) return parsedCandidates[0];\n\n // Fallback: treat the whole response as a single commit message\n return { type: \"single\", message: raw.trim() };\n}\n\nconst MAX_DIFF_CHARS = 80_000;\n\nfunction truncateDiff(diff: string): string {\n if (diff.length <= MAX_DIFF_CHARS) return diff;\n return diff.slice(0, MAX_DIFF_CHARS) + \"\\n\\n[diff truncated — too large for context window]\";\n}\n\n// ─── Core commit function ────────────────────────────────────────────────────\n\nconst SYSTEM_PROMPT = `You are a developer writing commit messages. Analyze the git diff and the list of staged files to determine if the changes span one or multiple contexts.\n\nRules for commit messages:\n- Format: type(scope): description\n- Types: feat, fix, refactor, test, chore, style, docs\n- Description must be imperative, lowercase, max 72 chars\n- Scope is optional but recommended\n- Do NOT mention AI, Claude, generated, LLM, or copilot\n\nResponse format (JSON only, no extra text):\n\nIf all changes belong to a SINGLE context, return:\n{\"type\": \"single\", \"message\": \"type(scope): description\"}\n\nIf changes span MULTIPLE distinct contexts (e.g., a bug fix AND a new feature, or docs AND refactoring), return:\n{\"type\": \"plan\", \"commits\": [{\"message\": \"type(scope): short title\", \"description\": \"brief explanation of what and why\", \"files\": [\"file1.ts\"]}, {\"message\": \"type(scope): short title\", \"description\": \"brief explanation of what and why\", \"files\": [\"file3.ts\"]}]}\n\nRules for plan:\n- \"message\" is the commit title (max 72 chars, imperative, lowercase)\n- \"description\" is a brief one-line explanation of the change purpose\n- \"files\" lists only the files belonging to that commit\n- Every staged file must appear in exactly one commit — do not leave any file unassigned\n- Only return a plan when there are clearly separate concerns. Do not split for minor differences.`;\n\nexport async function commit(opts: CommitOptions): Promise<CommitPlan | CommitAlternatives> {\n const { cwd, provider, prompt, split = \"auto\" } = opts;\n\n // Get staged files and diff\n const stagedFiles = await git.getStagedFilesList(cwd);\n const diff = await git.getStagedDiff(cwd);\n\n if (!diff) {\n throw new GitwiseError({\n code: \"NOTHING_STAGED\",\n message: \"No staged changes to commit\",\n });\n }\n\n // Sensitive file guard.\n // The user-facing Error message intentionally omits filenames: paths like\n // `prod-customer-db-credentials.json` are themselves sensitive and can leak\n // via shell history, CI logs, or pasted terminal output. The flagged files\n // are exposed only on the structured `files` property and emitted through\n // the debug logger so opt-in `--debug` (CLI flag) or `GITWISE_DEBUG=1` (env\n // var, for non-interactive/CI use) surfaces them for triage.\n const sensitiveFiles = stagedFiles.filter(isSensitiveFile);\n if (sensitiveFiles.length > 0) {\n debug(\"Sensitive files blocked from commit\", { files: sensitiveFiles });\n throw new GitwiseError({\n code: \"SENSITIVE_FILE_BLOCKED\",\n message: `SENSITIVE_FILE_BLOCKED: ${sensitiveFiles.length} file(s) matched sensitive patterns (env/pem/credentials). Re-run with --debug (or set GITWISE_DEBUG=1) to see which files were flagged.`,\n details: { files: sensitiveFiles },\n });\n }\n\n // Load template (or use built-in system prompt)\n let systemPrompt = SYSTEM_PROMPT;\n try {\n const templateContent = await loadTemplate(\"commit\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n // Only use the template as system prompt if it's a full prompt, not just a format string\n if (templateContent && !templateContent.includes(\"{{type}}\")) {\n systemPrompt = templateContent;\n }\n } catch {\n // Use built-in prompt if template not found\n }\n\n // When generating alternatives, extend the system prompt to include the third format.\n const effectiveSystemPrompt = opts.generateAlternatives\n ? `${systemPrompt}\\n\\nWhen asked to generate alternatives, return ONLY this JSON (no other text):\\n{\"type\": \"alternatives\", \"options\": [\"message1\", \"message2\", \"message3\"]}`\n : systemPrompt;\n\n // Build user message\n const userMessage = [\n `Staged files:\\n${stagedFiles.join(\"\\n\")}`,\n `\\nDiff:\\n${truncateDiff(diff)}`,\n prompt ? `\\nUser intent: ${prompt}` : \"\",\n opts.feedbackHint ? `\\nUser feedback on previous suggestion: ${opts.feedbackHint}` : \"\",\n opts.generateAlternatives\n ? `\\nIMPORTANT: Generate exactly 3 different alternative commit messages. Return JSON only: {\"type\": \"alternatives\", \"options\": [\"message1\", \"message2\", \"message3\"]}`\n : \"\",\n ].join(\"\");\n\n debug(\"Calling LLM for commit analysis\", { tier: \"fast\", fileCount: stagedFiles.length });\n\n const tier = resolveModelTier(\"commit\");\n const response = await provider.chat({ systemPrompt: effectiveSystemPrompt, userMessage, tier });\n\n const parsed = parseCommitResponse(response.content);\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n const tokensAvailable = response.tokensAvailable;\n\n // Alternatives mode: return up to 3 options instead of a plan\n if (opts.generateAlternatives) {\n const options = parseAlternativesResponse(response.content);\n if (options && options.length > 0) {\n return { kind: \"alternatives\", options, tokens, tokensAvailable } satisfies CommitAlternatives;\n }\n // Fallback: wrap whatever was parsed as a single-option list\n const fallbackMsg = parsed.type === \"single\"\n ? parsed.message\n : parsed.commits[0]?.message ?? response.content.trim().slice(0, 100);\n return { kind: \"alternatives\", options: [fallbackMsg], tokens, tokensAvailable } satisfies CommitAlternatives;\n }\n\n // Handle split modes\n if (split === \"never\") {\n // Always return single\n const message = parsed.type === \"single\"\n ? parsed.message\n : parsed.commits.map((c) => c.message).join(\"\\n\\n\");\n return {\n kind: \"single\",\n commits: [{ message, files: stagedFiles }],\n tokens,\n tokensAvailable,\n };\n }\n\n if (parsed.type === \"plan\" && parsed.commits.length > 1) {\n if (split === \"always\" || split === \"auto\") {\n const assignedFiles = new Set(parsed.commits.flatMap(c => c.files));\n const missing = stagedFiles.filter(f => !assignedFiles.has(f));\n if (missing.length > 0) {\n parsed.commits[parsed.commits.length - 1]!.files.push(...missing);\n }\n return {\n kind: \"split\",\n commits: parsed.commits,\n tokens,\n tokensAvailable,\n };\n }\n }\n\n if (split === \"always\" && parsed.type !== \"plan\") {\n throw new GitwiseError({\n code: \"NO_SPLIT_POSSIBLE\",\n message: \"split: 'always' requested but LLM returned a single-context plan\",\n exitCode: EXIT_CODES.INVALID_INTENT,\n });\n }\n\n // Single commit\n const message = parsed.type === \"single\" ? parsed.message : parsed.commits[0]?.message ?? \"chore: update\";\n return {\n kind: \"single\",\n commits: [{ message, files: stagedFiles }],\n tokens,\n tokensAvailable,\n };\n}\n\n// ─── Step factories ──────────────────────────────────────────────────────────\n\nexport interface CommitStepResult {\n priorSha: string;\n newSha: string;\n}\n\n/**\n * Transaction step that saves a named git stash as a backup of the pre-split\n * working tree, then immediately re-applies the stash so the normal flow can\n * continue with the same staged state. The predictable stash name\n * (`gitwise/split-<ISO8601>`) lets `docs/recovery.md` guide manual recovery\n * when the compensate fires.\n *\n * compensate: resets the index and working tree to HEAD (no-data-loss because\n * the stash is still present), then pops the named stash to restore the exact\n * pre-split state.\n */\nexport function takeNamedStashStep(cwd: string, stashName: string): Step<void> {\n return {\n name: `takeNamedStash(${stashName})`,\n async apply(): Promise<void> {\n await git.stashPushNamed(cwd, stashName);\n await git.stashApplyNamed(cwd, stashName);\n },\n async compensate(): Promise<void> {\n // Hard-reset + clean to reach a pristine HEAD state before popping.\n // reset --hard clears tracked/staged changes; clean -fd removes\n // untracked files that were restored by stashApplyNamed and would\n // otherwise conflict with the pop. The stash (taken with\n // --include-untracked) will restore those files during pop.\n // If pop fails, the stash is still in the list under its predictable\n // name so the user can recover manually.\n await git.resetHard(cwd, \"HEAD\");\n await git.cleanForced(cwd);\n await git.stashPopNamed(cwd, stashName);\n },\n };\n}\n\n/**\n * Transaction step that stages the given group's files and creates one commit.\n *\n * Staging goes through `stagedTree` — a tree object captured from the fully\n * staged index before the split unstaged everything — via the index alone\n * (`git reset <tree> -- <paths>`). This never reads the working tree, so a\n * planned path that no longer matches a worktree file (a staged-then-deleted\n * file, a staged deletion, or a path the plan named but that was never staged)\n * is handled by the index instead of aborting the whole commit with\n * \"pathspec did not match any files\". A group whose paths contribute nothing\n * to the index (e.g. all phantom paths) is skipped rather than failing on an\n * empty commit.\n *\n * apply — records the prior HEAD SHA (for compensate) and the new HEAD SHA\n * (as evidence of the created commit) in the result. When the group\n * stages nothing, `newSha === priorSha` and no commit is made.\n * compensate — runs `git reset --soft <priorSha>` to undo only this commit\n * while preserving the staged delta for potential retry.\n */\nexport function applyOneCommitStep(\n entry: CommitEntry,\n cwd: string,\n stagedTree: string,\n): Step<CommitStepResult> {\n const msg = entry.description\n ? `${entry.message}\\n\\n${entry.description}`\n : entry.message;\n return {\n name: `applyCommit(${entry.message})`,\n async apply(): Promise<CommitStepResult> {\n const priorSha = await git.headSha(cwd);\n await git.stagePathsFromTree(cwd, stagedTree, entry.files);\n const staged = await git.getStagedFilesList(cwd);\n if (staged.length === 0) {\n // None of the group's planned paths were actually staged (phantom or\n // already-committed). Skip instead of failing `git commit` with\n // \"nothing to commit\".\n debug(\"Skipping commit group with no staged changes\", {\n message: entry.message,\n files: entry.files,\n });\n return { priorSha, newSha: priorSha };\n }\n // files: [] — staging already done above via the index; this only commits.\n await git.applyCommit({ message: msg, files: [], cwd });\n const newSha = await git.headSha(cwd);\n return { priorSha, newSha };\n },\n async compensate({ priorSha }: CommitStepResult): Promise<void> {\n await git.resetSoft(cwd, priorSha);\n },\n };\n}\n\n// ─── applyCommitPlan ─────────────────────────────────────────────────────────\n\nexport async function applyCommitPlan(\n plan: CommitPlan,\n opts: ApplyCommitPlanOptions & { cwd: string },\n): Promise<void> {\n const { cwd, push: shouldPush = false, remote = \"origin\" } = opts;\n\n if (plan.kind === \"split\") {\n if (plan.commits.length === 0) {\n throw new GitwiseError({\n code: \"INVALID_INTENT\",\n message: \"Commit split plan has zero commits; cannot apply\",\n });\n }\n\n const stashName = `gitwise/split-${new Date().toISOString()}`;\n const releaseLock = await acquireRepoLock(cwd, { command: \"commit-split\" });\n\n try {\n const tx = new Transaction();\n const logger: Logger = { warn: logWarn };\n\n try {\n // Capture the fully-staged state as a tree object FIRST, while the index\n // still holds every staged change, so each group can be re-staged from it\n // via the index alone. This avoids re-running `git add` against the\n // working tree, which is fatal when a planned path no longer matches a\n // worktree file (see applyOneCommitStep and the single-commit note below).\n //\n // This MUST happen before takeNamedStashStep: that step runs\n // `git stash apply` without `--index`, which restores modifications to\n // already-tracked files to the WORKING TREE only, leaving the index equal\n // to HEAD. Capturing the tree after the stash would therefore snapshot an\n // empty (HEAD) index, so every per-group `git reset <tree> -- <path>`\n // would stage nothing and every group would be skipped — producing zero\n // commits while still reporting success.\n const stagedTree = await git.writeTree(cwd);\n\n // Root step: save pre-split state as a named stash backup,\n // then immediately re-apply so the working-tree files are still visible.\n await tx.run(takeNamedStashStep(cwd, stashName));\n\n // Unstage all files so per-commit staging can re-stage each group.\n await git.resetStaged(cwd);\n\n for (const entry of plan.commits) {\n await tx.run(applyOneCommitStep(entry, cwd, stagedTree));\n }\n\n // Happy path: drop the backup stash — it's no longer needed.\n await git.stashDropNamed(cwd, stashName);\n } catch (err) {\n const wrapped =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"GIT_FAILED\",\n message: err instanceof Error ? err.message : String(err),\n cause: err,\n details: { stderr: err instanceof Error ? err.message : String(err) },\n });\n await tx.rollback(wrapped, logger);\n throw wrapped;\n }\n } finally {\n await releaseLock();\n }\n } else {\n // Single commit — entry.files mirrors `git diff --cached --name-only`, so\n // every path is already staged. Re-running `git add` is both redundant\n // and fatal on staged deletions: once the deletion is in the index, the\n // file exists in neither the worktree nor the index, so pathspec\n // matching fails with \"pathspec did not match any files\".\n const entry = plan.commits[0];\n if (!entry) return;\n const msg = entry.description\n ? `${entry.message}\\n\\n${entry.description}`\n : entry.message;\n await git.applyCommit({ message: msg, files: [], cwd });\n }\n\n if (shouldPush) {\n const branch = await git.getBranch(cwd);\n await git.push(cwd, remote, branch);\n }\n}\n","import * as git from \"../infra/git.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface ReviewFinding {\n file?: string;\n line?: string;\n description: string;\n suggestion?: string;\n}\n\nexport interface ReviewResult {\n critical: ReviewFinding[];\n suggestions: ReviewFinding[];\n nitpicks: ReviewFinding[];\n markdown: string;\n tokens: { input: number; output: number };\n /** AD-002: false when the active provider doesn't report token usage. */\n tokensAvailable: boolean;\n}\n\nexport interface ReviewOptions {\n cwd: string;\n provider: LLMProvider;\n baseBranch?: string;\n prompt?: string;\n tier?: \"fast\" | \"balanced\" | \"powerful\";\n templatesPath?: string;\n repoRoot?: string;\n}\n\nconst MAX_DIFF_CHARS = 80_000;\n\n// Mirrors packages/core/templates/review.md so `gw review` stays functional\n// when a user-customized templates directory omits review.md or when the\n// bundled template is missing from a packaged build.\nconst DEFAULT_REVIEW_TEMPLATE = `You are a senior code reviewer. Analyze the diff and produce a code review with findings in these categories:\n\n## Critical\nIssues that must be fixed before merging (bugs, security, data loss).\n\n## Suggestions\nImprovements worth considering (performance, readability, patterns).\n\n## Nitpicks\nMinor style or convention issues.\n\nFor each finding, include:\n- File and line reference\n- Description of the issue\n- Suggested fix\n\nEnd with a summary: total findings count per category and overall recommendation (approve, request changes).\n\n{{diff}}\n`;\n\nfunction truncateDiff(diff: string): string {\n if (diff.length <= MAX_DIFF_CHARS) return diff;\n return diff.slice(0, MAX_DIFF_CHARS) + \"\\n\\n[diff truncated — too large for context window]\";\n}\n\n// ─── Response parsing ────────────────────────────────────────────────────────\n\ninterface ParsedReviewResponse {\n critical: ReviewFinding[];\n suggestions: ReviewFinding[];\n nitpicks: ReviewFinding[];\n}\n\nfunction extractSection(markdown: string, heading: string): string[] {\n const headingRegex = new RegExp(`##\\\\s*${heading}\\\\b([\\\\s\\\\S]*?)(?=##|$)`, \"i\");\n const match = markdown.match(headingRegex);\n if (!match || !match[1]) return [];\n return match[1]\n .split(\"\\n\")\n .map((l) => l.replace(/^[-*•]\\s*/, \"\").trim())\n .filter((l) => l.length > 0);\n}\n\nfunction linesToFindings(lines: string[]): ReviewFinding[] {\n return lines.map((line) => ({\n description: line,\n }));\n}\n\nfunction parseReviewMarkdown(text: string): ParsedReviewResponse {\n return {\n critical: linesToFindings(extractSection(text, \"Critical\")),\n suggestions: linesToFindings(extractSection(text, \"Suggestions\")),\n nitpicks: linesToFindings(extractSection(text, \"Nitpicks\")),\n };\n}\n\nfunction buildMarkdown(parsed: ParsedReviewResponse): string {\n const sections: string[] = [];\n\n sections.push(\"## Critical\");\n if (parsed.critical.length > 0) {\n sections.push(...parsed.critical.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No critical issues found._\");\n }\n\n sections.push(\"\\n## Suggestions\");\n if (parsed.suggestions.length > 0) {\n sections.push(...parsed.suggestions.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No suggestions._\");\n }\n\n sections.push(\"\\n## Nitpicks\");\n if (parsed.nitpicks.length > 0) {\n sections.push(...parsed.nitpicks.map((f) => `- ${f.description}`));\n } else {\n sections.push(\"_No nitpicks._\");\n }\n\n return sections.join(\"\\n\");\n}\n\n// ─── Core review function ────────────────────────────────────────────────────\n\nexport async function review(opts: ReviewOptions): Promise<ReviewResult> {\n const { cwd, provider, prompt, tier: requestedTier } = opts;\n\n // Resolve base branch\n const baseBranch = opts.baseBranch ?? await resolveBaseBranch(cwd);\n\n // Get diff\n let diff: string;\n try {\n diff = await git.getDiff(cwd, baseBranch);\n } catch (err: unknown) {\n if (isUnknownRevisionError(err)) {\n // Base branch is unknown locally (not fetched, typo, fresh clone). Fall back to\n // the working-tree diff so the caller still gets a review of pending edits.\n diff = await git.getDiff(cwd);\n } else {\n const reason = errorMessage(err);\n throw new GitwiseError({\n code: \"DIFF_FAILED\",\n message: `Failed to compute diff against ${baseBranch}: ${reason}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n }\n }\n\n if (!diff) {\n throw new GitwiseError({\n code: \"EMPTY_DIFF\",\n message: `No changes found between current branch and ${baseBranch}`,\n exitCode: EXIT_CODES.NOTHING_STAGED,\n });\n }\n\n // Load review template, falling back to the embedded default when the\n // resolved templates directory does not provide review.md.\n let templateContent: string;\n try {\n templateContent = await loadTemplate(\"review\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n } catch {\n templateContent = DEFAULT_REVIEW_TEMPLATE;\n }\n\n // Build system prompt from template (review.md is used as the user message with diff injected)\n const truncated = truncateDiff(diff);\n const userMessage = interpolate(templateContent, { diff: truncated })\n + (prompt ? `\\n\\nAdditional context: ${prompt}` : \"\");\n\n // Use default system prompt for the review\n const systemPrompt = \"You are a senior code reviewer. Analyze the provided diff carefully and return findings.\";\n\n const defaultTier = resolveModelTier(\"review\") as \"fast\" | \"balanced\" | \"powerful\";\n const activeTier = requestedTier ?? defaultTier;\n\n debug(\"Calling LLM for code review\", { tier: activeTier, diffLength: truncated.length });\n\n const response = await provider.chat({ systemPrompt, userMessage, tier: activeTier });\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n\n // Parse findings from response\n const parsed = parseReviewMarkdown(response.content);\n const markdown = buildMarkdown(parsed);\n\n return {\n critical: parsed.critical,\n suggestions: parsed.suggestions,\n nitpicks: parsed.nitpicks,\n markdown,\n tokens,\n tokensAvailable: response.tokensAvailable,\n };\n}\n\nasync function resolveBaseBranch(cwd: string): Promise<string> {\n try {\n return await git.detectBaseBranch(cwd);\n } catch {\n return \"main\";\n }\n}\n\nfunction errorMessage(err: unknown): string {\n if (err && typeof err === \"object\" && typeof (err as { message?: unknown }).message === \"string\") {\n return (err as { message: string }).message;\n }\n return String(err);\n}\n\n// Duck-typed instead of `instanceof Error` because jest's --experimental-vm-modules\n// can run modules in separate VM realms, where the Error constructor differs.\nfunction isUnknownRevisionError(err: unknown): boolean {\n if (err === null || typeof err !== \"object\") return false;\n const errObj = err as { message?: unknown; stderr?: unknown };\n const message = typeof errObj.message === \"string\" ? errObj.message : \"\";\n const stderr = typeof errObj.stderr === \"string\" ? errObj.stderr : \"\";\n const text = `${message}\\n${stderr}`;\n return /unknown revision|bad revision|not a valid object name|ambiguous argument/i.test(text);\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as git from \"../infra/git.js\";\nimport { isGhAvailable, createPR, updatePR } from \"../infra/github.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\n\nconst exec = promisify(execFile);\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport interface PrDraft {\n title: string;\n body: string;\n existingPrNumber?: number;\n tokens: { input: number; output: number };\n /** AD-002: false when the active provider doesn't report token usage. */\n tokensAvailable: boolean;\n}\n\nexport interface PrOptions {\n cwd: string;\n provider: LLMProvider;\n baseBranch?: string;\n prompt?: string;\n templatesPath?: string;\n repoRoot?: string;\n}\n\nexport interface ApplyPrOptions {\n cwd: string;\n draft?: boolean;\n baseBranch?: string;\n}\n\nexport interface ApplyPrResult {\n url: string;\n}\n\n// ─── PR response parsing ─────────────────────────────────────────────────────\n\nfunction parsePrResponse(content: string): { title: string; body: string } {\n const titleMatch = content.match(/^TITLE:\\s*(.+)$/m);\n const title = titleMatch ? titleMatch[1]!.trim() : \"Update\";\n const separatorIdx = content.indexOf(\"---\");\n const body = separatorIdx >= 0 ? content.slice(separatorIdx + 3).trim() : content;\n return { title, body };\n}\n\n// ─── Detect existing PR ──────────────────────────────────────────────────────\n\nasync function detectExistingPr(cwd: string): Promise<number | undefined> {\n try {\n const result = await exec(\"gh\", [\"pr\", \"view\", \"--json\", \"number\", \"--jq\", \".number\"], { cwd });\n const numberStr = result.stdout.trim();\n if (numberStr) {\n const n = parseInt(numberStr, 10);\n if (!isNaN(n)) return n;\n }\n } catch {\n // No existing PR or gh not available\n }\n return undefined;\n}\n\n// ─── Core pr function ────────────────────────────────────────────────────────\n\nconst PR_SYSTEM_PROMPT = `You are a developer creating a pull request. Based on the commit log, generate a PR title and description.\n\nOutput format (nothing else):\nTITLE: <concise title, max 70 chars>\n---\n## Summary\n<1-3 bullet points>\n\n## Changes\n<changelog based on commits>\n\n## Test Plan\n<testing checklist>`;\n\nexport async function pr(opts: PrOptions): Promise<PrDraft> {\n const { cwd, provider, prompt } = opts;\n\n const baseBranch = opts.baseBranch ?? await resolveBaseBranch(cwd);\n const currentBranch = await git.getBranch(cwd);\n const commits = await git.getLog(cwd, `${baseBranch}..HEAD`);\n\n if (!commits) {\n throw new GitwiseError({\n code: \"NO_COMMITS\",\n message: `No commits found on this branch relative to ${baseBranch}`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // Load PR template or use built-in\n let systemPrompt = PR_SYSTEM_PROMPT;\n let userMessageFromTemplate = `Branch: ${currentBranch}\\n\\nCommits:\\n${commits}`;\n\n try {\n const templateContent = await loadTemplate(\"pr\", {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n });\n // If template contains placeholders, use it as user message template\n if (templateContent && templateContent.includes(\"{{\")) {\n userMessageFromTemplate = interpolate(templateContent, {\n branch: currentBranch,\n commits,\n summary: \"\",\n changelog: \"\",\n test_plan: \"\",\n });\n }\n } catch {\n // Use built-in prompt\n }\n\n const userMessage = userMessageFromTemplate\n + (prompt ? `\\n\\nAdditional context: ${prompt}` : \"\");\n\n // Detect existing PR\n const existingPrNumber = await detectExistingPr(cwd);\n\n debug(\"Calling LLM for PR draft\", { tier: \"fast\", branch: currentBranch, existingPrNumber });\n\n const tier = resolveModelTier(\"pr\");\n const response = await provider.chat({ systemPrompt, userMessage, tier });\n const tokens = { input: response.tokens.input, output: response.tokens.output };\n\n const { title, body } = parsePrResponse(response.content);\n\n return {\n title,\n body,\n existingPrNumber,\n tokens,\n tokensAvailable: response.tokensAvailable,\n };\n}\n\n// ─── applyPr ────────────────────────────────────────────────────────────────\n\nexport async function applyPr(draft: PrDraft, opts: ApplyPrOptions): Promise<ApplyPrResult> {\n const { cwd, draft: isDraft = false, baseBranch } = opts;\n\n const ghAvailable = await isGhAvailable();\n if (!ghAvailable) {\n throw new GitwiseError({\n code: \"GH_UNAVAILABLE\",\n message: \"gh CLI is not installed — cannot create or update a PR\",\n exitCode: EXIT_CODES.GH_FAILED,\n details: { draft },\n });\n }\n\n if (draft.existingPrNumber !== undefined) {\n const updated = await updatePR({\n prNumber: draft.existingPrNumber,\n title: draft.title,\n body: draft.body,\n cwd,\n });\n return { url: updated.url };\n }\n\n const created = await createPR({\n title: draft.title,\n body: draft.body,\n base: baseBranch,\n cwd,\n draft: isDraft,\n });\n return { url: created.url };\n}\n\nasync function resolveBaseBranch(cwd: string): Promise<string> {\n try {\n return await git.detectBaseBranch(cwd);\n } catch {\n return \"main\";\n }\n}\n","import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport * as git from \"../infra/git.js\";\nimport { isGhAvailable, createGitHubRelease } from \"../infra/github.js\";\nimport { fileExists, readJSON, writeJSON, ensureDir } from \"../infra/filesystem.js\";\nimport { loadTemplate } from \"../template/loader.js\";\nimport { interpolate } from \"../template/interpolate.js\";\nimport type { LLMProvider } from \"../providers/types.js\";\nimport { resolveModelTier } from \"../providers/model-router.js\";\nimport { debug, warn as logWarn } from \"../infra/logger.js\";\nimport { readRepoConfig } from \"../config/repo.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport { Transaction, type Logger, type Step } from \"../infra/transaction.js\";\nimport { acquireRepoLock } from \"../infra/lockfile.js\";\nimport {\n createReleaseStrategy,\n type ReleaseStrategyName,\n} from \"../strategies/release.js\";\nimport {\n applyGitignoreEntry,\n deleteReleasePlan,\n ensureGitignored,\n loadReleasePlan,\n saveReleasePlan,\n type PersistedReleasePlan,\n} from \"./release-plan.js\";\n\nconst RELEASE_PLAN_REL_PATH = \".gitwise/release-plan.json\";\n// ADR-003 preserves the notes file (.gitwise/release-<v>.md) after finish so the\n// user can keep editing or archiving it. Gitignoring the glob keeps every past\n// version's notes file out of the next prepare's clean-tree check without\n// touching the file itself.\nconst RELEASE_NOTES_GLOB_REL_PATH = \".gitwise/release-*.md\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type BumpType = \"major\" | \"minor\" | \"patch\";\n\nexport interface ReleasePlan {\n suggestedBump: BumpType;\n newVersion: string;\n currentVersion: string;\n changelog: string;\n notes: string;\n commits: string;\n tokens: { input: number; output: number };\n /**\n * AD-002: the logical AND of every contributing LLM call's tokensAvailable\n * (version-suggestion, changelog, notes) — false if any of them didn't\n * report usage. In practice always uniform within one run, since a run\n * uses exactly one provider.\n */\n tokensAvailable: boolean;\n}\n\nexport interface ReleaseOptions {\n cwd: string;\n provider: LLMProvider;\n bump?: BumpType;\n language?: string;\n templatesPath?: string;\n repoRoot?: string;\n workspacePropagation?: boolean;\n}\n\nexport interface ApplyReleaseOptions {\n cwd: string;\n tagAndPush?: boolean;\n createGhRelease?: boolean;\n workspacePropagation?: boolean;\n /** Forwarded to finishRelease. Default true. Set false only for testing. */\n signTags?: boolean;\n}\n\n// ─── Version utilities ────────────────────────────────────────────────────────\n\nconst STRICT_SEMVER_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)$/;\n\nexport function bumpVersion(current: string, type: BumpType): string {\n const match = STRICT_SEMVER_RE.exec(current);\n if (!match) {\n throw new GitwiseError({\n code: \"INVALID_VERSION\",\n message: `Invalid current version: ${current}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n const major = Number(match[1]);\n const minor = Number(match[2]);\n const patch = Number(match[3]);\n switch (type) {\n case \"major\": return `${major + 1}.0.0`;\n case \"minor\": return `${major}.${minor + 1}.0`;\n case \"patch\": return `${major}.${minor}.${patch + 1}`;\n // Belt-and-suspenders: TS makes this unreachable for typed callers, but\n // any JS caller (or a cast like `parseVersionSuggestion`'s former one)\n // could smuggle in a bogus value. Surface it as INVALID_VERSION instead\n // of silently returning undefined and minting `release/undefined` /\n // `vundefined` artifacts downstream.\n default: throw new GitwiseError({\n code: \"INVALID_VERSION\",\n message: `Invalid bump type: ${String(type)}`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n}\n\ninterface VersionSuggestion {\n suggestion: BumpType;\n reasoning: string;\n}\n\nfunction parseVersionSuggestion(raw: string): VersionSuggestion | null {\n try {\n const cleaned = raw.replace(/```(?:json)?\\n?/g, \"\").trim();\n const parsed = JSON.parse(cleaned) as Record<string, unknown>;\n const { suggestion, reasoning } = parsed;\n // Constrain `suggestion` to the BumpType union — a `typeof === \"string\"`\n // check used to accept \"huge\" / \"feature\" / \"\" and cast them straight to\n // BumpType, after which bumpVersion's switch fell through and returned\n // undefined. Returning null on garbage routes release() to heuristicBump,\n // its existing safety net.\n if (\n (suggestion === \"major\" || suggestion === \"minor\" || suggestion === \"patch\") &&\n typeof reasoning === \"string\"\n ) {\n return { suggestion, reasoning };\n }\n } catch { /* fallback */ }\n return null;\n}\n\n/**\n * Heuristic bump from commit log strings.\n * BREAKING CHANGE / ! marker → major\n * feat: → minor\n * fix:/chore:/etc → patch\n */\nexport function heuristicBump(commits: string): BumpType {\n if (/BREAKING CHANGE|!:/.test(commits)) return \"major\";\n if (/^feat[:(]/m.test(commits)) return \"minor\";\n return \"patch\";\n}\n\nconst CHANGELOG_HEADER = `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com),\nand this project adheres to [Semantic Versioning](https://semver.org/).\n\n`;\n\n// ─── Core release function ────────────────────────────────────────────────────\n\n/**\n * @deprecated Prefer the explicit two-phase lifecycle ({@link prepareRelease}\n * → caller-supplied confirm → {@link finishRelease}) or the unified\n * {@link runReleaseInProcess} helper. Kept exported so the legacy skill script\n * and any external callers using `release()` + `applyRelease()` keep working;\n * a future task may collapse it into `prepareRelease`.\n */\nexport async function release(opts: ReleaseOptions): Promise<ReleasePlan> {\n const { cwd, provider, language = \"en\" } = opts;\n\n const pkgPath = join(cwd, \"package.json\");\n if (!(await fileExists(pkgPath))) {\n throw new GitwiseError({\n code: \"NO_PACKAGE_JSON\",\n message: \"No package.json found\",\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n const pkg = await readJSON<{ version: string; name?: string }>(pkgPath);\n const currentVersion = pkg.version;\n const projectName = pkg.name ?? \"project\";\n\n const lastTag = await git.getLatestTag(cwd);\n const logRange = lastTag ? `${lastTag}..HEAD` : undefined;\n const commits = await git.getLog(cwd, logRange);\n\n if (!commits) {\n throw new GitwiseError({\n code: \"NO_COMMITS\",\n message: \"No new commits since last release\",\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n const templateOpts = {\n repoRoot: opts.repoRoot ?? cwd,\n templatesPath: opts.templatesPath,\n };\n\n const tier = resolveModelTier(\"release\");\n let totalInput = 0;\n let totalOutput = 0;\n // AD-002: aggregate tokensAvailable as the logical AND of every\n // contributing call — starts true, and any call that doesn't report usage\n // flips the whole plan's tokensAvailable to false.\n let tokensAvailable = true;\n\n // 1. Determine bump type\n let suggestedBump: BumpType;\n if (opts.bump) {\n suggestedBump = opts.bump;\n } else {\n const versionTemplate = await loadTemplate(\"release-version\", templateOpts);\n const versionPrompt = interpolate(versionTemplate, { currentVersion });\n\n debug(\"Calling LLM for version suggestion\");\n const versionResponse = await provider.chat({\n systemPrompt: \"You are a release engineer. Respond with JSON only.\",\n userMessage: `${versionPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += versionResponse.tokens.input;\n totalOutput += versionResponse.tokens.output;\n tokensAvailable = versionResponse.tokensAvailable;\n\n const suggestion = parseVersionSuggestion(versionResponse.content);\n suggestedBump = suggestion?.suggestion ?? heuristicBump(commits);\n }\n\n const newVersion = bumpVersion(currentVersion, suggestedBump);\n\n // 2. Generate changelog\n const changelogTemplate = await loadTemplate(\"release-changelog\", templateOpts);\n const changelogPrompt = interpolate(changelogTemplate, { projectName });\n\n debug(\"Calling LLM for changelog generation\");\n const changelogResponse = await provider.chat({\n systemPrompt: \"You are a technical writer generating a changelog. Follow Keep a Changelog format.\",\n userMessage: `${changelogPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += changelogResponse.tokens.input;\n totalOutput += changelogResponse.tokens.output;\n tokensAvailable = tokensAvailable && changelogResponse.tokensAvailable;\n const changelog = changelogResponse.content;\n\n // 3. Generate release notes\n const notesTemplate = await loadTemplate(\"release-notes\", templateOpts);\n const notesPrompt = interpolate(notesTemplate, {\n version: newVersion,\n projectName,\n language,\n });\n\n debug(\"Calling LLM for release notes generation\");\n const notesResponse = await provider.chat({\n systemPrompt: \"You are a product communications specialist writing release notes.\",\n userMessage: `${notesPrompt}\\n\\nCommits:\\n${commits}`,\n tier,\n });\n totalInput += notesResponse.tokens.input;\n totalOutput += notesResponse.tokens.output;\n tokensAvailable = tokensAvailable && notesResponse.tokensAvailable;\n const notes = notesResponse.content;\n\n return {\n suggestedBump,\n newVersion,\n currentVersion,\n changelog,\n notes,\n commits,\n tokens: { input: totalInput, output: totalOutput },\n tokensAvailable,\n };\n}\n\n// ─── prepareRelease ──────────────────────────────────────────────────────────\n\nexport interface PrepareReleaseOptions extends ReleaseOptions {\n /** Strategy override; if omitted, resolved from RepoConfig (default \"github-flow\"). */\n strategy?: ReleaseStrategyName;\n /** Develop branch name override; if omitted, resolved from RepoConfig (default \"develop\"). */\n developBranch?: string;\n}\n\n/**\n * Step factory: create a gitflow release branch off `startPoint` and capture\n * the previously-checked-out branch so compensate can return there.\n *\n * Compensate: force-checkout the previously-checked-out branch (discards any\n * working-tree dirt accumulated on the release branch from later steps that\n * failed before their own compensate could fire) then `git branch -D` the\n * release branch. ADR-004 §Decision item 1 names this exact compensate.\n */\nexport function createReleaseBranchStep(\n cwd: string,\n branchName: string,\n startPoint: string,\n): Step<{ branchName: string; previousBranch: string }> {\n return {\n name: `create-branch:${branchName}`,\n apply: async () => {\n const previousBranch = await git.getBranch(cwd);\n await git.createBranch(cwd, branchName, startPoint);\n return { branchName, previousBranch };\n },\n compensate: async ({ branchName: branch, previousBranch }) => {\n // Force-checkout so any uncommitted working-tree mutation that the\n // catch path could not undo (e.g. a per-file compensate that itself\n // threw and is now reported as ROLLBACK_PARTIAL) is still discarded\n // before we try to delete the branch.\n await git.checkoutForce(cwd, previousBranch);\n await git.deleteBranch(cwd, branch, true);\n },\n };\n}\n\n/**\n * Step factory: write `contents` to `filePath` and capture any pre-existing\n * file's prior bytes so compensate can restore byte-for-byte.\n *\n * The captured state is either the original `Buffer` (file existed) or\n * `null` (file did not exist). Compensate restores the original bytes or\n * `fs.unlink`s the file accordingly. Mirrors the contract of\n * {@link writeWorkspaceVersionStep} for non-JSON payloads.\n */\nexport function writeFileStep(\n filePath: string,\n contents: string | Buffer,\n): Step<Buffer | null> {\n return {\n name: `write-file:${filePath}`,\n apply: async () => {\n const priorBytes = (await fileExists(filePath))\n ? await readFile(filePath)\n : null;\n await writeFile(filePath, contents);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(filePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(filePath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: apply both `ensureGitignored` calls (plan file + notes glob)\n * to `.gitignore`, capturing the file's prior bytes (or `null` when it did\n * not exist) so compensate can restore the original state.\n *\n * Compensate writes the captured bytes back, or unlinks the file when it\n * did not exist pre-apply. ADR-004 §Decision item 1 names \"gitignore\n * mutation → revert original bytes\" as the canonical compensate.\n */\nexport function mutateGitignoreStep(cwd: string): Step<Buffer | null> {\n const gitignorePath = join(cwd, \".gitignore\");\n return {\n name: \"mutate-gitignore\",\n apply: async () => {\n const priorBytes = (await fileExists(gitignorePath))\n ? await readFile(gitignorePath)\n : null;\n await ensureGitignored(cwd, RELEASE_PLAN_REL_PATH);\n await ensureGitignored(cwd, RELEASE_NOTES_GLOB_REL_PATH);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(gitignorePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(gitignorePath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: prepend a Keep-a-Changelog entry for `newVersion` to\n * `CHANGELOG.md`, creating the file with the standard header if it does not\n * exist. Captures the file's prior bytes (or `null`) so compensate can\n * restore it byte-for-byte.\n */\nexport function writeChangelogStep(\n cwd: string,\n newVersion: string,\n entryBody: string,\n): Step<Buffer | null> {\n const changelogPath = join(cwd, \"CHANGELOG.md\");\n return {\n name: \"write-changelog\",\n apply: async () => {\n const priorBytes = (await fileExists(changelogPath))\n ? await readFile(changelogPath)\n : null;\n const date = new Date().toISOString().split(\"T\")[0];\n const versionHeader = `## [${newVersion}] - ${date}\\n\\n${entryBody}\\n\\n`;\n if (priorBytes !== null) {\n const existing = priorBytes.toString(\"utf-8\");\n const headerEnd = existing.indexOf(\"## [\");\n if (headerEnd > 0) {\n await writeFile(\n changelogPath,\n existing.slice(0, headerEnd) + versionHeader + existing.slice(headerEnd),\n \"utf-8\",\n );\n } else {\n const body = existing.startsWith(CHANGELOG_HEADER)\n ? existing.slice(CHANGELOG_HEADER.length)\n : existing;\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader + body,\n \"utf-8\",\n );\n }\n } else {\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader,\n \"utf-8\",\n );\n }\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n if (priorBytes === null) {\n try {\n await unlink(changelogPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n } else {\n await writeFile(changelogPath, priorBytes);\n }\n },\n };\n}\n\n/**\n * Step factory: stage `files` and create the release commit on the current\n * branch. Captures the pre-commit `HEAD` SHA so compensate can `git reset\n * --hard` back to it, undoing the commit AND any staged/working-tree\n * changes that the prior file-write compensates have not yet reverted.\n *\n * The hard reset is intentional: when the release branch is later deleted\n * by {@link createReleaseBranchStep}'s compensate, a force-checkout would\n * need to be tolerant of working-tree dirt. Resetting the commit also\n * cleans the working tree so subsequent compensates run from a known state.\n */\nexport function commitReleaseStep(\n cwd: string,\n message: string,\n files: string[],\n): Step<string> {\n return {\n name: \"commit-release-bump\",\n apply: async () => {\n const preSha = await git.headSha(cwd);\n await git.applyCommit({ message, files, cwd });\n return preSha;\n },\n compensate: async (preSha) => {\n await git.resetHard(cwd, preSha);\n },\n };\n}\n\n/**\n * Step factory: persist the release plan JSON. ADR-004 §Decision item 1\n * names this as the LAST step of `prepareRelease` so a partial run never\n * leaves a plan file referencing a half-prepared branch.\n *\n * Compensate `fs.unlink`s the plan file (idempotent: ENOENT is swallowed).\n */\nexport function savePlanStep(\n cwd: string,\n plan: PersistedReleasePlan,\n): Step<void> {\n return {\n name: \"save-plan\",\n apply: async () => {\n await saveReleasePlan(cwd, plan);\n },\n compensate: async () => {\n await deleteReleasePlan(cwd);\n },\n };\n}\n\n/**\n * Run the planning half of the two-phase release lifecycle (ADR-001).\n *\n * Resolves the active strategy, validates preconditions (clean tree, develop\n * exists for gitflow, no pre-existing release branch), runs the LLM planner\n * via {@link release}, then drives every side-effectful step inside a single\n * {@link Transaction} under a `.gitwise/.lock`. On any failure the\n * transaction rolls back in LIFO order: plan file unlinked, release commit\n * reset (gitflow), `.gitignore` reverted, CHANGELOG.md reverted, workspace\n * + root manifests reverted, notes file unlinked, release branch deleted.\n * Compensate failures surface as a `ROLLBACK_PARTIAL` log warning emitted by\n * the transaction itself; the original cause is always rethrown unchanged.\n *\n * ADR-004 §Decision item 1: the release plan file is written LAST so its\n * presence on disk is a contract that every preceding step succeeded.\n */\nexport async function prepareRelease(\n opts: PrepareReleaseOptions,\n): Promise<PersistedReleasePlan> {\n const { cwd } = opts;\n\n // 1. Resolve strategy + develop branch from opts → repo config → defaults.\n const repoConfig = await readRepoConfig(cwd);\n const strategyName: ReleaseStrategyName =\n opts.strategy ?? repoConfig?.releaseStrategy ?? \"github-flow\";\n const developBranch =\n opts.developBranch ?? repoConfig?.developBranch ?? \"develop\";\n const strategy = createReleaseStrategy(strategyName);\n\n debug(\"release.prepare.start\", { strategy: strategyName, cwd });\n\n const releaseLock = await acquireRepoLock(cwd, {\n command: \"release prepare\",\n });\n\n try {\n // 2. Preflight — refuse to start if the working tree is dirty. Runs\n // before any LLM call so we don't pay tokens on a doomed run.\n //\n // Filter `.gitignore` from the dirty set: prepare mutates it via\n // `ensureGitignored`, and on github-flow that change is intentionally\n // deferred to finish (no commit happens in prepare). After a prior\n // github-flow `prepare → abort`, the leftover ` M .gitignore` (or\n // `?? .gitignore`) would otherwise block the next prepare and force the\n // user to manually `git checkout -- .gitignore`. Matches the symmetric\n // tolerance in finishRelease's step 2c.\n //\n // Also filter the `.gitwise/` directory entry: `acquireRepoLock` (just\n // above) writes `.gitwise/.lock` to coordinate concurrent gitwise runs,\n // which would otherwise surface here as `?? .gitwise/` on a fresh repo\n // and reject every prepare. The lockfile is gitwise's own scratch, not\n // user state.\n const dirtyEntries = (await git.status(cwd))\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+$/, \"\"))\n .filter((line) => line.length >= 3)\n .filter((line) => {\n const path = line.slice(3).trim();\n if (path === \".gitignore\") return false;\n if (path === \".gitwise/\" || path === \".gitwise\") return false;\n if (path.startsWith(\".gitwise/\")) return false;\n return true;\n });\n if (dirtyEntries.length > 0) {\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before preparing a release — commit or stash first.\\n${dirtyEntries.join(\"\\n\")}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n\n // 3. Refuse to clobber an in-flight plan (ADR-003 plan-first delete\n // invariant). Running this before the LLM call keeps retries cheap.\n const existingPlan = await loadReleasePlan(cwd);\n if (existingPlan) {\n throw new GitwiseError({\n code: \"RELEASE_PLAN_EXISTS\",\n message: `An in-flight release plan already exists at .gitwise/release-plan.json for v${existingPlan.newVersion} (${existingPlan.strategy}). Finish it with \"gw release finish\" or discard it with \"gw release abort\" before preparing a new release.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n\n // 4. Strategy preconditions — gitflow requires the develop branch to exist.\n if (strategy.requiresDevelop()) {\n if (!(await git.branchExists(cwd, developBranch))) {\n throw new GitwiseError({\n code: \"STRATEGY_DEVELOP_MISSING\",\n message: `GitFlow requires a \"${developBranch}\" branch but it does not exist. Create it first (e.g. git checkout -b ${developBranch}).`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n }\n\n // 5. Capture the user's HEAD before any mutation so the plan records\n // where prepare started — useful for stale-plan diagnostics in finish.\n const baseCommit = await git.headSha(cwd);\n\n // 6. Run the LLM planner (also raises NO_PACKAGE_JSON / NO_COMMITS /\n // INVALID_VERSION before we touch the filesystem).\n const plan = await release(opts);\n\n // 7. Now that newVersion is known, derive the release branch name and\n // check it doesn't already exist (gitflow only). Failure here predates\n // any transactional mutation; raise the typed `RELEASE_BRANCH_CONFLICT`\n // surfaced by ADR-004 with a docs/recovery.md hint.\n const releaseBranch = strategy.releaseBranchFor(plan.newVersion);\n if (releaseBranch && (await git.branchExists(cwd, releaseBranch))) {\n throw new GitwiseError({\n code: \"RELEASE_BRANCH_CONFLICT\",\n message: `Release branch \"${releaseBranch}\" already exists. Delete it or pick a different version — see docs/recovery.md if a prior prepare crashed before its rollback finished.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n details: { releaseBranch, newVersion: plan.newVersion },\n });\n }\n\n // 8. Drive every side-effectful mutation through a single Transaction\n // so a failure at any step rolls every prior step back in LIFO order.\n const tx = new Transaction();\n await ensureDir(join(cwd, \".gitwise\"));\n let targetBranch: string;\n\n try {\n if (releaseBranch) {\n await tx.run(\n createReleaseBranchStep(cwd, releaseBranch, developBranch),\n );\n debug(\"release.prepare.branch.created\", {\n branch: releaseBranch,\n from: developBranch,\n });\n targetBranch = releaseBranch;\n } else {\n targetBranch = await git.getBranch(cwd);\n }\n\n const notesPath = join(cwd, \".gitwise\", `release-${plan.newVersion}.md`);\n await tx.run(writeFileStep(notesPath, plan.notes));\n\n let propagatedManifests: string[] = [];\n if (releaseBranch) {\n const pkgPath = join(cwd, \"package.json\");\n await tx.run(writeWorkspaceVersionStep(pkgPath, plan.newVersion));\n\n if (opts.workspacePropagation) {\n propagatedManifests = await runWorkspaceVersionStepsInto(\n tx,\n cwd,\n plan.newVersion,\n );\n }\n\n await tx.run(writeChangelogStep(cwd, plan.newVersion, plan.changelog));\n }\n\n await tx.run(mutateGitignoreStep(cwd));\n\n if (releaseBranch) {\n const stagePaths = [\"package.json\", \"CHANGELOG.md\"];\n if (await fileExists(join(cwd, \".gitignore\"))) {\n stagePaths.push(\".gitignore\");\n }\n stagePaths.push(...propagatedManifests);\n await tx.run(\n commitReleaseStep(\n cwd,\n `chore(release): v${plan.newVersion}`,\n stagePaths,\n ),\n );\n }\n\n const persistedPlan: PersistedReleasePlan = {\n schema: 1,\n strategy: strategyName,\n currentVersion: plan.currentVersion,\n newVersion: plan.newVersion,\n suggestedBump: plan.suggestedBump,\n changelog: plan.changelog,\n notes: plan.notes,\n commits: plan.commits,\n preparedAt: new Date().toISOString(),\n baseCommit,\n targetBranch,\n releaseBranchCreated: releaseBranch !== null,\n tokens: plan.tokens,\n tokensAvailable: plan.tokensAvailable,\n };\n\n await tx.run(savePlanStep(cwd, persistedPlan));\n debug(\"release.prepare.plan.saved\", {\n newVersion: plan.newVersion,\n targetBranch,\n releaseBranchCreated: persistedPlan.releaseBranchCreated,\n });\n\n return persistedPlan;\n } catch (err) {\n const reason =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"RELEASE_PREPARE_FAILED\",\n message: `Failed to prepare release: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n debug(\"release.prepare.rollback.start\", {\n appliedSteps: tx.size,\n code: reason.code,\n });\n await tx.rollback(reason, txLogger);\n throw reason;\n }\n } finally {\n await releaseLock();\n }\n}\n\n// ─── applyRelease ────────────────────────────────────────────────────────────\n\n/**\n * @deprecated Prefer the explicit two-phase lifecycle ({@link prepareRelease}\n * → caller-supplied confirm → {@link finishRelease}) or the unified\n * {@link runReleaseInProcess} helper.\n *\n * Apply an in-memory {@link ReleasePlan} to the repository. Kept exported as\n * a thin adapter so the legacy skill script and any external callers that\n * still pair `release()` with `applyRelease()` keep working. Internally this\n * builds a {@link PersistedReleasePlan} from the in-memory plan, writes it\n * (and the user-editable notes file) to disk so {@link finishRelease} can\n * consume it, and then delegates the mutation pipeline to\n * {@link finishRelease}. Both phases now travel through the same code path.\n *\n * Preflight: throws `WORKING_TREE_DIRTY` if the working tree has uncommitted\n * changes, and (when `tagAndPush` is enabled) `TAG_EXISTS` if the target\n * `v<newVersion>` ref already exists. Both checks run before any file or git\n * mutation so a failed run leaves the repo untouched.\n */\nexport async function applyRelease(\n plan: ReleasePlan,\n opts: ApplyReleaseOptions,\n): Promise<void> {\n const { cwd, tagAndPush = true, createGhRelease = true, workspacePropagation = false, signTags } = opts;\n\n // Preflight — refuse to start if the repo isn't in a state where a release\n // commit + tag can be applied atomically. Unconditional tag check matches\n // finishRelease's stale-plan invariant: a pre-existing v<newVersion> tag\n // means the plan is inconsistent regardless of tagAndPush.\n const dirty = (await git.status(cwd)).trim();\n if (dirty) {\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before releasing — commit or stash first.\\n${dirty}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n const tag = `v${plan.newVersion}`;\n if (await git.tagExists(cwd, tag)) {\n throw new GitwiseError({\n code: \"TAG_EXISTS\",\n message: `Tag ${tag} already exists. Bump to a new version or delete the tag.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n\n // Build a PersistedReleasePlan equivalent of the in-memory plan and hand it\n // off to finishRelease. The legacy contract has only ever been exercised on\n // single-branch (github-flow) repos, so `strategy: \"github-flow\"` and\n // `releaseBranchCreated: false` are the correct fixed values here.\n await ensureDir(join(cwd, \".gitwise\"));\n await writeFile(\n join(cwd, \".gitwise\", `release-${plan.newVersion}.md`),\n plan.notes,\n \"utf-8\",\n );\n\n const persistedPlan: PersistedReleasePlan = {\n schema: 1,\n strategy: \"github-flow\",\n currentVersion: plan.currentVersion,\n newVersion: plan.newVersion,\n suggestedBump: plan.suggestedBump,\n changelog: plan.changelog,\n notes: plan.notes,\n commits: plan.commits,\n preparedAt: new Date().toISOString(),\n baseCommit: await git.headSha(cwd),\n targetBranch: await git.getBranch(cwd),\n releaseBranchCreated: false,\n tokens: plan.tokens,\n tokensAvailable: plan.tokensAvailable,\n };\n\n await ensureGitignored(cwd, RELEASE_PLAN_REL_PATH);\n await ensureGitignored(cwd, RELEASE_NOTES_GLOB_REL_PATH);\n await saveReleasePlan(cwd, persistedPlan);\n\n await finishRelease({ cwd, tagAndPush, createGhRelease, workspacePropagation, signTags });\n}\n\n/**\n * Build the `FINISH_PUSH_FAILED` error thrown by step 9 of {@link finishRelease}.\n * Called after the plan file (step 6) and, for github-flow, the release commit\n * (step 5) already exist — so the message spells out reconciliation via fetch\n * + merge rather than pointing back at \"gw release prepare\"/\"gw release finish\",\n * neither of which can recover this state.\n */\nfunction finishPushFailure(opts: {\n stage: \"tag\" | \"push-main\" | \"push-develop\";\n tag: string;\n mainBranch: string;\n developBranch?: string;\n newVersion: string;\n err: unknown;\n}): GitwiseError {\n const { stage, tag, mainBranch, developBranch, newVersion, err } = opts;\n const cause = err instanceof Error ? err.message : String(err);\n const action =\n stage === \"tag\"\n ? `create the tag \"${tag}\"`\n : stage === \"push-main\"\n ? `push \"${mainBranch}\" (with tags) to origin`\n : `push \"${developBranch}\" to origin`;\n const recoverySteps =\n stage === \"tag\"\n ? [\n `git tag -a ${tag} -F .gitwise/release-${newVersion}.md`,\n `git push origin ${mainBranch} --follow-tags`,\n ]\n : [\n `git ls-remote --tags origin ${tag} # check whether the tag already reached origin`,\n `git fetch origin`,\n `git merge origin/${mainBranch} # do NOT rebase — that changes the hash ${tag} points to`,\n `git push origin ${mainBranch} --follow-tags`,\n ];\n return new GitwiseError({\n code: \"FINISH_PUSH_FAILED\",\n message: `Failed to ${action} while finishing v${newVersion}: ${cause}. The release plan file has already been deleted, so \"gw release finish\" cannot be re-run, and the release commit already exists locally, so \"gw release prepare\" will refuse with NO_COMMITS. Recover manually:\\n${recoverySteps.map((s) => ` ${s}`).join(\"\\n\")}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: { stage, tag, mainBranch, developBranch, newVersion },\n });\n}\n\n// ─── finishRelease ───────────────────────────────────────────────────────────\n\nexport interface FinishReleaseOptions {\n cwd: string;\n /** Tag locally and push (with `--follow-tags`); default true. */\n tagAndPush?: boolean;\n /** Invoke `gh release create` after the tag is pushed; default true. */\n createGhRelease?: boolean;\n /** Delete the local release branch after gitflow merges; default true. Ignored for github-flow. */\n deleteReleaseBranch?: boolean;\n /**\n * Propagate the new root version into every workspace package's\n * `package.json` (and sibling `plugin.json`) before the github-flow release\n * commit, then stage exactly those manifests alongside the root files so\n * they all land in the same commit. Workspace layout is read from\n * `package.json.workspaces` (array or yarn-style `{ packages: [...] }`);\n * falls back to `packages/*` when the field is missing. Default false.\n * Ignored for gitflow because prepare already committed manifests on the\n * release branch.\n */\n workspacePropagation?: boolean;\n /**\n * Sign the release tag with the local GPG key (`git tag -s`). Default true.\n * Set to false only for testing or environments without a GPG key — a\n * warning is emitted to stderr when signing is skipped.\n */\n signTags?: boolean;\n}\n\n/**\n * Consume a persisted release plan and finalize the release (ADR-001 / ADR-003).\n *\n * Lifecycle: load plan → validate against live repo state → reload notes from\n * `.gitwise/release-<version>.md` → on github-flow, bump `package.json` and\n * prepend the CHANGELOG entry then commit on the current branch → delete the\n * plan file (BEFORE any irreversible operation — merges, tags, pushes — so a\n * downstream failure cannot trigger a second `finish`; on gitflow this is\n * effectively the same as deleting first because the github-flow block is\n * skipped) → merge `plan.targetBranch` into every `strategy.mergeTargets`\n * entry that isn't `targetBranch` itself → annotate the tag with the reloaded\n * notes, push with `--follow-tags`, and on gitflow also push the develop\n * branch → optionally create the GitHub release (graceful: failure logs but\n * does not roll back) → on gitflow, delete the now fully-merged release\n * branch unless `deleteReleaseBranch === false`.\n *\n * Throws typed errors before mutating anything: `NO_RELEASE_PLAN`,\n * `STALE_PLAN_TAG_EXISTS`, `STALE_PLAN_BRANCH_MISMATCH`, `WORKING_TREE_DIRTY`,\n * `STRATEGY_DEVELOP_MISSING`, plus `INVALID_PLAN_SCHEMA` / `INVALID_PLAN_JSON`\n * surfaced by `loadReleasePlan`. On the github-flow path, a pre-commit hook\n * failure during step 5's release commit surfaces as `COMMIT_HOOK_FAILURE`\n * with the plan file STILL on disk — recover by resolving the hook issue\n * and running `git reset --hard HEAD` to clear the partial manifest/CHANGELOG\n * writes before re-running `gw release finish`, or run `gw release abort` to\n * discard the in-flight release. Once the plan file is deleted at step 6, a\n * failed strategy merge (typically gitflow's develop merge when develop has\n * advanced) surfaces as `FINISH_MERGE_CONFLICT` — the repo is left mid-merge\n * for manual recovery (`git merge --continue` then tag + push by hand) since\n * the plan can no longer be re-run. A failure in step 9 (tag creation, or a\n * rejected push — typically a non-fast-forward because origin's mainBranch\n * advanced, e.g. a CI bot commit, while this release was being prepared or\n * finished) surfaces as `FINISH_PUSH_FAILED` with the exact fetch/merge/push\n * recovery commands embedded in the message; merge (never rebase) is required\n * because the tag, if already created, is pinned to the release commit's hash.\n */\nexport async function finishRelease(opts: FinishReleaseOptions): Promise<void> {\n const {\n cwd,\n tagAndPush = true,\n createGhRelease = true,\n deleteReleaseBranch = true,\n workspacePropagation = false,\n signTags = true,\n } = opts;\n\n if (signTags === false) {\n process.stderr.write(\n \"[gitwise] WARNING: --no-sign / signTags:false is a testing-only escape hatch. Release tags will NOT be GPG-signed. Do not use in production releases.\\n\",\n );\n }\n\n // 1. Load the persisted plan (also raises INVALID_PLAN_SCHEMA / INVALID_PLAN_JSON).\n const plan = await loadReleasePlan(cwd);\n if (!plan) {\n throw new GitwiseError({\n code: \"NO_RELEASE_PLAN\",\n message: `No release plan found at .gitwise/release-plan.json. Run \"gw release prepare\" first.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n debug(\"release.finish.start\", {\n strategy: plan.strategy,\n newVersion: plan.newVersion,\n targetBranch: plan.targetBranch,\n });\n\n const strategy = createReleaseStrategy(plan.strategy);\n const tag = `v${plan.newVersion}`;\n\n // 2. Validate the plan against live repo state. All checks run before any\n // mutation so a stale-plan rejection leaves the file in place for `abort`.\n\n // 2a. Tag must not already exist (checked unconditionally — the plan is\n // stale even if the user opted out of pushing).\n if (await git.tagExists(cwd, tag)) {\n debug(\"release.finish.validate.failed\", {\n code: \"STALE_PLAN_TAG_EXISTS\",\n tag,\n });\n throw new GitwiseError({\n code: \"STALE_PLAN_TAG_EXISTS\",\n message: `Tag ${tag} already exists — the saved plan is stale. Run \"gw release abort\" or delete the tag before retrying.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // 2b. Current branch must match the plan's target branch.\n const currentBranch = await git.getBranch(cwd);\n if (currentBranch !== plan.targetBranch) {\n debug(\"release.finish.validate.failed\", {\n code: \"STALE_PLAN_BRANCH_MISMATCH\",\n expected: plan.targetBranch,\n actual: currentBranch,\n });\n throw new GitwiseError({\n code: \"STALE_PLAN_BRANCH_MISMATCH\",\n message: `Release plan targets \"${plan.targetBranch}\" but the current branch is \"${currentBranch}\". Check out the target branch before running finish.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n // 2c. Working tree must be clean of user changes. Filter out paths prepare\n // legitimately leaves dirty: the notes file (user is meant to edit it), the\n // plan file itself (gitignored after the first prepare but still surfaces as\n // untracked the very first time), and the .gitwise/ directory entry (git\n // collapses fully-untracked dirs).\n const expectedDirtyPaths = new Set<string>([\n \".gitwise/\",\n \".gitwise/release-plan.json\",\n `.gitwise/release-${plan.newVersion}.md`,\n ]);\n // `.gitignore` is conditionally tolerated. Prepare's `ensureGitignored`\n // mutates it on github-flow (the change is deferred to step 6 here because\n // prepare cannot commit on a trunk-based flow). Any *other* user edit to\n // `.gitignore` between prepare and finish would otherwise ride silently\n // into the release commit. Predict the exact bytes `ensureGitignored`\n // would have written from HEAD's `.gitignore` and only tolerate the dirty\n // entry when the working-tree file matches that prediction byte-for-byte\n // — mismatches fall through to WORKING_TREE_DIRTY so the surprise surfaces.\n if (await gitignoreMatchesPrepareOutput(cwd)) {\n expectedDirtyPaths.add(\".gitignore\");\n }\n const dirtyEntries = (await git.status(cwd))\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+$/, \"\"))\n .filter((line) => line.length >= 3)\n .filter((line) => !expectedDirtyPaths.has(line.slice(3).trim()));\n if (dirtyEntries.length > 0) {\n debug(\"release.finish.validate.failed\", {\n code: \"WORKING_TREE_DIRTY\",\n });\n throw new GitwiseError({\n code: \"WORKING_TREE_DIRTY\",\n message: `Working tree must be clean before finishing a release — commit or stash first.\\n${dirtyEntries.join(\"\\n\")}`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n\n // 2d. Gitflow requires a develop branch to merge into and push.\n const repoConfig = await readRepoConfig(cwd);\n const developBranch = repoConfig?.developBranch ?? \"develop\";\n if (strategy.requiresDevelop()) {\n if (!(await git.branchExists(cwd, developBranch))) {\n debug(\"release.finish.validate.failed\", {\n code: \"STRATEGY_DEVELOP_MISSING\",\n developBranch,\n });\n throw new GitwiseError({\n code: \"STRATEGY_DEVELOP_MISSING\",\n message: `GitFlow requires a \"${developBranch}\" branch but it does not exist.`,\n exitCode: EXIT_CODES.REPO_STATE_INVALID,\n });\n }\n }\n\n // 3. Resolve the main branch. For github-flow the plan's targetBranch IS\n // main; for gitflow we auto-detect it via the same helper used elsewhere.\n const mainBranch = strategy.requiresDevelop()\n ? await git.detectBaseBranch(cwd)\n : plan.targetBranch;\n\n // 4. Reload notes from disk so any user edits between prepare and finish\n // make it into the tag annotation and gh release body. If the file is\n // missing (user deleted it, moved it out for editing, CI cleaned `.gitwise`,\n // …), fall back to the in-memory notes captured at prepare time so the tag\n // is still annotated with the LLM output rather than blowing up with a raw\n // ENOENT. Other read failures (permissions, I/O) surface as a typed\n // NOTES_READ_FAILED so `formatReleaseError` can show an actionable hint.\n const notesPath = join(cwd, \".gitwise\", `release-${plan.newVersion}.md`);\n let notes: string;\n try {\n notes = await readFile(notesPath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n debug(\"release.finish.notes.missing\", { path: notesPath });\n notes = plan.notes;\n } else {\n const cause = err instanceof Error ? err.message : String(err);\n debug(\"release.finish.notes.read.failed\", { path: notesPath, error: cause });\n throw new GitwiseError({\n code: \"NOTES_READ_FAILED\",\n message: `Failed to read release notes at ${notesPath}: ${cause}. Recreate the file from the plan or run \"gw release abort\" to discard the in-flight release.`,\n cause: err,\n });\n }\n }\n\n // 5. For github-flow, prepare did NOT mutate package.json / CHANGELOG.md;\n // do those writes now on the current branch and commit. The plan file is\n // intentionally NOT deleted yet: the release commit is local and reversible\n // (`git reset --hard HEAD`), so if a pre-commit hook rejects the commit\n // (`COMMIT_HOOK_FAILURE`) or any write here fails mid-way, the user can\n // recover by clearing the partial state and re-running `gw release finish`,\n // or by running `gw release abort`. ADR-003's \"plan gone before any\n // irreversible op\" invariant is still honored — the plan delete moves to\n // step 6, before merges/tags/pushes.\n if (!plan.releaseBranchCreated) {\n const pkgPath = join(cwd, \"package.json\");\n const pkg = await readJSON<Record<string, unknown>>(pkgPath);\n pkg[\"version\"] = plan.newVersion;\n await writeJSON(pkgPath, pkg);\n\n // Workspace propagation runs after the root bump and before the commit so\n // every package + sibling plugin.json lands in the same release commit.\n // The helper returns the exact list of manifests it touched so we can\n // stage them explicitly below — no `git add packages` sweep.\n let propagatedManifests: string[] = [];\n if (workspacePropagation) {\n propagatedManifests = await propagateVersionToWorkspaces(cwd, plan.newVersion);\n }\n\n const changelogPath = join(cwd, \"CHANGELOG.md\");\n const date = new Date().toISOString().split(\"T\")[0];\n const versionHeader = `## [${plan.newVersion}] - ${date}\\n\\n${plan.changelog}\\n\\n`;\n\n if (await fileExists(changelogPath)) {\n const existing = await readFile(changelogPath, \"utf-8\");\n const headerEnd = existing.indexOf(\"## [\");\n if (headerEnd > 0) {\n await writeFile(\n changelogPath,\n existing.slice(0, headerEnd) + versionHeader + existing.slice(headerEnd),\n \"utf-8\",\n );\n } else {\n const body = existing.startsWith(CHANGELOG_HEADER)\n ? existing.slice(CHANGELOG_HEADER.length)\n : existing;\n await writeFile(\n changelogPath,\n CHANGELOG_HEADER + versionHeader + body,\n \"utf-8\",\n );\n }\n } else {\n await writeFile(changelogPath, CHANGELOG_HEADER + versionHeader, \"utf-8\");\n }\n\n const stagePaths = [\"package.json\", \"CHANGELOG.md\"];\n // prepare's ensureGitignored leaves .gitignore dirty on github-flow (no\n // commit happens there). Fold it into the release commit here so the\n // working tree is clean afterward — otherwise the next prepare trips\n // WORKING_TREE_DIRTY on a leftover ` M .gitignore`.\n if (await fileExists(join(cwd, \".gitignore\"))) {\n stagePaths.push(\".gitignore\");\n }\n // Stage the exact manifests propagation modified — never a broad\n // `git add <workspace-root>`, which would also pick up unrelated\n // untracked work in the same directory.\n stagePaths.push(...propagatedManifests);\n // Route through `applyCommit` (not raw `git.commit`) so a pre-commit hook\n // rejection surfaces as a typed `COMMIT_HOOK_FAILURE` — `formatReleaseError`\n // maps that to a recovery hint instead of the generic `UNKNOWN_HINT`.\n await git.applyCommit({\n message: `chore(release): v${plan.newVersion}`,\n files: stagePaths,\n cwd,\n });\n }\n\n // 6. Delete the plan file (ADR-003 invariant) BEFORE any irreversible\n // operation — merges, tags, pushes, gh release. Past this point a downstream\n // failure cannot trigger a second `finish` against this plan. For gitflow\n // (`releaseBranchCreated === true`) step 5 above is a no-op, so the delete\n // here lands in the same spot as the pre-fix ordering. For github-flow it\n // lands AFTER the now-successful release commit, shrinking the partial-\n // mutation window so a pre-commit hook failure leaves the plan recoverable.\n await deleteReleasePlan(cwd);\n\n // 7. Merge into each strategy target. Skip self-merges (github-flow's only\n // target is `plan.targetBranch` itself; nothing to merge there). A merge\n // failure here surfaces as a typed FINISH_MERGE_CONFLICT so the CLI can show\n // an actionable recovery hint. The plan file is already gone at this point\n // (step 6, ADR-003), so the repo is intentionally left mid-merge for the\n // user to resolve with `git merge --continue` and then tag + push manually.\n const mergeTargets = strategy.mergeTargets(mainBranch, developBranch);\n for (const target of mergeTargets) {\n if (target === plan.targetBranch) continue;\n await git.checkout(cwd, target);\n try {\n await git.mergeNoFf(cwd, plan.targetBranch);\n } catch (err) {\n const cause = err instanceof Error ? err.message : String(err);\n debug(\"release.finish.merge.failed\", {\n target,\n source: plan.targetBranch,\n error: cause,\n });\n throw new GitwiseError({\n code: \"FINISH_MERGE_CONFLICT\",\n message: `Failed to merge \"${plan.targetBranch}\" into \"${target}\" while finishing v${plan.newVersion}. The release plan file has already been deleted, so finish cannot be re-run. Resolve the conflicts, run \"git merge --continue\", then tag and push manually: git tag -a v${plan.newVersion} -F .gitwise/release-${plan.newVersion}.md && git push --follow-tags origin ${mainBranch}.\\n${cause}`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n details: {\n target,\n source: plan.targetBranch,\n newVersion: plan.newVersion,\n },\n });\n }\n debug(\"release.finish.merge.target\", {\n target,\n source: plan.targetBranch,\n });\n }\n\n // 8. Move to the main branch so the tag lives on the release commit there.\n // For github-flow we're already there (we never left). For gitflow we end\n // the merge loop on the last target — usually develop — and need to swap.\n if ((await git.getBranch(cwd)) !== mainBranch) {\n await git.checkout(cwd, mainBranch);\n }\n\n // 9. Tag and push. Tag annotation = the (possibly edited) notes. The plan\n // file is already gone (step 6), so any failure here — most commonly a\n // rejected non-fast-forward push because origin/mainBranch advanced while\n // this release was being prepared/finished (e.g. a CI bot commit) — leaves\n // \"gw release finish\" unable to re-run. Wrap each git call so the thrown\n // FINISH_PUSH_FAILED error spells out the exact manual recovery: the\n // release commit (and possibly the tag) already exist locally, so the fix\n // is to fetch + merge (never rebase, which would change the hash the tag\n // points to) and push again — not to re-run prepare or recreate the commit.\n if (tagAndPush) {\n try {\n await git.createTag(cwd, tag, notes, { signed: signTags !== false });\n } catch (err) {\n throw finishPushFailure({ stage: \"tag\", tag, mainBranch, newVersion: plan.newVersion, err });\n }\n try {\n await git.pushWithTags(cwd, \"origin\", mainBranch);\n } catch (err) {\n throw finishPushFailure({ stage: \"push-main\", tag, mainBranch, newVersion: plan.newVersion, err });\n }\n debug(\"release.finish.tag.pushed\", {\n tag,\n branch: mainBranch,\n remote: \"origin\",\n });\n if (strategy.requiresDevelop()) {\n try {\n await git.push(cwd, \"origin\", developBranch);\n } catch (err) {\n throw finishPushFailure({ stage: \"push-develop\", tag, mainBranch, developBranch, newVersion: plan.newVersion, err });\n }\n }\n }\n\n // 10. Optional GitHub release. Failure here is non-fatal: the tag is\n // already pushed and the user can `gh release create` manually.\n if (createGhRelease) {\n if (await isGhAvailable()) {\n try {\n await createGitHubRelease({\n tag,\n title: tag,\n body: notes,\n cwd,\n });\n } catch (err) {\n debug(\"release.finish.gh.failed\", {\n tag,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n } else {\n debug(\"gh not available, skipping GitHub release creation\");\n }\n }\n\n // 11. Gitflow only: delete the release branch. `-d` (safe delete) refuses\n // unless the branch is fully merged into HEAD — and by now it has been\n // merged into both mainBranch and developBranch, so this succeeds.\n if (plan.releaseBranchCreated && deleteReleaseBranch) {\n try {\n await git.deleteBranch(cwd, plan.targetBranch);\n } catch (err) {\n debug(\"release.finish.branch.delete.failed\", {\n branch: plan.targetBranch,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n}\n\n// ─── abortRelease ────────────────────────────────────────────────────────────\n\nexport interface AbortReleaseOptions {\n cwd: string;\n /** When true, also delete the release branch (gitflow only). Default false. */\n deleteBranch?: boolean;\n}\n\n/**\n * Discard an in-flight release (ADR-001 / ADR-003).\n *\n * Loads the persisted plan and removes it from disk. When `deleteBranch` is\n * true AND prepare created a release branch, verifies that branch is fully\n * merged into every strategy merge target (main, and develop for gitflow)\n * BEFORE deleting the plan. If the branch still has unmerged commits, throws\n * `RELEASE_BRANCH_UNMERGED` and leaves both the plan file and the branch in\n * place so the user can recover. Notes (`.gitwise/release-<v>.md`) are never\n * touched — the user may still want them.\n */\nexport async function abortRelease(opts: AbortReleaseOptions): Promise<void> {\n const { cwd, deleteBranch = false } = opts;\n\n debug(\"release.abort.start\", { cwd, deleteBranch });\n\n // 1. Load the plan; absent plan is the only fatal precondition.\n const plan = await loadReleasePlan(cwd);\n if (!plan) {\n throw new GitwiseError({\n code: \"NO_RELEASE_PLAN\",\n message: `No release plan found at .gitwise/release-plan.json. Nothing to abort.`,\n exitCode: EXIT_CODES.RELEASE_PLAN_STALE,\n });\n }\n\n const shouldDeleteBranch = deleteBranch && plan.releaseBranchCreated;\n\n // 2. Safety check FIRST — refuse upfront if the release branch has commits\n // not yet merged into every strategy target. This runs before the plan file\n // is deleted so the user can investigate or retry the abort.\n let mainBranch = \"\";\n if (shouldDeleteBranch) {\n const strategy = createReleaseStrategy(plan.strategy);\n const repoConfig = await readRepoConfig(cwd);\n const developBranch = repoConfig?.developBranch ?? \"develop\";\n mainBranch = strategy.requiresDevelop()\n ? await git.detectBaseBranch(cwd)\n : plan.targetBranch;\n\n for (const target of strategy.mergeTargets(mainBranch, developBranch)) {\n if (target === plan.targetBranch) continue;\n if (!(await git.isBranchMerged(cwd, plan.targetBranch, target))) {\n throw new GitwiseError({\n code: \"RELEASE_BRANCH_UNMERGED\",\n message: `Refusing to delete release branch \"${plan.targetBranch}\" — it has commits not present in \"${target}\". Merge or cherry-pick them first, or remove the branch manually.`,\n exitCode: EXIT_CODES.RELEASE_BRANCH_CONFLICT,\n });\n }\n }\n }\n\n // 3. Delete the plan file (idempotent: ENOENT is swallowed by the helper).\n await deleteReleasePlan(cwd);\n\n // 4. Optionally delete the release branch. `git branch -d` refuses to delete\n // the currently checked-out branch, so move to main first if we're still\n // sitting on the release branch (the usual state right after `prepare`).\n if (shouldDeleteBranch) {\n if ((await git.getBranch(cwd)) === plan.targetBranch) {\n await git.checkout(cwd, mainBranch);\n }\n await git.deleteBranch(cwd, plan.targetBranch);\n debug(\"release.abort.branch.deleted\", { branch: plan.targetBranch });\n }\n}\n\n// ─── runReleaseInProcess ─────────────────────────────────────────────────────\n\nexport interface RunReleaseInProcessOptions extends PrepareReleaseOptions {\n /**\n * Resolved with the persisted plan after `prepareRelease` writes it. Return\n * `false` (or have the promise reject with `p.isCancel`-style cancellation)\n * to abort: the helper calls {@link abortRelease} which removes the plan\n * file (and any gitflow release branch when `confirmAbortDeletesBranch` is\n * true). The on-disk notes file is always preserved.\n */\n confirm: (plan: PersistedReleasePlan) => Promise<boolean> | boolean;\n /** Forwarded to {@link finishRelease} when `confirm` returns true. */\n finishOptions?: Omit<FinishReleaseOptions, \"cwd\">;\n /**\n * When `confirm` returns false on a gitflow plan, also delete the release\n * branch that prepare created. Default false. Ignored for github-flow.\n *\n * Pass a callback to decide after the plan exists — useful for CLIs that\n * want to ask \"Also delete the release branch?\" only when a gitflow\n * release branch was actually created. The callback runs inside the abort\n * paths (post-confirm-false and confirm-threw); errors thrown from it are\n * treated as \"do not delete\" so the abort itself still completes.\n */\n confirmAbortDeletesBranch?:\n | boolean\n | ((plan: PersistedReleasePlan) => Promise<boolean> | boolean);\n}\n\n/**\n * Drive the two-phase release lifecycle inside a single process against the\n * same plan written to and read from `.gitwise/release-plan.json`.\n *\n * Runs {@link prepareRelease}, awaits the caller-supplied `confirm` callback\n * (which receives the persisted plan), and either calls {@link finishRelease}\n * (confirm true) or {@link abortRelease} (confirm false / throws). On\n * confirmed completion the plan file is deleted by `finishRelease`; on abort\n * it is removed by `abortRelease`. The on-disk notes file\n * (`.gitwise/release-<version>.md`) is preserved either way.\n *\n * This is the unified path used by both the legacy `applyRelease` adapter\n * (auto-confirms) and the upcoming `gw release` CLI root action (task_09).\n * Decoupling the prompt into a `confirm` callback keeps core free of CLI UI\n * dependencies (e.g. `@clack/prompts`).\n *\n * Returns the persisted plan when the release was applied, or `null` if the\n * caller declined via `confirm`.\n */\nexport async function runReleaseInProcess(\n opts: RunReleaseInProcessOptions,\n): Promise<PersistedReleasePlan | null> {\n const plan = await prepareRelease(opts);\n\n const resolveDeleteBranch = async (): Promise<boolean> => {\n const setting = opts.confirmAbortDeletesBranch;\n if (typeof setting !== \"function\") return setting ?? false;\n try {\n return (await setting(plan)) === true;\n } catch {\n // Never let a CLI-side prompt failure block the abort cleanup.\n return false;\n }\n };\n\n let confirmed: boolean;\n try {\n confirmed = await opts.confirm(plan);\n } catch (err) {\n await abortRelease({\n cwd: opts.cwd,\n deleteBranch: await resolveDeleteBranch(),\n });\n throw err;\n }\n\n if (!confirmed) {\n await abortRelease({\n cwd: opts.cwd,\n deleteBranch: await resolveDeleteBranch(),\n });\n return null;\n }\n\n await finishRelease({ cwd: opts.cwd, ...opts.finishOptions });\n return plan;\n}\n\n/**\n * Update the root `version` field of every workspace package's `package.json`\n * (and any sibling `plugin.json`) to match the new release version.\n *\n * The workspace layout is taken from `package.json.workspaces` so repos using\n * `apps/*` / `libs/*` / specific paths all work — not just the historical\n * `packages/*` convention. Both the npm/pnpm array form\n * (`workspaces: [\"apps/*\", \"libs/foo\"]`) and the legacy yarn object form\n * (`workspaces: { packages: [\"apps/*\"] }`) are supported. Missing or empty\n * workspaces falls back to `packages/*` so existing single-layout repos that\n * never declared the field keep working.\n *\n * Returns the cwd-relative paths of every manifest the function actually\n * modified so the caller can stage exactly those files (never a directory\n * sweep, which would also pick up unrelated untracked work).\n */\n/**\n * Predict whether the current `.gitignore` equals exactly what prepare's two\n * `ensureGitignored` calls would produce from HEAD's `.gitignore`. Used by\n * `finishRelease`'s working-tree check to tolerate prepare's expected\n * leftover while rejecting unrelated user edits that would otherwise ride\n * silently into the `chore(release): vX.Y.Z` commit.\n *\n * Returns true when the file on disk byte-matches the prediction (so the\n * caller can add `.gitignore` to its allow-list); false when it differs (so\n * the caller falls through to WORKING_TREE_DIRTY).\n *\n * Treats `.gitignore` missing from HEAD as an empty baseline (e.g. a brand-\n * new repo where prepare created the file). Treats `.gitignore` missing from\n * the working tree the same way and lets the equality check decide.\n */\nasync function gitignoreMatchesPrepareOutput(cwd: string): Promise<boolean> {\n const headContent = (await git.showFileAtHead(cwd, \".gitignore\")) ?? \"\";\n const gitignorePath = join(cwd, \".gitignore\");\n const currentContent = (await fileExists(gitignorePath))\n ? await readFile(gitignorePath, \"utf-8\")\n : \"\";\n let expected = applyGitignoreEntry(headContent, RELEASE_PLAN_REL_PATH);\n expected = applyGitignoreEntry(expected, RELEASE_NOTES_GLOB_REL_PATH);\n return currentContent === expected;\n}\n\n/**\n * Step factory for atomically bumping the `version` field of a single\n * manifest (package.json or sibling plugin.json) under a {@link Transaction}.\n *\n * `apply` reads the manifest's prior bytes (Buffer, not parsed JSON, so any\n * trailing newline or formatting in the on-disk file is preserved verbatim\n * for rollback), rewrites the `version` field in place via `writeJSON`, and\n * returns the captured prior bytes as the step result. `compensate` writes\n * those bytes back, restoring the file byte-for-byte regardless of what the\n * apply path produced. Steps are intended to run sequentially so ordering is\n * deterministic per ADR-004.\n */\nconst CROSS_WORKSPACE_DEPENDENCY_FIELDS = [\n \"dependencies\",\n \"devDependencies\",\n \"optionalDependencies\",\n \"peerDependencies\",\n] as const;\n\n/**\n * Rewrite any dependency entry in `parsed` whose key names another workspace\n * package, pointing it at `newVersion`. Preserves the existing `^`/`~` range\n * prefix (or leaves it exact if the prior spec had none) so a sibling bump\n * doesn't silently loosen or tighten the caller's intended range strictness.\n *\n * Without this, `writeWorkspaceVersionStep` only bumps a package's own\n * `version` field — a package's `dependencies` entry on another workspace\n * package (e.g. the CLI's pin on gitwise-core) is left stale, drifting away\n * from the sibling's real version every release until `npm ci` fails with\n * ETARGET because the stale pinned version was never published.\n */\nfunction updateCrossWorkspaceDependencies(\n parsed: Record<string, unknown>,\n workspaceNames: ReadonlySet<string>,\n newVersion: string,\n): void {\n for (const field of CROSS_WORKSPACE_DEPENDENCY_FIELDS) {\n const deps = parsed[field];\n if (!deps || typeof deps !== \"object\") continue;\n const depsRecord = deps as Record<string, unknown>;\n for (const depName of Object.keys(depsRecord)) {\n const spec = depsRecord[depName];\n if (!workspaceNames.has(depName) || typeof spec !== \"string\") continue;\n const prefix = spec.startsWith(\"^\") || spec.startsWith(\"~\") ? spec[0] : \"\";\n depsRecord[depName] = `${prefix}${newVersion}`;\n }\n }\n}\n\nexport function writeWorkspaceVersionStep(\n manifestPath: string,\n newVersion: string,\n workspaceNames?: ReadonlySet<string>,\n): Step<Buffer> {\n return {\n name: `write-version:${manifestPath}`,\n apply: async () => {\n const priorBytes = await readFile(manifestPath);\n const parsed = JSON.parse(priorBytes.toString(\"utf-8\")) as Record<\n string,\n unknown\n >;\n parsed[\"version\"] = newVersion;\n if (workspaceNames && workspaceNames.size > 0) {\n updateCrossWorkspaceDependencies(parsed, workspaceNames, newVersion);\n }\n await writeJSON(manifestPath, parsed);\n return priorBytes;\n },\n compensate: async (priorBytes) => {\n await writeFile(manifestPath, priorBytes);\n },\n };\n}\n\nconst txLogger: Logger = {\n warn(message, context) {\n logWarn(`[gitwise] ${message}`, context);\n },\n};\n\n/**\n * Propagate `version` into every workspace manifest under a {@link Transaction}\n * so that a write failure on `packages[N]/package.json` reliably restores the\n * bytes of every previously-written manifest (ADR-004 §Decision item 2).\n *\n * Acquires `.gitwise/.lock` for the duration of the flow and releases it in a\n * `finally` block so a concurrent gitwise invocation fails fast with\n * `REPO_LOCKED`. Writes are sequential (not concurrent) to keep ordering\n * deterministic. On any apply failure, runs `Transaction.rollback` BEFORE\n * propagating the error so callers always see the original cause; a partial\n * rollback (compensate itself fails) surfaces as a single `ROLLBACK_PARTIAL`\n * warning emitted by `Transaction.rollback`.\n *\n * Returns the cwd-relative paths of every manifest the function actually\n * modified so the caller can stage exactly those files (never a directory\n * sweep, which would also pick up unrelated untracked work).\n */\nexport async function propagateVersionToWorkspaces(\n cwd: string,\n version: string,\n): Promise<string[]> {\n const releaseLock = await acquireRepoLock(cwd, {\n command: \"release propagate-version\",\n });\n\n try {\n const tx = new Transaction();\n try {\n return await runWorkspaceVersionStepsInto(tx, cwd, version);\n } catch (err) {\n const reason =\n err instanceof GitwiseError\n ? err\n : new GitwiseError({\n code: \"WORKSPACE_VERSION_WRITE_FAILED\",\n message: `Failed to propagate version ${version} to workspaces: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.GIT_FAILED,\n cause: err,\n });\n await tx.rollback(reason, txLogger);\n throw reason;\n }\n } finally {\n await releaseLock();\n }\n}\n\n/**\n * Inner variant of {@link propagateVersionToWorkspaces} that runs the\n * workspace version-bump steps inside a caller-provided {@link Transaction}.\n *\n * Use this when a larger flow (e.g. {@link prepareRelease}) already holds the\n * repo lock and owns its own transaction — calling\n * {@link propagateVersionToWorkspaces} from inside such a flow would\n * deadlock on `acquireRepoLock` (same-pid is treated as alive).\n *\n * Sorts workspace directories alphabetically so iteration order is\n * deterministic across filesystems — ADR-004 §Decision requires sequential\n * writes \"so ordering is deterministic.\" Without this, `readdir` order leaks\n * platform-specific behavior into both the rollback boundary AND the list\n * returned to callers (which drives `git add` order in the release commit).\n *\n * Returns the cwd-relative paths of every manifest the function modified so\n * the caller can stage exactly those files.\n */\nexport async function runWorkspaceVersionStepsInto(\n tx: Transaction,\n cwd: string,\n version: string,\n): Promise<string[]> {\n const patterns = await readWorkspacePatterns(cwd);\n const workspaceDirs = (await expandWorkspacePatterns(cwd, patterns)).sort();\n // Gather every workspace package's name up front (before any writes) so\n // sibling dependency pins can be identified and rewritten alongside each\n // package's own version bump.\n const workspaceNames = new Set<string>();\n for (const dir of workspaceDirs) {\n const pkgPath = join(dir, \"package.json\");\n if (!(await fileExists(pkgPath))) continue;\n const parsed = await readJSON<{ name?: unknown }>(pkgPath);\n if (typeof parsed.name === \"string\") workspaceNames.add(parsed.name);\n }\n const modified: string[] = [];\n for (const dir of workspaceDirs) {\n const pkgPath = join(dir, \"package.json\");\n if (await fileExists(pkgPath)) {\n await tx.run(writeWorkspaceVersionStep(pkgPath, version, workspaceNames));\n modified.push(relative(cwd, pkgPath));\n }\n // Keep the Claude Code plugin manifest in lockstep with package.json so\n // its surfaced version doesn't drift after release. The spec-conformant\n // location is `.claude-plugin/plugin.json`; a top-level `plugin.json` is\n // checked too for repos still on the legacy layout.\n for (const pluginPath of [\n join(dir, \".claude-plugin\", \"plugin.json\"),\n join(dir, \"plugin.json\"),\n ]) {\n if (await fileExists(pluginPath)) {\n await tx.run(writeWorkspaceVersionStep(pluginPath, version));\n modified.push(relative(cwd, pluginPath));\n }\n }\n }\n return modified;\n}\n\n/**\n * Detect whether `cwd` is the root of an npm/pnpm/yarn workspaces monorepo\n * (or otherwise uses a `packages/*` layout with at least one nested\n * `package.json`). Single source of truth for the CLI and the skills runner\n * when auto-defaulting `workspacePropagation` per ADR-005.\n *\n * Returns `true` exactly when {@link propagateVersionToWorkspaces} would have\n * at least one manifest to rewrite — i.e. some workspace pattern in the root\n * `package.json` (array form, yarn-object `{ packages: [...] }` form, or the\n * `packages/*` fallback) resolves to a directory containing a `package.json`.\n */\nexport async function detectWorkspaceRoot(cwd: string): Promise<boolean> {\n const patterns = await readWorkspacePatterns(cwd);\n const dirs = await expandWorkspacePatterns(cwd, patterns);\n for (const dir of dirs) {\n if (await fileExists(join(dir, \"package.json\"))) return true;\n }\n return false;\n}\n\nasync function readWorkspacePatterns(cwd: string): Promise<string[]> {\n const pkgPath = join(cwd, \"package.json\");\n if (!(await fileExists(pkgPath))) return [\"packages/*\"];\n let parsed: { workspaces?: unknown };\n try {\n parsed = await readJSON<{ workspaces?: unknown }>(pkgPath);\n } catch {\n return [\"packages/*\"];\n }\n const ws = parsed.workspaces;\n const fromArray = Array.isArray(ws)\n ? ws.filter((p): p is string => typeof p === \"string\" && p.length > 0)\n : [];\n if (fromArray.length > 0) return fromArray;\n if (ws && typeof ws === \"object\" && !Array.isArray(ws)) {\n const inner = (ws as { packages?: unknown }).packages;\n if (Array.isArray(inner)) {\n const fromObject = inner.filter(\n (p): p is string => typeof p === \"string\" && p.length > 0,\n );\n if (fromObject.length > 0) return fromObject;\n }\n }\n return [\"packages/*\"];\n}\n\nasync function expandWorkspacePatterns(\n cwd: string,\n patterns: string[],\n): Promise<string[]> {\n const { readdir } = await import(\"node:fs/promises\");\n const readdirFn = readdir as unknown as ReaddirWithTypes;\n const matched = new Set<string>();\n for (const pattern of patterns) {\n // npm/yarn workspaces support `!`-prefixed negations to exclude paths.\n // Skipping them is a strict superset of the previous packages/* behavior\n // (which had no notion of exclusion at all) and is safe: we never delete,\n // we only bump versions inside matched directories.\n if (pattern.startsWith(\"!\")) continue;\n const segments = pattern.split(\"/\").filter((s) => s.length > 0);\n if (segments.length === 0) continue;\n await walkWorkspaceSegments(cwd, segments, 0, matched, readdirFn);\n }\n return Array.from(matched);\n}\n\ntype ReaddirWithTypes = (\n path: string,\n options: { withFileTypes: true },\n) => Promise<Array<{ name: string; isDirectory(): boolean }>>;\n\nasync function walkWorkspaceSegments(\n current: string,\n segments: string[],\n index: number,\n out: Set<string>,\n readdirFn: ReaddirWithTypes,\n): Promise<void> {\n if (index >= segments.length) {\n out.add(current);\n return;\n }\n const segment = segments[index] ?? \"\";\n if (!segment.includes(\"*\")) {\n await walkWorkspaceSegments(\n join(current, segment),\n segments,\n index + 1,\n out,\n readdirFn,\n );\n return;\n }\n let entries: Array<{ name: string; isDirectory(): boolean }>;\n try {\n entries = await readdirFn(current, { withFileTypes: true });\n } catch {\n return;\n }\n const regex = segmentToRegex(segment);\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (!regex.test(entry.name)) continue;\n await walkWorkspaceSegments(\n join(current, entry.name),\n segments,\n index + 1,\n out,\n readdirFn,\n );\n }\n}\n\nfunction segmentToRegex(segment: string): RegExp {\n // `*` matches any run of non-slash chars; `?` matches a single non-slash char.\n // Other regex metacharacters are escaped so a literal `.` in a workspace name\n // (e.g. `pkg.v2`) matches literally rather than as a wildcard.\n const escaped = segment\n .split(/(\\*|\\?)/)\n .map((part) => {\n if (part === \"*\") return \"[^/]*\";\n if (part === \"?\") return \"[^/]\";\n return part.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n })\n .join(\"\");\n return new RegExp(`^${escaped}$`);\n}\n","/**\n * Release-lifecycle strategy abstraction. Narrow on purpose (per ADR-002):\n * it covers branch creation, merge targets, and the develop-branch requirement\n * — nothing else from the broader FlowStrategy design lives here yet.\n */\n\nexport type ReleaseStrategyName = \"github-flow\" | \"gitflow\";\n\nexport interface ReleaseStrategy {\n readonly name: ReleaseStrategyName;\n /** Optional release branch to create during prepare; null = no branch. */\n releaseBranchFor(version: string): string | null;\n /** Branches to merge the release into during finish, in order. */\n mergeTargets(mainBranch: string, developBranch?: string): string[];\n /** True if a develop branch must exist for this strategy to run. */\n requiresDevelop(): boolean;\n}\n\nconst githubFlow: ReleaseStrategy = Object.freeze({\n name: \"github-flow\",\n releaseBranchFor(_version: string): string | null {\n return null;\n },\n mergeTargets(mainBranch: string, _developBranch?: string): string[] {\n return [mainBranch];\n },\n requiresDevelop(): boolean {\n return false;\n },\n});\n\nconst gitflow: ReleaseStrategy = Object.freeze({\n name: \"gitflow\",\n releaseBranchFor(version: string): string {\n return `release/${version}`;\n },\n mergeTargets(mainBranch: string, developBranch?: string): string[] {\n return developBranch ? [mainBranch, developBranch] : [mainBranch];\n },\n requiresDevelop(): boolean {\n return true;\n },\n});\n\nconst STRATEGIES: Readonly<Record<ReleaseStrategyName, ReleaseStrategy>> = Object.freeze({\n \"github-flow\": githubFlow,\n gitflow,\n});\n\nexport function createReleaseStrategy(name: ReleaseStrategyName): ReleaseStrategy {\n return STRATEGIES[name];\n}\n","import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileExists, writeJSON } from \"../infra/filesystem.js\";\nimport { info } from \"../infra/logger.js\";\nimport { EXIT_CODES, GitwiseError } from \"../errors.js\";\nimport type { ReleaseStrategyName } from \"../strategies/release.js\";\nimport type { BumpType } from \"./release.js\";\n\n/**\n * On-disk handoff between `gw release prepare` and `gw release finish`.\n * Lifecycle and validation rules are defined in ADR-003 — written last in\n * prepare, deleted first in finish; never edit by hand.\n */\nexport interface PersistedReleasePlan {\n schema: 1;\n strategy: ReleaseStrategyName;\n currentVersion: string;\n newVersion: string;\n suggestedBump: BumpType;\n changelog: string;\n notes: string;\n commits: string;\n preparedAt: string;\n baseCommit: string;\n targetBranch: string;\n releaseBranchCreated: boolean;\n tokens: { input: number; output: number };\n /**\n * AD-002: false when the active provider didn't report token usage.\n * Missing on a plan persisted before this field existed — the validator\n * (isPersistedReleasePlan) defaults an absent field to `true` (T15).\n */\n tokensAvailable: boolean;\n}\n\nconst PLAN_REL_PATH = \".gitwise/release-plan.json\";\n\nfunction planPath(cwd: string): string {\n return join(cwd, PLAN_REL_PATH);\n}\n\nexport async function saveReleasePlan(cwd: string, plan: PersistedReleasePlan): Promise<void> {\n await writeJSON(planPath(cwd), plan);\n}\n\nexport async function loadReleasePlan(cwd: string): Promise<PersistedReleasePlan | null> {\n const filePath = planPath(cwd);\n if (!(await fileExists(filePath))) return null;\n\n const raw = await readFile(filePath, \"utf-8\");\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_JSON\",\n message: `Release plan at ${filePath} is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n cause: err,\n });\n }\n\n const schema = (parsed as { schema?: unknown } | null)?.schema;\n if (schema !== 1) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_SCHEMA\",\n message: `Release plan schema ${String(schema)} is not supported by this gitwise binary (expected 1).`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n if (!isPersistedReleasePlan(parsed)) {\n throw new GitwiseError({\n code: \"INVALID_PLAN_SCHEMA\",\n message: `Release plan at ${filePath} is missing or has wrong-typed required fields for schema 1.`,\n exitCode: EXIT_CODES.CONFIG_INVALID,\n });\n }\n\n // AD-002 backward compatibility: a plan persisted before tokensAvailable\n // existed has no such key on disk at all — default it to true (real\n // providers reported real usage at the time) rather than leaving it\n // undefined on the returned, statically-typed-as-required value.\n return { ...parsed, tokensAvailable: (parsed as { tokensAvailable?: boolean }).tokensAvailable ?? true };\n}\n\nfunction isPersistedReleasePlan(value: unknown): value is PersistedReleasePlan {\n if (!value || typeof value !== \"object\") return false;\n const p = value as Record<string, unknown>;\n if (p.schema !== 1) return false;\n if (p.strategy !== \"gitflow\" && p.strategy !== \"github-flow\") return false;\n if (p.suggestedBump !== \"major\" && p.suggestedBump !== \"minor\" && p.suggestedBump !== \"patch\") {\n return false;\n }\n if (typeof p.currentVersion !== \"string\") return false;\n if (typeof p.newVersion !== \"string\") return false;\n if (typeof p.changelog !== \"string\") return false;\n if (typeof p.notes !== \"string\") return false;\n if (typeof p.commits !== \"string\") return false;\n if (typeof p.preparedAt !== \"string\") return false;\n if (typeof p.baseCommit !== \"string\") return false;\n if (typeof p.targetBranch !== \"string\") return false;\n if (typeof p.releaseBranchCreated !== \"boolean\") return false;\n if (!p.tokens || typeof p.tokens !== \"object\") return false;\n const tokens = p.tokens as Record<string, unknown>;\n if (typeof tokens.input !== \"number\" || !Number.isFinite(tokens.input)) return false;\n if (typeof tokens.output !== \"number\" || !Number.isFinite(tokens.output)) return false;\n // AD-002: absent (plan persisted before this field existed) is valid —\n // loadReleasePlan defaults it to true. Present-but-wrong-typed is not.\n if (p.tokensAvailable !== undefined && typeof p.tokensAvailable !== \"boolean\") return false;\n return true;\n}\n\nexport async function deleteReleasePlan(cwd: string): Promise<void> {\n try {\n await unlink(planPath(cwd));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw err;\n }\n}\n\n/**\n * Pure string transform behind {@link ensureGitignored}: returns the\n * `.gitignore` content that would result from ensuring `entry` is covered.\n * If `entry` is already covered by an exact-match line or a wildcard for its\n * directory (`dir/` or `dir/*`), `content` is returned unchanged. Exported so\n * callers outside the filesystem layer (e.g. `finishRelease`'s working-tree\n * validator) can predict the exact bytes `ensureGitignored` writes without\n * touching disk — keeping the writer and the validator in single-source-of-\n * truth alignment.\n */\nexport function applyGitignoreEntry(content: string, entry: string): string {\n if (isCovered(content, entry)) return content;\n const needsLeadingNewline = content.length > 0 && !content.endsWith(\"\\n\");\n return `${content}${needsLeadingNewline ? \"\\n\" : \"\"}${entry}\\n`;\n}\n\n/**\n * Ensure `entry` is covered by the repo's `.gitignore`. Coverage is detected\n * by an exact-match line OR a wildcard for the entry's directory (`dir/` or\n * `dir/*`). When appending, prints a one-line notice and preserves the file's\n * existing trailing-newline behavior.\n */\nexport async function ensureGitignored(cwd: string, entry: string): Promise<void> {\n const gitignorePath = join(cwd, \".gitignore\");\n const exists = await fileExists(gitignorePath);\n const original = exists ? await readFile(gitignorePath, \"utf-8\") : \"\";\n const next = applyGitignoreEntry(original, entry);\n if (next === original) return;\n await writeFile(gitignorePath, next, \"utf-8\");\n info(`Added ${entry} to .gitignore`);\n}\n\nfunction isCovered(content: string, entry: string): boolean {\n const candidates = new Set<string>([entry]);\n const dir = dirname(entry);\n if (dir && dir !== \".\" && dir !== \"/\") {\n candidates.add(`${dir}/`);\n candidates.add(`${dir}/*`);\n }\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith(\"#\")) continue;\n if (candidates.has(line)) return true;\n }\n return false;\n}\n","// PROV-07: shared by every command's token-count print site — the `gw` CLI\n// (commit, review, pr, release) and every native-surface skill script\n// (packages/skills/scripts/*) that shells out to the same four commands.\n// Shows the real counts when the active provider reports usage, \"n/a\" when\n// it doesn't (AD-002). Never a misleading \"0 in / 0 out\".\nexport function formatTokens(tokens: { input: number; output: number }, tokensAvailable: boolean): string {\n return tokensAvailable ? `${tokens.input} in / ${tokens.output} out` : \"n/a\";\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport { debug } from \"../infra/logger.js\";\nimport { GitwiseError } from \"../errors.js\";\nimport type { LLMChatRequest, LLMChatResponse, LLMProvider, ModelConfig, ModelTier } from \"./types.js\";\n\nconst DEFAULT_MAX_TOKENS = 4096;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 1000;\n\nexport class AnthropicProvider implements LLMProvider {\n private readonly client: Anthropic;\n private readonly models: ModelConfig;\n\n constructor(apiKey: string | undefined, models: ModelConfig) {\n this.client = new Anthropic({\n apiKey: apiKey ?? process.env[\"ANTHROPIC_API_KEY\"],\n timeout: DEFAULT_TIMEOUT_MS,\n });\n this.models = models;\n }\n\n async chat(req: LLMChatRequest): Promise<LLMChatResponse> {\n const modelId = this.resolveModel(req.tier);\n debug(\"Calling Anthropic API\", { model: modelId, tier: req.tier });\n return this.callWithRetry(req, modelId);\n }\n\n private async callWithRetry(\n req: LLMChatRequest,\n modelId: string,\n ): Promise<LLMChatResponse> {\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n return await this.callApi(req, modelId);\n } catch (err: unknown) {\n lastError = err instanceof Error ? err : new Error(String(err));\n if (this.isRetryable(err)) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n debug(\"Retrying after error\", { attempt, delay, error: lastError.message });\n await this.sleep(delay);\n continue;\n }\n throw lastError;\n }\n }\n throw new GitwiseError({\n code: \"API_RATE_LIMITED\",\n message: lastError?.message ?? \"Max retries exceeded\",\n cause: lastError,\n });\n }\n\n private async callApi(\n req: LLMChatRequest,\n modelId: string,\n ): Promise<LLMChatResponse> {\n const response = await this.client.messages.create({\n model: modelId,\n max_tokens: DEFAULT_MAX_TOKENS,\n system: req.systemPrompt,\n messages: [{ role: \"user\", content: req.userMessage }],\n });\n const text = response.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n return {\n content: text,\n tokens: {\n input: response.usage.input_tokens,\n output: response.usage.output_tokens,\n },\n tokensAvailable: true,\n };\n }\n\n private resolveModel(tier: ModelTier): string {\n return this.models[tier];\n }\n\n private isRetryable(err: unknown): boolean {\n if (err instanceof Anthropic.APIError) {\n return err.status === 429 || err.status === 529;\n }\n return false;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import { AnthropicProvider } from \"./anthropic.js\";\nimport { ClaudeCodeProvider } from \"./claude-code.js\";\nimport { CliSubprocessProvider } from \"./cli-subprocess.js\";\nimport { codexSpec } from \"./codex.js\";\nimport { copilotSpec } from \"./copilot.js\";\nimport { kiroSpec } from \"./kiro.js\";\nimport { GitwiseError } from \"../errors.js\";\nimport type { LLMProvider, ProviderConfig } from \"./types.js\";\nimport type { MergedConfig } from \"../config/types.js\";\n\nexport function createProvider(config: ProviderConfig): LLMProvider {\n switch (config.kind) {\n case \"claude-code\":\n return new ClaudeCodeProvider(config.models, config.claudeCliPath);\n case \"codex\":\n return new CliSubprocessProvider(codexSpec, config.models, config.codexCliPath);\n case \"copilot\":\n return new CliSubprocessProvider(copilotSpec, config.models, config.copilotCliPath);\n case \"kiro\":\n return new CliSubprocessProvider(kiroSpec, config.models, config.kiroCliPath);\n case \"api\":\n return new AnthropicProvider(config.apiKey, config.models);\n default: {\n const unhandled: never = config.kind;\n throw new GitwiseError({\n code: \"CONFIG_INVALID\",\n message: `Unknown provider \"${String(unhandled)}\" in config. Re-run \\`gw provider\\` to choose a supported provider.`,\n });\n }\n }\n}\n\n/**\n * The single place that turns a MergedConfig (+ optional API key) into the\n * ProviderConfig createProvider() expects — narrowing the per-provider\n * `models` map down to the active provider's tier block and threading every\n * per-tool CLI path field through. Replaces the 8 duplicated inline\n * `{ kind: config.provider, models: config.models, ... }` object literals\n * that predated the per-provider models map (MDL-03, MDL-04).\n */\nexport function buildProviderConfig(merged: MergedConfig, apiKey?: string): ProviderConfig {\n return {\n kind: merged.provider,\n models: merged.models[merged.provider],\n apiKey,\n claudeCliPath: merged.claudeCliPath,\n codexCliPath: merged.codexCliPath,\n copilotCliPath: merged.copilotCliPath,\n kiroCliPath: merged.kiroCliPath,\n };\n}\n","// Inline the version so bundled runner scripts don't need a package.json on disk.\n// (createRequire(\"../package.json\") would fail after git-clone with no node_modules.)\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nexport const version: string = packageJson.version;\n\nexport const __placeholder__ = Symbol.for(\"@denisvieiradev/gitwise-core#placeholder\");\n\n// Error exports\nexport { GitwiseError, EXIT_CODES, wrapError } from \"./errors.js\";\nexport type { GitwiseErrorArgs } from \"./errors.js\";\n\n// Infra exports\nexport * from \"./infra/logger.js\";\nexport * from \"./infra/filesystem.js\";\nexport { git } from \"./infra/index.js\";\nexport { github } from \"./infra/index.js\";\nexport { env } from \"./infra/index.js\";\nexport type { ChangedFile, ApplyCommitParams } from \"./infra/git.js\";\nexport { stashList } from \"./infra/git.js\";\nexport type { CreatePRParams, PRResult, UpdatePRParams, CreateReleaseParams } from \"./infra/github.js\";\nexport { Transaction } from \"./infra/transaction.js\";\nexport type {\n Step,\n Logger,\n RollbackFailure,\n RollbackResult,\n} from \"./infra/transaction.js\";\nexport { acquireRepoLock, STALE_LOCK_MS } from \"./infra/lockfile.js\";\nexport type { LockPayload, AcquireRepoLockOptions } from \"./infra/lockfile.js\";\n// Export the per-tool binary resolvers for CLI use (CFG-01:\n// detectAvailableProviders reuses these instead of re-implementing detection).\nexport { resolveClaudeBinary } from \"./providers/claude-code.js\";\nexport { resolveCodexBinary } from \"./providers/codex.js\";\nexport { resolveCopilotBinary } from \"./providers/copilot.js\";\nexport { resolveKiroBinary } from \"./providers/kiro.js\";\n\n// Config exports — note: ModelConfig here is the config-layer version\nexport type { UserConfig, RepoConfig, MergedConfig, Language, CommitConvention, ModelsByProvider } from \"./config/types.js\";\nexport type { ModelConfig as ConfigModelConfig } from \"./config/types.js\";\nexport { DEFAULT_USER_CONFIG } from \"./config/types.js\";\nexport { getMergedConfig, getApiKey } from \"./config/merge.js\";\nexport { readUserConfig, writeUserConfig, writeApiKey } from \"./config/user.js\";\nexport { readRepoConfig } from \"./config/repo.js\";\n\n// Template exports\nexport { loadTemplate, loadAndInterpolate, interpolate } from \"./template/index.js\";\nexport type { LoadTemplateOptions } from \"./template/index.js\";\n\n// Command exports\nexport {\n commit,\n applyCommitPlan,\n parseCommitResponse,\n takeNamedStashStep,\n applyOneCommitStep,\n} from \"./commands/commit.js\";\nexport { review } from \"./commands/review.js\";\nexport { pr, applyPr } from \"./commands/pr.js\";\nexport {\n release,\n prepareRelease,\n applyRelease,\n finishRelease,\n abortRelease,\n runReleaseInProcess,\n bumpVersion,\n heuristicBump,\n detectWorkspaceRoot,\n propagateVersionToWorkspaces,\n writeWorkspaceVersionStep,\n} from \"./commands/release.js\";\nexport type {\n ReleaseOptions,\n ReleasePlan,\n PrepareReleaseOptions,\n ApplyReleaseOptions,\n FinishReleaseOptions,\n AbortReleaseOptions,\n RunReleaseInProcessOptions,\n BumpType,\n} from \"./commands/release.js\";\nexport { createReleaseStrategy } from \"./strategies/release.js\";\nexport type { ReleaseStrategy, ReleaseStrategyName } from \"./strategies/release.js\";\nexport {\n saveReleasePlan,\n loadReleasePlan,\n deleteReleasePlan,\n ensureGitignored,\n} from \"./commands/release-plan.js\";\nexport type { PersistedReleasePlan } from \"./commands/release-plan.js\";\nexport type { PrOptions, PrDraft, ApplyPrOptions, ApplyPrResult } from \"./commands/pr.js\";\nexport type { ReviewOptions, ReviewResult, ReviewFinding } from \"./commands/review.js\";\nexport type { CommitOptions, CommitPlan, CommitEntry, SplitMode, ApplyCommitPlanOptions, CommitStepResult, CommitAlternatives } from \"./commands/commit.js\";\nexport { formatTokens } from \"./commands/token-format.js\";\n\n// Provider exports — ModelConfig here is the provider-layer version\nexport type { LLMProvider, LLMChatRequest, LLMChatResponse, ModelTier, ModelConfig, ProviderConfig, ProviderKind } from \"./providers/types.js\";\nexport { PROVIDER_KINDS } from \"./providers/types.js\";\nexport { createProvider, buildProviderConfig } from \"./providers/factory.js\";\nexport { resolveModelTier, SUPPORTED_COMMANDS } from \"./providers/model-router.js\";\n"],"mappings":"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","names":["path","execFile","promisify","exec","promisify","execFile","readFile","readFile","mkdir","open","readFile","unlink","mkdir","unlink","open","readFile","os","path","path","setTimeout","path","version","setTimeout","path","os","os","path","path","os","os","path","path","os","os","path","path","os","os","join","os","join","os","join","join","os","readFile","dirname","join","os","__dirname","dirname","join","os","readFile","commit","message","MAX_DIFF_CHARS","truncateDiff","execFile","promisify","exec","promisify","execFile","resolveBaseBranch","readFile","unlink","writeFile","join","version","readFile","unlink","writeFile","dirname","join","join","readFile","unlink","writeFile","dirname","join","readFile","writeFile","unlink","path","deleteBranch","version","DEFAULT_TIMEOUT_MS"]}
|