# LitClaude Hooks

LitClaude hooks translate the LitClaude prompt workflow into Claude Code hook
events while keeping execution local and bounded.

## #contract.activation

```yaml
contract_schema_version: litclaude.llm-contract.v1
artifact_type: hook-doc
surface: Claude Code plugin hooks
runtime: plugins/litclaude/bin/litclaude-hook.js
registration: plugins/litclaude/hooks/hooks.json
settings_activation: global Claude settings.json enabledPlugins entry for litclaude@litclaude-ai
events: [SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStart, SubagentStop, SessionEnd]
verdicts: [PASS, FAIL, BLOCKED]
```

| Hook contract field | LLM-facing rule | Runtime evidence |
| --- | --- | --- |
| activation | UserPromptSubmit may add contract context, not execute prompt text. | Hook JSON `additionalContext`. |
| route safety | Slash commands stay on Claude Code's native command surface. | Near-miss and code-span hook fixtures. |
| completion | Hook edits require direct fixture output plus plugin validation. | `node plugins/litclaude/bin/litclaude-hook.js ...`, `npm run validate:plugin`. |

## #contract.inputs

- Hook stdin JSON from Claude Code, including `hook_event_name`, `prompt`, `cwd`, `transcript_path`, tool metadata, and optional native-goal state.
- Repo-local command/SKILL/agent contracts using the same `litclaude.llm-contract.v1` vocabulary.
- Runtime capability facts for native `/goal`, `Workflow`, `EnterWorktree`, agent teams, MCP, and LSP as observed in the active Claude Code session.

## #contract.mode_matrix

| Event or route | Contract mode | Hard boundary |
| --- | --- | --- |
| `SessionStart` | rules discovery and static injection | Adds context and, when `source: compact`, durably reserves bounded rule re-injection after clearing the session dedup set. |
| `UserPromptSubmit` | route classifier, static-rule straggler lane, and explicit resume gate | Prompt routing adds guidance. Static-rule delivery may update rule dedup state; the exact start-work resume route may also mutate code-owned lifecycle state. |
| `PreToolUse` | bounded-authority enforcement | Allows normal Claude permission handling only for classified, authorized action/root pairs; otherwise denies before execution. |
| `PostToolUse` | conditional post-edit routing, dynamic rule injection, and deliverable hedge feedback | Names only the checks the edit earned and only the rules whose globs matched. When the current transcript identifies a loaded LitClaude skill, it reads that skill's shipped output-channel declaration and scans bounded submitted content for reader-facing paths only for `client_deliverable` / `reply`. It emits nothing when no lane applies and does not claim completion. |
| `Stop` | start-work continuation, then litgoal autoloop gate | Root-session and progress-bound; no unbounded loop. |
| `SubagentStart` / `SubagentStop` | lane identity observation | Records the root session, child lane, and Claude-owned worktree without emitting child continuation. |
| `SessionEnd` | root-session receipt | Records the observed end and emits no continuation. |

## #contract.procedure

1. Parse stdin JSON; malformed input returns a controlled error.
2. Classify only explicit LitClaude triggers outside code spans, closed backtick or tilde code fences, slash mentions, and diagnostic literal prompts. Text outside a closed fence and intentionally unclosed fence content remains eligible.
3. Return contract vocabulary with route, Skill, mode, evidence, and hard-stop guidance.
4. Preserve prompt text as inert data; emit redacted summaries and stable route strings only.
5. Prove changes with targeted hook fixtures, route tests, and plugin validation before claiming readiness.

## #contract.outputs

- Claude Code hook JSON containing `continue: true`, `hookSpecificOutput.hookEventName`, and route-specific `additionalContext`.
- Optional `systemMessage` for the once-per-session mark and active route ignition marks.
- `DELIVERABLE_HEDGE_VIOLATION` as `PostToolUse` `additionalContext` after a client-deliverable write contains a matched limitation phrase; the file has already been written, so the response asks Claude to revise it rather than claiming the write was prevented.
- `BLOCKED:` text for natural-language start-work, unavailable native goal binding, disabled Dynamic workflow, or gated agent-team setup.
- Unavailable native goal binding also includes `READY_TO_PASTE` and one bounded `/goal` command for the user to copy, paste, and send; the hook never enters or submits it.

