# KISS Framework API Reference

> **Auto-generated** — run `uv run generate-api-docs` to regenerate.

<details><summary><b>Table of Contents</b></summary>

```
- [`kiss.core.kiss_agent`](#kisscorekiss_agent)
- [`kiss.core.relentless_agent`](#kisscorerelentless_agent)
  - [`kiss.agents.sorcar.sorcar_agent`](#kissagentssorcarsorcar_agent)
  - [`kiss.agents.sorcar.chat_sorcar_agent`](#kissagentssorcarchat_sorcar_agent)
  - [`kiss.agents.sorcar.git_worktree`](#kissagentssorcargit_worktree)
  - [`kiss.agents.sorcar.worktree_sorcar_agent`](#kissagentssorcarworktree_sorcar_agent)
```

</details>

______________________________________________________________________

#### `kiss.core.kiss_agent` — *Core KISS agent implementation with native function calling support.*

##### `class KISSAgent(Base)` — A KISS agent using native function calling.

**Constructor:** `KISSAgent(name: str) -> None`

- **run** — Runs the agent's main ReAct loop to solve the task.<br/>`run(model_name: str, prompt_template: str, arguments: dict[str, str] | None = None, system_prompt: str = '', tools: list[Callable[..., Any]] | None = None, is_agentic: bool = True, max_steps: int | None = None, max_budget: float | None = None, model_config: dict[str, Any] | None = None, printer: Printer | None = None, verbose: bool | None = None, attachments: list[Attachment] | None = None) -> str`

  - `model_name`: The name of the model to use for the agent.
  - `prompt_template`: The prompt template for the agent.
  - `arguments`: The arguments to be substituted into the prompt template. Default is None.
  - `system_prompt`: Optional system prompt to provide to the model. Default is empty string (no system prompt).
  - `tools`: The tools to use for the agent. If None, no tools are provided (only the built-in finish tool is added).
  - `is_agentic`: Whether the agent is agentic. Default is True.
  - `max_steps`: The maximum number of steps to take. Default is 100.
  - `max_budget`: The maximum budget to spend. Default is 10.0.
  - `model_config`: The model configuration to use for the agent. Default is None.
  - `printer`: Optional printer for streaming output. Default is None.
  - `verbose`: Whether to print output to console. Default is None (verbose enabled).
  - `attachments`: Optional file attachments (images, PDFs) to include in the initial prompt. Default is None.
  - **Returns:** str: The result of the agent's task.

- **finish** — The agent must call this function with the final answer to the task.<br/>`finish(result: str) -> str`

  - `result`: The result generated by the agent.
  - **Returns:** Returns the result of the agent's task.

______________________________________________________________________

#### `kiss.core.relentless_agent` — *Base relentless agent with smart continuation for long tasks.*

##### `class RelentlessAgent(Base)` — Base agent with auto-continuation for long tasks.

- **perform_task** — Execute the task with auto-continuation across multiple sub-sessions.<br/>`perform_task(tools: list[Callable[..., Any]], attachments: list[Attachment] | None = None) -> str`

  - `tools`: List of callable tools available to the agent during execution.
  - `attachments`: Optional file attachments (images, PDFs) for the initial prompt.
  - **Returns:** YAML string with 'success' and 'summary' keys on successful completion.

- **run** — Run the agent with the provided tools.<br/>`run(model_name: str | None = None, prompt_template: str = '', arguments: dict[str, str] | None = None, system_prompt: str = '', max_steps: int | None = None, max_budget: float | None = None, model_config: dict[str, Any] | None = None, work_dir: str | None = None, printer: Printer | None = None, max_sub_sessions: int | None = None, docker_image: str | None = None, verbose: bool | None = None, tools: list[Callable[..., Any]] | None = None, attachments: list[Attachment] | None = None) -> str`

  - `model_name`: LLM model to use. Defaults to config value.
  - `prompt_template`: Task prompt template with format placeholders.
  - `arguments`: Dictionary of values to fill prompt_template placeholders.
  - `system_prompt`: System-level instructions passed to the underlying LLM via model_config. Defaults to empty string (no system instructions).
  - `max_steps`: Maximum steps per sub-session. Defaults to config value.
  - `max_budget`: Maximum budget in USD. Defaults to config value.
  - `model_config`: Optional dictionary of additional model configuration parameters (e.g. temperature, top_p). Defaults to None.
  - `work_dir`: Working directory for the agent. Defaults to artifact_dir/kiss_workdir.
  - `printer`: Printer instance for output display.
  - `max_sub_sessions`: Maximum continuation sub-sessions. Defaults to config value.
  - `docker_image`: Docker image name to run tools inside a container.
  - `verbose`: Whether to print output to console. Defaults to True.
  - `tools`: List of callable tools available to the agent during execution.
  - `attachments`: Optional file attachments (images, PDFs) for the initial prompt.
  - **Returns:** YAML string with 'success' and 'summary' keys.

