Contracts
claude-md-section.md
Contract: CLAUDE.md Orientation Block Format
Purpose
Specifies the exact structure of the Spec Kitty orientation block injected into .claude/CLAUDE.md (and into any other Markdown rules file via MarkdownRulesWriter).
Format
<!-- spec-kitty:orientation -->
**Spec Kitty v{version}** — project: {project_slug} ({health})
[optional upgrade line]
[optional migration line]
Two usage patterns:
- **Full mission** (spec → plan → tasks → implement → review → merge):
trigger: "spec out", "create a mission", "write a spec", "plan this"
→ run `/spec-kitty.specify`
- **Lightweight dispatch** (ad-hoc fix, question, or advice — no mission created):
trigger: "hey spec kitty", "use spec kitty to", "spec kitty, fix/do/ask/advise"
→ run `spec-kitty do "<request verbatim>"`
<!-- /spec-kitty:orientation -->
Optional Lines
⚠ Upgrade available: {available_version} — run \spec-kitty upgrade --cli\ to update.
- Upgrade line (present only when
health == "upgrade-available"):
⚠ Project migration required — run \spec-kitty upgrade\ before using missions.
- Migration line (present only when
health == "migration-required"):
Idempotency
has_presence(): returnsTrueif and only if<!-- spec-kitty:orientation -->appears anywhere in the file content.- On re-write: the block between
<!-- spec-kitty:orientation -->and<!-- /spec-kitty:orientation -->(inclusive) is replaced in-place. Lines outside the markers are not touched. - On first write (append_mode=True): the block is appended to the file with a preceding newline. The file is created if it does not exist.
- On first write (append_mode=False): the block is written as the entire file content.
Removal
remove() strips the block between the markers (inclusive) from the file. If append_mode=False, the file is deleted entirely.
settings-json-hook.md
Contract: Claude Code settings.json Hook Structure
Purpose
Specifies the exact JSON structure added to .claude/settings.json by ClaudeCodeHookRegistrar.register().
Added Structure
{
"hooks": {
"SessionStart": [
{
"hooks": [
{ "type": "command", "command": "spec-kitty session-start" }
]
}
]
}
}
Merge Semantics
- The file is read as JSON if it exists; treated as
{}if absent or malformed. hookskey is created if absent.hooks.SessionStartkey is created as[]if absent.- The spec-kitty entry object is appended only if no existing list element contains
{"type": "command", "command": "spec-kitty session-start"}(exact match on these two fields). - All other keys at any level are left untouched.
- The result is written back atomically (temp file +
os.replace()).
Idempotency Check
is_registered() returns True if hooks.SessionStart contains any element whose hooks list contains {"type": "command", "command": "spec-kitty session-start"}.
Removal Semantics (unregister())
- Filter out only the spec-kitty entry from the
hooks.SessionStartlist. - If the list becomes empty, leave
hooks.SessionStart: [](do not delete the key). - All other entries and keys are preserved.
- Write back atomically.
version-cache.md
Contract: Version Cache File Format
Path
~/.kittify/last-cli-check.json
(Note: global user-level cache, not per-project. Path.home() / ".kittify" / "last-cli-check.json")
Schema
{
"checked_at": "2026-06-07T13:00:00+00:00",
"latest_version": "3.2.0"
}
| Field | Type | Description |
|---|---|---|
checked_at | ISO 8601 UTC datetime string | When the background check last completed successfully |
latest_version | str | Latest version string returned by the index query |
TTL Semantics
- TTL = 3600 seconds (1 hour).
- Cache is valid if
now - checked_at < TTL_SECONDS. - Cache is stale if the file is absent, unreadable, malformed, or
now - checked_at >= TTL_SECONDS. - On stale/absent:
check_in_background()is spawned (fire-and-forget);get_available_version()returns the last knownlatest_versionorNoneif no cache exists. - On valid:
check_in_background()is not spawned;get_available_version()returns the cachedlatest_version.
Write Semantics
- Written atomically (temp file +
os.replace()) in the same directory as the target. - Parent directory
~/.kittify/is created withmkdir(parents=True, exist_ok=True)before writing. - Any write failure is silently swallowed — the cache is best-effort.