## #contract.evidence

- Direct fixture command: `node plugins/litclaude/bin/litclaude-hook.js user-prompt-submit < fixtures/hooks/user-prompt-litwork.json`.
- Route tests in `test/hooks.test.mjs` for positive routes, near-misses, degraded goal guidance, and unsafe prompt text.
- Real plugin surface checks: `npm run validate:plugin`, `npm run doctor`, scanner, and payload guard when hook payload changes.

## #contract.hard_stops

- Do not echo dangerous prompt substrings, secrets, or command text into hook output.
- Do not claim native `/goal`, Dynamic workflow, EnterWorktree, MCP, LSP, or agent-team success unless Claude Code exposes and confirms that surface.
- Prompt route classification does not mutate host settings, registries, or remote state. The static-rule lane may write only project-local `.litclaude/rules/session-<id>.json` dedup state, and the exact trusted start-work resume route may mutate only code-owned schema-3 lifecycle state after identity checks.

## #contract.anti_patterns

- Do not convert a natural-language trigger into a hidden slash-command dispatch.
- Do not treat a hook context injection as proof that a Skill, agent, Workflow, or `/goal` actually ran.
- Do not broaden route detection to substrings, code blocks, command mentions, or documentation examples.
- Do not replace Claude Code plugin terminology with another harness's routing model.

## Hook Events

The plugin declares these events in `plugins/litclaude/hooks/hooks.json`. Claude Code
loads that manifest only when install has enabled `litclaude@litclaude-ai` in the global
Claude `settings.json` `enabledPlugins` map. The hook runner itself does not edit those
settings. Separately, installer permission modes mutate global Claude settings
`permissions.allow` and `permissions.deny`, preserve pre-existing entries, track only
entries LitClaude inserted, and remove only those tracked entries on mode change or
uninstall.

For `SessionStart` with `source: compact`, the **2-per-session** budget reservation must
be durably persisted before static rule text is emitted. A failed reservation keeps the hook alive
for diagnostics but does not re-inject rules as if a budget unit had been reserved.
Reservation uses a per-session exclusive filesystem lock around strict state read,
increment, fsynced temporary write, atomic rename, and directory fsync where supported. Known
unsupported directory-sync errors preserve portability; genuine I/O failures report an
uncertain reservation, while renamed state still suppresses duplicate emission. Genuinely
missing state may initialize at count 0. Existing malformed, unreadable, oversized, symlinked,
or otherwise uncertain state fails closed without replacement. Lock uncertainty also fails
closed; ordinary owners remove their lock artifact, and only a stale valid nonce/PID owner
whose process is no longer alive may be replaced through a fenced takeover. Live or malformed
incumbents are not silently removed. Claude Code does not collect model context from its
`PostCompact` stdout, so LitClaude does not register that event; the legacy runner is a
silent no-op for callers that still invoke it directly.