**`finish`** — Finish execution with status and summary.<br/>`def finish(success: bool, is_continue: bool = False, summary: str = '') -> str`

- `success`: True if the agent has successfully completed the task, False otherwise
- `is_continue`: True if the task is incomplete and should continue, False otherwise
- `summary`: precise chronologically-ordered list of things the agent did with the reason for doing that along with relevant code snippets

______________________________________________________________________

#### `kiss.agents.sorcar.sorcar_agent` — *Sorcar agent with both coding tools and browser automation.*

##### `class SorcarAgent(RelentlessAgent)` — Agent with both coding tools and browser automation for web + code tasks.

**Constructor:** `SorcarAgent(name: str) -> None`

- **perform_task** — Execute the task, building docker-aware tools after docker_manager is set.<br/>`perform_task(tools: list, attachments: list | None = None) -> str`

  - `tools`: Extra tools passed by the caller (from run(tools=...)).
  - `attachments`: Optional file attachments for the initial prompt.
  - **Returns:** YAML string with 'success' and 'summary' keys.

- **run** — Run the assistant agent with coding tools and browser automation.<br/>`run(model_name: str | None = None, prompt_template: str = '', arguments: dict[str, str] | None = None, system_prompt: str | None = None, tools: list[Callable[..., Any]] | None = None, max_steps: int | None = None, max_budget: float | None = None, model_config: dict[str, Any] | None = None, work_dir: str | None = None, printer: Printer | None = None, max_sub_sessions: int | None = None, docker_image: str | None = None, web_tools: bool = True, is_parallel: bool = False, verbose: bool | None = None, current_editor_file: str | None = None, attachments: list[Attachment] | None = None, ask_user_question_callback: Callable[[str], str] | None = None) -> str`

  - `model_name`: LLM model to use. Defaults to config value.
  - `prompt_template`: Task prompt template with format placeholders.
  - `arguments`: Dictionary of values to fill prompt_template placeholders.
  - `system_prompt`: system prompt to be appended to the actual system prompt
  - `tools`: List of tools to be added in addition to bash and web tools.
  - `max_steps`: Maximum steps per sub-session. Defaults to config value.
  - `max_budget`: Maximum budget in USD. Defaults to config value.
  - `work_dir`: Working directory for the agent. Defaults to artifact_dir/kiss_workdir.
  - `printer`: Printer instance for output display.
  - `max_sub_sessions`: Maximum continuation sub-sessions. Defaults to config value.
  - `docker_image`: Docker image name to run tools inside a container.
  - `web_tools`: Whether to include browser/web tools. Defaults to True. Set to False for terminal-only environments.
  - `is_parallel`: Whether to include the run_parallel tool. Defaults to False. When True, the agent can spawn parallel sub-agents for independent tasks.
  - `verbose`: Whether to print output to console. Defaults to config verbose setting.
  - `current_editor_file`: Path to the currently active editor file, appended to prompt.
  - `attachments`: Optional file attachments (images, PDFs) for the initial prompt.
  - `ask_user_question_callback`: Optional callback used by the ask_user_question tool to collect a text response from the user.
  - **Returns:** YAML string with 'success' and 'summary' keys.

