Skip to content

Repo State

xsyetopz edited this page Oct 16, 2026 · 1 revision

Repo State

This page describes the repostate slice of the core plugin claudetto. It is for a user who wants to know why Claude asks before an edit of CHANGELOG.md or before a package install, and for a contributor who edits the slice.

The Problem

Three failures repeat in long sessions:

  • Claude states the version, the branch, or the release state from memory, and the state is wrong.
  • Claude adds a line to [Unreleased] for a version that has no tag yet, so the line goes to the wrong release.
  • Claude installs a package with a name that it made up, and the registry has no such package.

The slice reads the facts and asks before the risky call. It makes no model call.

The Git State at the Start

A classic.SessionStart hook gives Claude one context text. The text holds the output of git describe --tags --always, the branch, the version in the manifests, and whether the tag of that version exists. The manifests are package.json and .claude-plugin/plugin.json. The tag names that the check tries are 1.2.3, v1.2.3, and <name>--v1.2.3. A folder with no git gives no text. A project with no manifest gives the git state and says that the manifests give no version. The text is a fact that the hook read, never a fact from memory (D56 in Engine Facts).

The Ask on [Unreleased]

A tool.check hook asks before an Edit, a MultiEdit, or a Write of CHANGELOG.md that changes the [Unreleased] section, when the version in the manifest has no tag. The hook parses the Markdown structure. A heading in a code block is no heading, and the section ends at the next heading of level 1 or 2. The hook applies the edit to the text of the file and compares the section before and after. So an edit of another section gets no ask. The reason names the version and the manifest that holds it. When the version has a tag, the release is out, and the hook asks nothing.

The Ask on a Package

A tool.check hook asks before a bun, npm, pnpm, or yarn command that adds a package. The add words are add and a for bun, install, i, and add for npm, add, install, and i for pnpm, and add for yarn. src/kernel/shell.ts parses the command, so a chain, a wrapper, and a quoted text are right. A spec that is a path, a URL, or a user/repo form is no registry name and gets no check. The hook checks each name in this order:

  1. The lockfile or package.json holds the name as a whole name. Then the package is known, and the hook makes no request.
  2. The registry page of the name answers with 404 or 410. Then the hook asks, and the reason names the missing package.

The request has a timeout of REGISTRY_TIMEOUT_MS (4 seconds). When the registry cannot be reached, answers late, or answers with a status that says nothing, the hook does not ask. It writes a notice for the user with $.ui.log, and the model does not read the notice.

The Served-Model Mark

A turn.complete hook compares the model of the response (usage.model) with the model that the session requested ($.session.model()). When both are Claude ids that name different models, the hook sets the text of $.ui.status to a mark that names both models. A date suffix in an id is no difference. When a later response comes from the requested model, the hook clears the mark. A turn of a subagent has its own model, so it sets no mark. The status-line command of Claude Code gets no served model in its input, so the mark goes through $.ui.status.

Bounds

The bounds are in slices/repostate/limits.ts: the timeout of git, the timeout of the registry, the registry URL, the largest file, the lockfiles, and the largest count of packages for one command.

Cost

No hook makes a model call. The session start runs up to four git commands. A package that no lockfile holds costs one request to the registry.

Related Pages

Clone this wiki locally