| Event | Runner | Purpose |
| --- | --- | --- |
| `SessionStart` | `plugins/litclaude/bin/litclaude-hook.js session-start` | Runs the bounded foreground automatic-update barrier on a fresh session, discovers repo-local rule files, names the newest valid `plans/<slug>.md`, and on `source: compact` spends one of two durable re-injection reservations at reduced caps. |
| `UserPromptSubmit` | `plugins/litclaude/bin/litclaude-hook.js user-prompt-submit` | Detects prompt routes and injects workflow context; independently delivers static rules not already recorded for this session. |
| `PreToolUse` | `plugins/litclaude/bin/litclaude-hook.js pre-tool-use` | Enforces semantic action/root grants before Write, Edit, MultiEdit, NotebookEdit, Bash, Agent, and bounded read tools execute. |
| `PostToolUse` | `plugins/litclaude/bin/litclaude-hook.js post-tool-use` | Names the post-edit checks the edit actually earned, injects any glob-scoped rule matching the edited paths, and returns model-visible feedback when bounded submitted content for the loaded skill's declared `client_deliverable` artifact contains a hedge finding. |
| `Stop` | `plugins/litclaude/bin/litclaude-hook.js stop` | Emits bounded start-work continuation on new progress, otherwise applies the opt-in litgoal autoloop gate. |
| `SubagentStart` | `plugins/litclaude/bin/litclaude-hook.js subagent-start` | Registers child-lane and Claude-owned worktree identity. |
| `SubagentStop` | `plugins/litclaude/bin/litclaude-hook.js subagent-stop` | Finalizes the lane without child continuation. |
| `SessionEnd` | `plugins/litclaude/bin/litclaude-hook.js session-end` | Records root-session end without blocking Claude. |

### Deliverable hedge feedback

The hedge checker runs after a successful `Write`, `Edit`, or `MultiEdit` whose
host event includes explicit `content`, `new_string`, or `edits`, because Claude
Code's `PostToolUse` response can add `additionalContext` beside the completed
tool result. It scans only that bounded host-event content and never opens the
artifact path. The hook locates a loaded LitClaude skill only from an
assistant-side Skill invocation or the
exact Claude `hook_additional_context` attachment shape carrying LitClaude's
own `<litclaude-skill-body>` marker and the byte-identical current shipped skill
body in the bounded host transcript; generic
attachments, user text, and assistant text that merely echoes the marker cannot
grant a genre. It then reads `artifact_genre` and `limitations_channel` from that
skill's shipped `#contract.output_channels` block. The genre is never inferred
from a filename and is not maintained as a parallel hard-coded skill map.

Only `client_deliverable` with `limitations_channel: reply` is enforced in this
guard. The same prose under `working_note` is intentionally left alone. Quoted
status enums, JSON/YAML status scalars, fenced code, and status-table cells are
state rather than prose and are excluded. Korean absent-evidence matching is
anchored to evidence-bearing subjects such as 근거, 자료, 측정값, and 관찰 기록 so
factual specifications such as `이 API는 반환값이 없습니다` stay clean.

This is post-write feedback, not rollback or a permission denial. If the
transcript is absent, stale, oversized, malformed, or does not identify a known
bundled skill, or if the host event is path-only, unknown, outside the lexical
working root, not reader-facing, oversized, or over budget, the guard fails open
and the normal post-edit routes continue. It accepts at most 16 paths, 512 KiB
of submitted content per path, and 2 MiB in aggregate. Because no artifact
pathname is opened, a FIFO or an ancestor replacement cannot block the hook or
redirect which bytes it scans. The direct driver and regression fixtures live in
`test/deliverable-hedge-guard.test.mjs`.

### Cross-session plan discovery

The scaffold writer remains the only plan write surface. It writes through a same-directory
temporary file, fsyncs the file, and commits atomically. A new target uses a no-clobber
create step. Existing no-op, `--reset`, `--force`, hand-edit, and symlink checks remain in
force. A slug-scoped lock serializes paired writers. A concurrent writer fails closed instead
of replacing an in-flight pair. A failed paired write removes only files created by that
invocation. Temporary files and lock files are verified after cleanup. Residue is reported
on the write error instead of being hidden.

`SessionStart` and the non-resume `$start-work` prompt routes perform discovery only. The
resolver walks upward from the hook `cwd` to the nearest project marker. It never crosses that
boundary. Without a project marker, the supplied `cwd` is the boundary. It returns a path
relative to the supplied `cwd`, so a nested hook receives a usable `../plans/<slug>.md` path.
It rejects symlinked parents and directories. It ignores symlinked files, unreadable or
changing entries, files over 256 KiB, and plans that fail the scaffold's canonical
`checkPlanStructure()` check. An unfilled or stale scaffold therefore stays silent. The
notice does not echo plan text, create `.litclaude` lifecycle state, or grant start-work
authority. The exact resume route remains the only lifecycle mutation path. `--check` uses
the same bounded, no-follow regular-file read boundary.