**`auto_commit_changes`** — Stage all changes, generate a commit message, and commit. Stages once so *message_fn* can compute the diff, runs *message_fn* (typically a slow LLM call) to generate the commit subject/body, then re-stages immediately before the commit so any file that appeared in the worktree during the LLM call (e.g. `PROGRESS.md` rewrites, macOS `.DS_Store` materializing after an `open` of the report, an editor side-channel saving swap files) is included in the same commit. Without the second `stage_all` those late-arriving files would be left uncommitted, `_finalize_worktree` would see them via `has_uncommitted_changes` and abort the auto-merge with the misleading "pre-commit hook may have rejected" warning (observed in production on 2026-06-26 07:23:14 for worktree `kiss_wt-1782483430-cb03445c` even though the repo had no custom pre-commit hooks installed). Falls back to a generic commit message when *message_fn* raises (e.g. the LLM-based generator is unavailable).<br/>`def auto_commit_changes(commit_dir: Path, user_prompt: str | None, message_fn: Callable[[Path, str | None], str], notify_fn: Callable[[str, str], None] | None = None) -> bool`

- `commit_dir`: Directory whose changes are staged and committed.
- `user_prompt`: The user's task prompt, woven into the commit message (or its fallback), or `None` when unavailable.
- `message_fn`: Callable producing a commit message from `(commit_dir, user_prompt)`.
- `notify_fn`: Optional UI callback invoked at two life-cycle points so the chat webview can render toasts: - `notify_fn("generating", "")` immediately before *message_fn* runs (typically a slow LLM call) so the user sees "Generating commit message" while the LLM works. - `notify_fn("committed", subject)` immediately after a successful commit, where *subject* is the first non-empty line of the committed message. Both hooks are SKIPPED when there is nothing to commit (no staged diff after the initial `stage_all`), so the webview never sees a misleading "Generating commit message" toast without a follow-up. The "committed" hook is also not invoked when `commit_staged` returns `False` after *message_fn* (e.g. pre-commit hook rejected the commit). All `notify_fn` exceptions are swallowed so a broken UI hook can never block the commit itself.
- **Returns:** True if a commit was created, False if nothing to commit.

**`run_tasks_parallel`** — Execute multiple SorcarAgent tasks concurrently using threads. Each task gets its own `ChatSorcarAgent` instance and runs in a separate thread via :class:`~concurrent.futures.ThreadPoolExecutor`. This is ideal for I/O-bound workloads (LLM API calls, network requests) where the GIL is released during I/O waits. This helper is a pure parallel executor: it has no knowledge of backend task ids or any frontend concepts. It simply marks each spawned agent as a sub-agent (via `_subagent_info`) and the sub-agent itself owns any sub-agent-specific behaviour (such as broadcasting `new_tab` to a browser-based frontend) inside its own `run()` method.<br/>`def run_tasks_parallel(tasks: list[str], max_workers: int | None = None, model_name: str | None = None, work_dir: str | None = None, printer: Printer | None = None, totals_out: dict[str, float] | None = None) -> list[str]`

- `tasks`: List of task description strings. Each string is passed as the `prompt_template` argument to :meth:`SorcarAgent.run`. Example:: [ "Summarize file A", "Summarize file B", ]
- `max_workers`: Maximum number of threads. `None` lets :class:`~concurrent.futures.ThreadPoolExecutor` pick a default (typically `min(32, cpu_count + 4)`).
- `model_name`: LLM model name for all parallel agents. `None` uses the default from persistence (same as :meth:`SorcarAgent.run`).
- `work_dir`: Working directory for all parallel agents. `None` uses the default (`artifact_dir/kiss_workdir`).
- `printer`: Optional printer from the parent agent. Forwarded verbatim to each sub-agent's `run` so live events continue to flow through the same channel. The executor itself does not call any printer methods.
- **Returns:** List of YAML result strings in the **same order** as *tasks*. Each string contains `success` and `summary` keys. If a task raises an unhandled exception the corresponding entry is a YAML string with `success: false` and the traceback in `summary`.

______________________________________________________________________

#### `kiss.agents.sorcar.chat_sorcar_agent` — *Stateful Sorcar agent with chat-session persistence.*

##### `class ChatSorcarAgent(SorcarAgent)` — SorcarAgent with chat-session state management.

**Constructor:** `ChatSorcarAgent(name: str) -> None`

- **chat_id** — Return the current chat session ID ("" means new session).<br/>`chat_id() -> str` *(property)*

