Design covering findings 1/2/3 — shell injection for project.md override, shared-vs-personal gitignore handling, spire:init skill modelled on tome:init, and adept scaffolding. Ready for plan.
100 lines
5.3 KiB
Markdown
100 lines
5.3 KiB
Markdown
# Design: Spire project override + spire:init
|
|
|
|
**Date**: 2026-09-17
|
|
**Covers**: Findings 1 (no spire:init), 2 (project.md cannot be overridden), 3 (adept scaffolding not shipped)
|
|
**Source research**: `research/2026-09-17-project-md-override-mechanics.md`
|
|
**Goal**: `goals/igor-project-adoption.md`
|
|
|
|
## The problem
|
|
|
|
Igor and Athanor hard-bind `references/project.md` via `@` directives that resolve into the plugin cache. Every project gets the blank template. There is no init, no override, and no adept scaffolding — three gaps that compound into "every session rediscovers the project from scratch."
|
|
|
|
## Solution: three new pieces
|
|
|
|
### 1. `spire:project` skill (the injection vehicle)
|
|
|
|
A tiny skill whose only job is loading the right `project.md` at skill-load time via shell injection:
|
|
|
|
```markdown
|
|
---
|
|
name: project
|
|
description: Loads per-project Spire configuration (override or bundled template).
|
|
allowed-tools: Bash(cat:*)
|
|
---
|
|
|
|
!`cat "${CLAUDE_PROJECT_DIR}/.claude/spire/project.md" 2>/dev/null || cat "${CLAUDE_SKILL_DIR}/../igor/references/project.md"`
|
|
|
|
<!-- If the above block is the blank template, run /spire:init to configure this project. -->
|
|
```
|
|
|
|
**Igor** (`agents/igor.md`) and **Athanor** (`agents/athanor.md`) add `spire:project` to their `skills:` list and drop their `@` imports. The skill loads at agent startup — same timing as today's `@`, but with fallback.
|
|
|
|
Runtime belt: Igor's skill text keeps a one-line instruction — "if the project block is the blank template, try reading `.claude/spire/project.md` before orienting from scratch" — so it degrades gracefully if `disableSkillShellExecution` is on.
|
|
|
|
### 2. Shared vs. personal installs
|
|
|
|
`.claude/` is often committed to git. But Spire may be personal — installed via `settings.local.json` or user-level settings — in which case `.claude/spire/` should not leak into the project's history.
|
|
|
|
**The rule**: `spire:init` detects where Spire is enabled:
|
|
|
|
| Spire enabled in | `.claude/spire/` should be | Init action |
|
|
|---|---|---|
|
|
| `settings.json` (project-level, committed) | Committed with the project | Write files, commit |
|
|
| `settings.local.json` or user settings | Gitignored | Write files, add `/spire/` to `.claude/.gitignore`, do not commit |
|
|
| Not detected / ambiguous | Ask the user | Prompt: "Should the Spire config be shared with the project or personal?" |
|
|
|
|
Detection: check whether `"spire@reliquary"` (or any spire plugin ref) appears in `.claude/settings.json` vs. `.claude/settings.local.json`. If in `settings.json` → shared. If in `settings.local.json` or absent from both → personal/ask.
|
|
|
|
The `.claude/.gitignore` approach (a gitignore inside `.claude/`) keeps the ignore scoped — it only affects `.claude/spire/`, not the rest of `.claude/`.
|
|
|
|
A `project.local.md` sibling can follow later for personal overrides on top of shared config — but that's out of scope for the first pass.
|
|
|
|
### 3. `spire:init` skill
|
|
|
|
Human-triggered (`disable-model-invocation: true`). Modelled on `tome:init` Phase 2.1.
|
|
|
|
**Phases**:
|
|
|
|
1. **Detect** — read the project to auto-fill what we can:
|
|
- Workpad location via the `spire:workpad` resolution chain
|
|
- Pinned agents from `.claude/agents/*.md`
|
|
- Verification commands from `Justfile` (recipe names like `check`, `test`, `lint`, `build`), `Makefile`, `package.json` scripts, `Cargo.toml`
|
|
- Conventions from root `CLAUDE.md`
|
|
- Tome skill presence (check if a `*-tome:navigate` skill exists or `tome@reliquary` is enabled)
|
|
|
|
2. **Scaffold** — create the following:
|
|
- `.claude/spire/project.md` — filled template with `status: generated` front matter (re-init safe: only overwrite while status is `generated`, never clobber `authored`)
|
|
- `<workpad>/adepts/_template/CLAUDE.md` — the adept CLAUDE.md template that `agents/adept.md:23,26` references but doesn't ship
|
|
- `<workpad>/adepts/` directory (if missing)
|
|
|
|
3. **Gitignore** (if personal install) — add `spire/` to `.claude/.gitignore`
|
|
|
|
4. **Alias** (optional) — offer to create `.claude/skills/assistant/SKILL.md` that invokes `spire:igor` as `/assistant`, so the project can use a generic entry point
|
|
|
|
5. **Commit** (if shared install) — commit the scaffolded files with gitmoji
|
|
|
|
### 4. Adept template
|
|
|
|
The adept agent (`agents/adept.md`) references `<workpad>/adepts/_template/CLAUDE.md` and `<workpad>/adepts/CLAUDE.md` — neither exists. `spire:init` creates both:
|
|
|
|
- `adepts/CLAUDE.md` — root-level orientation for the adepts directory (what it is, how adepts use it)
|
|
- `adepts/_template/CLAUDE.md` — the default starting CLAUDE.md for a newly spawned adept's domain folder
|
|
|
|
These are `status: generated` and re-init safe.
|
|
|
|
### 5. Changes to existing files
|
|
|
|
| File | Change |
|
|
|---|---|
|
|
| `skills/igor/SKILL.md:32` | Remove `@references/project.md`. Add a one-line runtime fallback instruction. |
|
|
| `agents/igor.md` | Add `spire:project` to `skills:` list |
|
|
| `agents/athanor.md:25` | Remove `@skills/igor/references/project.md`. Add `spire:project` to `skills:` list |
|
|
| `references/project.md` | Add "run `/spire:init`" hint at top. Keep as fallback template. |
|
|
|
|
### 6. Backward compatibility
|
|
|
|
- No `.claude/spire/project.md` → shell injection falls back to bundled template → same as today
|
|
- Template gains a hint to run init, turning the dead-end into a prompt
|
|
- `disableSkillShellExecution` → runtime fallback instruction kicks in
|
|
- `status: generated` front matter protects hand-edits from re-init
|