Node's path-based `rename()` has no compare-and-swap primitive. The writer therefore combines
the slug lock with parent and target identity checks before and after the commit. A
non-cooperating process can still race the final path operation. The writer reports a changed
identity and fails closed when it observes that race. It does not claim an atomic
compare-and-swap guarantee that Node's standard library does not provide.

### Foreground automatic-update barrier

The SessionStart hook waits for one bounded automatic-update transaction when a
fresh product-owned cache entry names a newer strict stable package version. The
transaction uses a separate install lock, credential-free npm environment, exact
`npm exec --yes --package @litfamily/litclaude@<version> -- litclaude-ai` arguments, a backup/journal,
post-install doctor, and rollback before the hook emits its rules context. The
same lane is available to interactive TTY `install`, `update`, and `doctor`
commands. `--no-auto-update`, `LITCLAUDE_NO_AUTO_UPDATE`,
`NO_UPDATE_NOTIFIER`, and `LITCLAUDE_NO_UPDATE_CHECK` disable it. CI, JSON,
dry-run, non-TTY management commands, import, and tool surfaces remain no-op;
the host-owned SessionStart hook is the lifecycle exception to the TTY check.
The detached registry cache refresh described in the update-notifier section is
advisory and never performs an install.

## Bounded-authority start-work lifecycle

The hook and CLI share `plugins/litclaude/lib/start-work-lifecycle.mjs`.
Schema-3 `.litclaude/boulder.json` owns active work/session identity, monotonic
revision, canonical roots, semantic action/root grants, consumed grants,
fenced-safe progress, bounded events/history, and terminal state. CLI mutations
use exact idempotency keys, exclusive locks, and ledger reconciliation.
PreToolUse Bash handling recognizes only a small command grammar: every
permitted path operand is canonicalized and checked against the matching
semantic grant, while relocation flags, shell syntax that would require a
speculative parser, and unverified or outside targets are denied without
creating an approval boundary.

Resume is the only mutating `UserPromptSubmit` exception. It accepts only the
complete prompt below, with no surrounding explanation or extra arguments:

```text
/litclaude:start-work resume --work-id <id> --revision <n> --boundary-id <id> --prompt-id <id> --grant-id <id>
```

The hook matches the owning Claude session and exact paused boundary before
recording the grant. There is no generic CLI resume or agent-callable approval
bypass. Returned `<litclaude-start-work-context>` JSON is bounded structured
data and contains no raw prompt. Stop ignores subagents and stale work/revision
events; each normal Stop re-evaluates progress, a new fingerprint emits once,
and `stop_hook_active: true` re-entry stays silent so stale output cannot block
again. Later unchanged turns are silent. Transcript pressure checks read only regular,
non-symlink files at or below 256 KiB.
Continuation receipts use the same bounded window as retained state events.
They remain bounded audit receipts, not re-entry instructions; neither retained
nor evicted receipts reconstructs a blocking continuation.

LitClaude intentionally does not register `WorktreeCreate` or `WorktreeRemove`.
Registering `WorktreeCreate` replaces Claude Code's native git-worktree
implementation and obligates the hook to create and return the worktree itself.
Instead, `SubagentStart.cwd` observes each Claude-owned worktree and binds it to
the root session and lane; `SubagentStop` marks host removal as expected without
pretending LitClaude owns creation or deletion.

## Workflow Trigger Phrases

The `UserPromptSubmit` hook activates on these natural-language phrases and
legacy dollar shorthands:

```text
lit
litwork
lit plan
lit review
lit research
lit search
lit query
lit goal
lit workflow
lit dynamic workflow
lit ultracode
lit team
lit team mode
lit teammates
lit start work
lit recap
litrecap
recap
리캡
lit-comprehend
comprehend
$lit-comprehend
$comprehend
$lit-plan
$lit-recap
$lit-loop
$deep-interview
$lit-loop
$review-work
$litgoal
$start-work
autoresearch <mode>
$autoresearch <mode>
autoconference <mode>
$autoconference <mode>
wikify <mode>
$wikify <mode>
handoff
lit-scientific-visualization
```