- **new_chat** — Reset to a new chat session (equivalent to VS Code 'Clear').<br/>`new_chat() -> None`

- **resume_chat_by_id** — Resume a chat session using a stable chat identifier.<br/>`resume_chat_by_id(chat_id: str) -> None`

  - `chat_id`: String chat session identifier to resume.

- **build_chat_prompt** — Load chat context and augment prompt with previous tasks/results.<br/>`build_chat_prompt(prompt: str) -> str`

  - `prompt`: The original task prompt.
  - **Returns:** The augmented prompt with chat history prepended, or the original prompt if no prior context exists.

- **run** — Run the agent with chat-session context management. Loads prior chat context, persists the new task, augments the prompt with previous tasks/results, runs the underlying agent, and saves the result back to history. Only the result summary is persisted here. Callers that record chat events (e.g. the VS Code server) persist events incrementally via :func:`~kiss.agents.sorcar.persistence._append_chat_event`.<br/>`run(prompt_template: str = '', **kwargs: Any) -> str`

  - `prompt_template`: The task prompt.
  - `**kwargs`: All other arguments forwarded to `SorcarAgent.run()`.
  - **Returns:** YAML string with 'success' and 'summary' keys.

______________________________________________________________________

#### `kiss.agents.sorcar.git_worktree` — *Git worktree operations and state.*

##### `class GitWorktree` — Immutable snapshot of a pending worktree task.

##### `class MergeResult(enum.Enum)` — Outcome of a merge operation.

##### `class GitWorktreeOps` — Stateless helper class with all git worktree operations.

- **discover_repo** — Find the git repo root containing *path*.<br/>`discover_repo(path: Path) -> Path | None`

  - `path`: Directory to start searching from.
  - **Returns:** The repo root path, or `None` if *path* is not in a repo.

- **current_branch** — Return the current branch name, or `None` for detached HEAD.<br/>`current_branch(repo: Path) -> str | None`

  - `repo`: Git repo root path.
  - **Returns:** Branch name string, or `None` if HEAD is detached or empty.

- **create** — Create a new worktree with a new branch.<br/>`create(repo: Path, branch: str, wt_dir: Path) -> bool`

  - `repo`: Git repo root path.
  - `branch`: New branch name to create.
  - `wt_dir`: Directory for the new worktree.
  - **Returns:** True if worktree was created successfully, False otherwise.

