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(): returns True if 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.
  • hooks key is created if absent.
  • hooks.SessionStart key 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.SessionStart list.
  • 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"
}
FieldTypeDescription
checked_atISO 8601 UTC datetime stringWhen the background check last completed successfully
latest_versionstrLatest 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 known latest_version or None if no cache exists.
  • On valid: check_in_background() is not spawned; get_available_version() returns the cached latest_version.

Write Semantics

  • Written atomically (temp file + os.replace()) in the same directory as the target.
  • Parent directory ~/.kittify/ is created with mkdir(parents=True, exist_ok=True) before writing.
  • Any write failure is silently swallowed — the cache is best-effort.