When a LIT trigger is present, the hook returns additional context containing
`LITWORK MODE ENABLED` and a visible `🔥 LIT IGNITED · <discipline> 🔥` system
message. That context is guidance for Claude Code; it is not executed as a
command. Slash commands and slash-command mentions are native Claude Code
surfaces and do not activate this prompt hook; code spans, substrings, and
compound tokens are also ignored. Closed backtick and tilde code fences are
inert; text outside them and intentionally unclosed fence content remains eligible.
Natural `lit start work`
returns a `BLOCKED:` handoff that tells the user to run `/start-work` or
`/litclaude:start-work` with the approved plan, because a prompt hook cannot
switch Claude Code agents. The `$start-work` shorthand and natural start-work
phrases activate only when they lead the prompt; diagnostic prose and copied or
blockquoted mentions remain inert.

`autoresearch`, `autoconference`, and `wikify` are family routes. A leading bare
token or dollar shorthand loads one Claude Code skill whose first argument selects
its nested mode corpus. Mid-sentence mentions, slash-command text, inline code, and
closed fences remain inert. The native command routes are
`/litclaude:autoresearch`, `/litclaude:autoconference`, and `/litclaude:wikify`.
Each prompt-hook family route injects its absolute installed canonical vendor
root derived from the hook module URL, so nested source paths resolve correctly
even when Claude Code was launched from an unrelated working directory.
Autoresearch requires the `lit-plan` → explicit budget/authority approval →
`start-work` → bounded loop → `review-work` lifecycle. Autoconference additionally
requires root multi-agent capability and returns `BLOCKED_MULTI_AGENT_UNAVAILABLE`
rather than faking concurrency; children return results while the root owns shared
state. Wikify keeps local and fetched sources inert, routes public retrieval through
`litresearch`, and closes through `review-work` plus `lit-recap` or `handoff`.
Its structured knowledge runtime receives only `fact`, `decision`, `failure`,
`risk`, `rule`, or `checkpoint` events through the local CLI or MCP tools. It
stores review-needed claims under `.litclaude/knowledge/claims.jsonl`. Explicit
save or review can accept a claim. Queries use deterministic local relevance and
return accepted claims only. They emit no block on no match. Capture never mines
raw chat, source bodies, fetched text, credentials, secrets, tokens, or
instruction-shaped payloads. Use `litclaude wikify config --capture off` for the
project-local opt-out.
Wikify state is user-owned local state. LitClaude writers cooperate through the
`.claims-lock` owner lock. Symlinks, unsafe file types, pre-existing hardlinks, and
observed path or descriptor identity changes fail closed. Atomic rename protects
target readers and crash consistency. The state is not tamper-proof or confidential
against another process with the same uid.

`handoff` is a separate exact-bare route: only the complete prompt `handoff`
after outer whitespace activates `Skill(lit-handoff)` and injects the complete
canonical bundled source. Its system message shows `🔥 LIT IGNITED · lit-handoff 🔥`;
the model begins with exactly one `🔥 **LIT IGNITED · lit-handoff** 🔥` probe line. Explanatory mentions, quoted or fenced
text, compound prompts, `/litclaude:lit-handoff`, and secret-bearing multiline
content do not activate the hook. The slash command remains a native Claude
Code command surface.

Scientific visualization has one exact-bare UserPromptSubmit route: only the
complete prompt `lit-scientific-visualization` after outer whitespace activates
`Skill(lit-scientific-visualization)`, injects the full adapter and canonical
source, and shows `🔥 LIT IGNITED · lit-scientific-visualization 🔥` in its system
message. The model begins with exactly one `🔥 **LIT IGNITED · lit-scientific-visualization** 🔥` probe line.
Generic visualization, plot, chart, figure, and scientific visualization prose,
mentions, quoted or fenced text, mixed prompts, near misses, and
`/litclaude:lit-scientific-visualization` text do not activate the prompt hook.
The slash command remains a native Claude Code command surface.