- **remove** — Remove a worktree directory (best-effort, force). Every caller (`discard`, `cleanup_partial`, `_finalize_worktree`) intends permanent removal of an agent-owned directory, so failures of `git worktree remove` are escalated rather than abandoned: 1. Plain `--force` (handles dirty/untracked content). 2. `--force --force` (git requires force twice for worktrees locked via `git worktree lock`). 3. Direct `rmtree` + `git worktree prune` (handles corrupted worktrees — e.g. a deleted `.git` link file — that fail git's removal validation entirely).<br/>`remove(repo: Path, wt_dir: Path) -> None`

  - `repo`: Git repo root path.
  - `wt_dir`: Worktree directory to remove.

- **prune** — Prune stale worktree bookkeeping entries.<br/>`prune(repo: Path) -> None`

  - `repo`: Git repo root path.

- **stage_all** — Stage all changes in the worktree (`git add -A`).<br/>`stage_all(wt_dir: Path) -> None`

  - `wt_dir`: Worktree directory.

- **commit_all** — Stage all changes and commit in the worktree.<br/>`commit_all(wt_dir: Path, message: str) -> bool`

  - `wt_dir`: Worktree directory.
  - `message`: Commit message.
  - **Returns:** True if a commit was created, False if nothing to commit or the commit failed (e.g. pre-commit hook rejection).

- **commit_staged** — Commit already-staged changes without re-staging. Unlike :meth:`commit_all`, this does **not** run `git add -A` first. Use when the caller has already staged the desired changes (e.g. via :meth:`stage_all`).<br/>`commit_staged(wt_dir: Path, message: str, *, no_verify: bool = False) -> bool`

  - `wt_dir`: Worktree directory with pre-staged changes.
  - `message`: Commit message.
  - `no_verify`: If True, pass `--no-verify` to skip pre-commit and commit-msg hooks. Use for infrastructure commits (e.g. baseline snapshots) that must always succeed.
  - **Returns:** True if a commit was created, False if nothing was staged or the commit failed (e.g. pre-commit hook rejection).

- **has_uncommitted_changes** — Check if the working tree or index has uncommitted changes.<br/>`has_uncommitted_changes(wt_dir: Path) -> bool`

  - `wt_dir`: Git working directory to check.
  - **Returns:** True if there are staged, unstaged, or untracked changes.

- **status_porcelain** — Return the raw `git status --porcelain` output. Used by failure-path warnings (e.g. :meth:`~kiss.agents.sorcar.worktree_sorcar_agent.WorktreeSorcarAgent._finalize_worktree`) to embed the exact leftover files in the log so an operator can tell a real pre-commit-hook rejection apart from a race leftover or a corrupt index without having to ssh in and run git themselves.<br/>`status_porcelain(wt_dir: Path) -> str`

  - `wt_dir`: Git working directory to inspect.
  - **Returns:** The stripped porcelain output (possibly empty).

- **staged_diff** — Return the staged diff text for the worktree.<br/>`staged_diff(wt_dir: Path) -> str`

  - `wt_dir`: Worktree directory (must have staged changes).
  - **Returns:** The diff text, or empty string if no staged changes.

- **checkout** — Checkout a branch in the main worktree.<br/>`checkout(repo: Path, branch: str) -> tuple[bool, str]`

  - `repo`: Git repo root path.
  - `branch`: Branch name to checkout.
  - **Returns:** `(True, "")` on success, `(False, stderr)` on failure. The stderr string describes why the checkout failed (e.g. dirty working tree, missing branch).

- **stash_if_dirty** — Stash uncommitted changes if the working tree or index is dirty. Uses `git stash push --include-untracked` so both staged and unstaged changes (including new files) are saved.<br/>`stash_if_dirty(repo: Path) -> bool`

  - `repo`: Git repo root path.
  - **Returns:** True if a stash entry was created, False if the tree was clean.

- **stash_pop** — Pop the latest stash entry, preserving the staging state. Tries `git stash pop --index` first so that files that were staged before the stash stay staged after the pop. If `--index` fails (e.g. the merge changed a file that was in the index), falls back to plain `git stash pop` which restores all changes as unstaged.<br/>`stash_pop(repo: Path) -> bool`

  - `repo`: Git repo root path.
  - **Returns:** True if the pop succeeded, False on conflict or error.

- **squash_merge_branch** — Squash-merge a branch and commit the result. Uses `git merge --squash` to apply all changes from *branch*, then commits the staged result using the HEAD commit message of *branch* (see :meth:`_merge_commit_message`). On conflict, resets to a clean state with `git reset --hard`.<br/>`squash_merge_branch(repo: Path, branch: str) -> MergeResult`

  - `repo`: Git repo root path.
  - `branch`: Branch to squash-merge.
  - **Returns:** :attr:`MergeResult.SUCCESS` or :attr:`MergeResult.CONFLICT`.

- **unstaged_files** — List files with unstaged changes in the working tree.<br/>`unstaged_files(repo: Path) -> list[str]`

  - `repo`: Git repo root path.
  - **Returns:** List of files with uncommitted, unstaged modifications, or an empty list if the command fails.

- **staged_files** — List files with staged (cached) changes in the index.<br/>`staged_files(repo: Path) -> list[str]`

  - `repo`: Git repo root path.
  - **Returns:** List of files staged for commit, or an empty list if the command fails.

- **delete_branch** — Delete a branch and its git config section (best-effort). Tries `-d` first (safe delete), falls back to `-D` (force). Also removes the `branch.<name>.*` config section.<br/>`delete_branch(repo: Path, branch: str) -> bool`

  - `repo`: Git repo root path.
  - `branch`: Branch name to delete.
  - **Returns:** True if the branch was deleted (or never existed), False if git refused both `-d` and `-D` — typically because the branch is the current HEAD of a worktree and cannot be deleted without first switching away.

- **branch_exists** — Check if a branch exists.<br/>`branch_exists(repo: Path, branch: str) -> bool`

  - `repo`: Git repo root path.
  - `branch`: Branch name to check.
  - **Returns:** True if the branch exists.

- **ensure_excluded** — Add `.kiss-worktrees/` to local git exclude (not .gitignore). Uses `<git_common_dir>/info/exclude` so the agent never modifies any tracked file in the user's repo.<br/>`ensure_excluded(repo: Path) -> None`

  - `repo`: Git repo root path.

- **ensure_scratch_merge_driver** — Install a merge driver that auto-resolves agent scratch files. `PROGRESS.md` is a tracked per-task agent log that every task wholesale rewrites ("clear PROGRESS.md when a new task begins"), and `src/kiss/INJECTIONS.md` is an agent-maintained scratch prompt file that can drift between task baselines. Whenever main's copy diverged from the worktree's fork point, these scratch-file rewrites made three-way merges (`git merge --squash` and `git cherry-pick` alike) conflict — blocking the entire worktree merge over non-user source state. This registers a repo-local `kiss-scratch` merge driver that resolves content conflicts in such files by keeping the incoming branch's version (`%B`): the newest task's scratch state wins, matching the clear-on-new-task convention. Installation uses only untracked plumbing — `<git_common_dir>/info/attributes` plus repo-local config — so no tracked file is ever modified, and the driver equally fixes the *manual* merge/cherry-pick commands suggested to the user on merge failure.<br/>`ensure_scratch_merge_driver(repo: Path) -> None`

  - `repo`: Git repo root path.

- **load_original_branch** — Load the original branch from git config.<br/>`load_original_branch(repo: Path, branch: str) -> str | None`

  - `repo`: Git repo root path.
  - `branch`: The worktree branch name.
  - **Returns:** The original branch name, or `None` if not stored.

- **save_original_branch** — Store the original branch in git config.<br/>`save_original_branch(repo: Path, branch: str, original: str) -> bool`

  - `repo`: Git repo root path.
  - `branch`: The worktree branch name.
  - `original`: The original branch to store.
  - **Returns:** True if config was saved successfully, False otherwise.

- **save_baseline_commit** — Store the baseline commit SHA in git config. The baseline commit captures the user's dirty state (staged, unstaged, untracked files) at worktree creation time. Downstream operations diff against this SHA to isolate agent-only changes.<br/>`save_baseline_commit(repo: Path, branch: str, sha: str) -> bool`

  - `repo`: Git repo root path.
  - `branch`: The worktree branch name.
  - `sha`: The baseline commit SHA to store.
  - **Returns:** True if config was saved successfully, False otherwise.

- **copy_dirty_state** — Copy uncommitted/staged/untracked files from main worktree. Reads `git status --porcelain` in *repo* and mirrors every dirty file into *wt_dir*. Files that exist in the main worktree are copied; files that were deleted are removed from *wt_dir*. The caller is expected to stage and commit the result as a baseline commit.<br/>`copy_dirty_state(repo: Path, wt_dir: Path) -> bool`

  - `repo`: Git repo root (main worktree).
  - `wt_dir`: Target worktree directory.
  - **Returns:** True if any dirty state was copied, False if the main worktree was clean.

- **head_sha** — Return the SHA of HEAD in the given directory.<br/>`head_sha(wt_dir: Path) -> str | None`

  - `wt_dir`: Git working directory (repo root or worktree).
  - **Returns:** The full SHA string, or `None` on failure.

- **squash_merge_from_baseline** — Squash-merge only the agent's changes (after baseline) into HEAD. Uses `git cherry-pick --no-commit` to replay each commit after *baseline* onto the current HEAD. Cherry-pick performs a proper three-way merge per commit (using the commit's parent as the merge base), so it handles cases where the user's dirty state (captured in the baseline) diverges from the committed HEAD content. Falls back to :meth:`squash_merge_branch` when *baseline* is `None` (legacy worktrees).<br/>`squash_merge_from_baseline(repo: Path, branch: str, baseline: str) -> MergeResult`

  - `repo`: Git repo root path.
  - `branch`: The worktree branch to merge from.
  - `baseline`: SHA of the baseline commit to diff against.
  - **Returns:** :attr:`MergeResult.SUCCESS` or :attr:`MergeResult.CONFLICT`.