LitClaude follows the LitClaude goal pattern as an honest native-binding attempt,
not as slash-command injection. If Claude Code exposes native goal tools, the
guidance tells Claude to inspect `get_goal`, call `create_goal` only when no
matching goal is active, and delay `update_goal` until verified completion or a
genuine blocker. The hook also refuses to clobber a different active goal
without explicit replacement.

Source-backed capability note: the observed `UserPromptSubmit` hook surface can
add `additionalContext` or block; it cannot replace the prompt or run another
slash command. If goal tools are unavailable or not exposed in the running
Claude Code session, the hook returns explicit `BLOCKED:` / degraded-mode
guidance plus a `READY_TO_PASTE` command, does not claim native goal success,
keeps the local `litgoal` evidence ledger authoritative, and may suggest
`/goal <completion condition>` or
`claude -p "/goal <completion condition>"` for the native user surface.
For a separate worker session, `litclaude-ai litgoal native-worker --condition
"<completion condition>"` builds a dry-run native `/goal` worker command; adding
`--execute` starts a print/background Claude Code worker whose first prompt begins
with `/goal`. This worker launcher is not current-TUI auto-arm and must not be
described as hook-driven slash-command dispatch.

For broad work, the same context ports the LitClaude model-facing principle to
Claude Code's exposed orchestration surfaces: `lit workflow`, `lit dynamic
workflow`, and `lit ultracode` prefer current `ultracode` or explicit “run a
workflow” / “use a workflow” wording, not casual mentions. If `Workflow` is
available, propose it first and call the `Workflow` tool only after user opt-in
or existing session permission, binding every lane to evidence and cleanup. If
`CLAUDE_CODE_DISABLE_WORKFLOWS=1` is set, report the setup gate and fall back to
normal LitClaude planning/subagent delegation. If isolated edits need a
model-facing worktree lane, use `EnterWorktree`; when only the CLI surface is
available, the actionable launch form is `claude --worktree <short-name> --tmux`.

For native agent teams, `lit team`, `lit team mode`, and `lit teammates` are
setup-gated by `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. When enabled, steer
Claude to spawn named teammates only after user approval, with roles, file-scope
boundaries, acceptance criteria, wait instructions, and final synthesis. When
disabled, say so and offer subagent/Dynamic workflow fallback. Optional display
setup is `claude --teammate-mode auto` or `"teammateMode": "auto"`.

`/litclaude:lit-loop` and `$lit-loop` are the consolidated route
for that behavior. They load `/litclaude:lit-loop` / `Skill(lit-loop)`,
then map subagent delegation to `lit-planner`, `lit-executor`,
`lit-verifier`, `qa-runner`, `quality-reviewer`, and `librarian-researcher`.
The hook also reminds child assignments to use `TASK:`, `DELIVERABLE`, `SCOPE`,
and `VERIFY`, with short wait cycles and fallback only after missing
deliverables, acknowledgement-only replies, or `BLOCKED:` reports. Run
`litclaude-ai workflow-check --json` to verify the hook route, goal guidance,
Dynamic workflow guidance, subagent reliability, and command/hook agreement
before full QA.

When SessionStart sees transcript context-pressure markers, the hook adds a
bounded resume reminder to reread `HANDOFF.md`, the active plan, the start-work
ledger, Boulder state, and `git status --short` before edits.

Plain `lit` therefore activates hook context, not a visible Skill tool call.
For a visible LitClaude command/skill invocation, use the namespaced Claude
Code commands:

```text
/litclaude:lit-loop <goal or delegated objective>
/litclaude:lit-plan <planning brief>
/litclaude:start-work plans/example-plan.md
```

These are real Claude Code command files in `plugins/litclaude/commands/`.

`/deep-interview` is the clarification route. It loads
`/litclaude:deep-interview` / `Skill(deep-interview)` guidance and should run
before `/lit-plan` when a request lacks non-goals, decision boundaries, or
acceptance criteria.

`/review-work` and `$review-work` load `/litclaude:review-work` /
`Skill(review-work)`. For a draft plan, that route performs a read-only
objective-achievability audit and returns `PASS`, `ITERATE`, or `NEEDS-CONTEXT`
without implementing. For completed work it runs the 5-lane review: scope/diff
verification, tests/evidence execution, package/payload and code quality,
security/provenance, and real-surface/docs readiness. `/litgoal` and `$litgoal` load
`/litclaude:litgoal` / `Skill(litgoal)` for durable local goal state and
the litgoal CLI.

`recap`, `lit recap`, `litrecap`, `리캡`, and `$lit-recap` load
`/litclaude:lit-recap` / `Skill(lit-recap)` for a read-only session recap built
from `.litclaude/litgoal/goals.json`, `ledger.jsonl`, `brief.md`, and
current-session context. Recap activation is side-effect-free: it never writes
ledgers or files and forbids mutating `litgoal` subcommands. Near-miss tokens
(`recapture`, `recaptcha`, `리캡처`) and slash forms (`/lit-recap`, `/litrecap`,
`/lit recap`) do not activate the hook. The recap answers in Korean with fixed
headers by default, switches the body to English on `--en`, and emits the brief
`## ⚡ 요약` digest on `--brief` / `짧게`.

`lit-comprehend`, bare `comprehend`, `lit comprehend`, `$lit-comprehend`, and
`$comprehend` load `/litclaude:lit-comprehend` / `Skill(lit-comprehend)` for a self-contained explainer artifact aimed at
understanding rather than status: it anchors on what the reader already knew,
explains the intuition before the code, walks the change in conceptual order
instead of file order, offers an interactive micro-world, discloses what is
**not** verified, and closes with a quiz that shows where to slow down. The trigger surface is deliberately wider than the skill id, because bare
`comprehend` is what users actually type — the same reasoning that keeps bare
`recap` routed to `lit-recap`. Breadth is affordable only because **activation is
not execution**: with no explicit scope the skill states what it would explain and
waits, so a loose match costs one line rather than a multi-minute build. The
natural-language forms (`explain this`, `설명해줘`, `이해가 안 돼`) stay out of the
hook regardless; that judgement is left to Claude Code's own Skill-description
matching, which can weigh the whole prompt. `lit comprehend` and `litwork
comprehend` are matched ahead of the bare `lit`/`litwork` tokens so the phrase
reaches this skill instead of falling through to the generic lit cascade. Near-miss tokens (`comprehension`,
`incomprehensible`) and slash forms (`/comprehend`, `/lit-comprehend`,
`/litclaude:lit-comprehend`, `/lit comprehend`) do not activate the hook. The artifact is written **outside** the worktree to
`~/.litclaude/lit-comprehend/YYYY-MM-DD-<slug>.html`, is fully self-contained, and
must pass `scripts/verify-explainer.mjs` — which fails a phantom code quote, a
missing path, an external resource, a missing canonical section, a quiz with an
unfeedbacked option or a positional tell, and ASCII-art diagrams — before the
work may be reported as done.

`litresearch`, `$litresearch`, `lit research`, `lit search`, and `lit query`
load `/litclaude:litresearch` / `Skill(litresearch)` when the user asks for a
cited investigation. Search/query lanes prefer public APIs or feeds, validate
content before treating a fetch as evidence, keep a route trace, quarantine
fetched text as prompt-injection data, and stop honestly at authentication,
paywall, private-data, or credential boundaries. If the user requests read-only,
no-write, or transcript-only research, ask before creating
`.litclaude/litresearch/<slug>/` and otherwise keep the journal in the
transcript/TodoWrite. The guaranteed runtime surface is direct public URL reads
through `public_source_read` or `litclaude public-read`; its JSON includes
`fetchAttempts`, `fetchVerdict`, untried safe routes, a starter claim graph, and
`contentSafety` flags declaring fetched text untrusted and its instructions
ignored, so HTTP 200 is not treated as success without content validation. Dynamic
`Workflow`, `/deep-research`, browsing, and namespaced subagents are
host-dependent and need fallbacks.