- **cleanup_partial** — Remove a partially-created worktree and branch (best-effort).<br/>`cleanup_partial(repo: Path, branch: str, wt_dir: Path) -> None`

  - `repo`: Git repo root path.
  - `branch`: The branch name to delete.
  - `wt_dir`: The worktree directory to remove.

**`repo_lock`** — Return a per-repo re-entrant lock for multi-step git operations. Concurrent tabs operating on the same main repository must serialize their checkout → stash → merge → pop sequences to prevent interleaving that could corrupt the working tree. The lock is an :class:`threading.RLock` (re-entrant) so a caller holding the lock for an outer multi-step operation (e.g. `_try_setup_worktree` releasing a previous worktree before creating a new one) can safely call inner helpers (`_do_merge`, `discard`) that re-acquire the same lock on the same thread. Cross-thread acquisitions still block as expected.<br/>`def repo_lock(repo: Path) -> threading.RLock`

- `repo`: Git repo root path.
- **Returns:** A :class:`threading.RLock` specific to the resolved repo path.

**`strip_worktree_suffix`** — Return *path* with the `.kiss-worktrees/kiss_wt-<slug>[/...]` suffix removed, leaving the parent repository path. Worktree directories are ephemeral — they are deleted when the worktree is merged or discarded. Persisting a worktree path in long-lived storage (e.g. `task_history.extra.work_dir`) would leave dangling references the user-visible UI cannot resolve. Use this helper at every persistence boundary that records a `work_dir` for later display or filtering. Paths that do not contain a `<repo>/.kiss-worktrees/kiss_wt-*` segment are returned unchanged, including the empty string.<br/>`def strip_worktree_suffix(path: str) -> str`