## Safety

Hooks parse JSON from stdin and return JSON to Claude Code. The hook does not
execute prompt text and does not echo prompt text into the returned context;
prompt text is not executed or echoed. The litwork detector returns constant
workflow guidance, so a prompt cannot become a shell command through the hook
response. Malformed hook input and malformed litgoal JSON should surface a controlled error instead of a misleading success response.

## Local Smoke

Run the hook fixture directly:

```bash
node plugins/litclaude/bin/litclaude-hook.js user-prompt-submit < fixtures/hooks/user-prompt-litwork.json
```

Reload local plugin metadata in Claude Code after hook edits:

```text
/reload-plugins
```

## LIT mark and reply probe

SessionStart renders the standard 22×10 Ignition B mark and `litclaude vX.Y.Z` in
`systemMessage`. The existing locked per-session rule state retains a separate
ignition receipt through resume and compact resets. Missing session identity or
unsafe/unwritable state suppresses this decoration; normal context still loads.
Ignition uses the same discovered project root as rules and compaction, so moving
between nested directories and the root cannot create a second receipt. Invalid
or symlinked cwd and missing project roots also suppress decoration; unsafe root
state never falls back to a nested state directory.
A compact event never repeats the session mark.

Activated UserPromptSubmit routes render the plain mark
`🔥 LIT IGNITED · <discipline> 🔥` in `systemMessage`. Inactive prompts stay silent.
`additionalContext` requires the model's reply to begin with exactly one
`🔥 **LIT IGNITED · <discipline>** 🔥` line. The system mark and reply probe are
separate outputs; the model must not draw ASCII art or emit a second probe.

Hook JSON uses uncolored rows. CLI output uses the 44×20 Ignition B banner and
native product column inside the existing installer frame; `--help` and doctor
share that mark. The micro envelope remains 16×5, with eleven active columns.
Interactive terminals paint each symbol cell with orange `#FF6337`, lime
`#D7F75B`, or ivory `#F2EFDF` (fixed 256-color fallback: 203/191/230). The symbol
has no row gradient, extrusion, or forced background; product labels remain plain.
NO_COLOR and CI presence (including empty values), non-TTY, and JSON suppress ANSI.
A non-UTF-8 locale or TERM=dumb uses a plain `LIT` line. Functional install-step
colors and progress behavior retain their existing policy.

The plugin-local `lib/lit-mark.mjs` owns all three row exports and per-cell color
maps. The package CLI re-exports that module, and installed hooks import it from
their own plugin payload. `canonical-skill-resources.mjs` pins its bytes for the
installer and doctor. The test fixture `test/fixtures/litmark/ignition.json` pins
the selected rows independently; historical round6 fixtures no longer define the
current logo. `colorize()` accepts the three marks, native lockups with arbitrary
valid product labels, and their trimmed row copies; unrelated block rows use ivory.
The former `shadow` option is accepted but has no effect on this flat design.


### Design production skills

Leading `frontend-ui-ux` / `$frontend-ui-ux` and the existing interface-intent route
supply action guidance: implement an authorized build, resolve material ambiguity,
then inspect actual renders. The evolving beta2 contract supports execution; it is
not a contract-only stopping point. Review-only and plan-only requests remain read-only.
PostToolUse reminders never widen the requested design scope.

Leading `readme-studio` / `$readme-studio` selects the plugin's README skill and embeds
its body. Incidental, quoted and near-miss tokens do not enroll that route. There is
no dedicated command file, new hook event or bundled image-generation service.
Actual Claude Code capability governs generation; absent tools produce
`IMAGE_GENERATION_UNAVAILABLE`, followed by composition from a supplied inspected
background if available. Integrity checks include its nested templates and helpers.