- `path`: An absolute or relative filesystem path string.
- **Returns:** The parent-repo path string, or the unchanged input when the path is not inside a KISS worktree.

______________________________________________________________________

#### `kiss.agents.sorcar.worktree_sorcar_agent` — *Worktree-based agent that runs each task on an isolated git branch.*

##### `class WorktreeSorcarAgent(ChatSorcarAgent)` — SorcarAgent that isolates every task in a git worktree.

**Constructor:** `WorktreeSorcarAgent(name: str) -> None`

- **new_chat** — Reset to a new chat session, auto-merging any pending worktree. If a worktree task is pending from the previous session, it is auto-committed with a detailed LLM message and squash-merged into the original branch before the chat state is reset.<br/>`new_chat() -> None`

- **run** — Run a task on an isolated git worktree branch. Creates a new worktree and branch, redirects `work_dir` into the worktree, and delegates to `ChatSorcarAgent.run()`. Each call starts a fresh worktree; any previously pending branch from an earlier run is auto-committed and squash-merged into its original branch first (kept in git for manual resolution only when that auto-merge fails or conflicts). Falls back to direct execution (no worktree) when: - `use_worktree` kwarg is explicitly `False` - `work_dir` is not inside a git repo - The repo has no commits - HEAD is detached (no merge target) - Any git command fails during setup<br/>`run(prompt_template: str = '', **kwargs: Any) -> str`

  - `prompt_template`: The task prompt.
  - `**kwargs`: All other arguments forwarded to `ChatSorcarAgent.run()`. The optional `use_worktree` kwarg (default `True`) gates the worktree behavior — when `False` the call is equivalent to `ChatSorcarAgent.run()`.
  - **Returns:** YAML string with 'success' and 'summary' keys.

- **merge** — Merge the task branch into the original branch. Every step is idempotent — safe to re-run after a crash. Auto-commits any uncommitted changes in the worktree before merging. If the main working tree has uncommitted changes, they are stashed before the merge and restored afterward so user edits don't block the merge.<br/>`merge() -> str`

  - **Returns:** Success message, or error message if merge fails.

- **discard** — Throw away the task branch and worktree, checkout original. Every step is idempotent — safe to call multiple times. Acquires `repo_lock` to serialize against concurrent merge/release operations on the same repository.<br/>`discard() -> str`

  - **Returns:** Confirmation message (includes a warning if checkout to the original branch failed).

______________________________________________________________________
