Skip to content

Latest commit

 

History

History
258 lines (201 loc) · 11.6 KB

File metadata and controls

258 lines (201 loc) · 11.6 KB

Implementation — SPEAR Plugin

Date: 2026-04-23 Status: Bootstrap (will be regenerated by /spear:init at v0.1.0) Owner: BadgersMC

1. Repo layout (canonical)

Reflects the design §3.1 layout with the §9 amendment (single polyglot hook script, no .ps1).

spear-plugin/
├── .claude-plugin/
│   └── marketplace.json
├── plugins/
│   └── spear/
│       ├── .claude-plugin/
│       │   └── plugin.json
│       ├── skills/
│       │   ├── using-spear/SKILL.md
│       │   ├── init/SKILL.md
│       │   ├── spec/SKILL.md
│       │   ├── prove/SKILL.md
│       │   ├── engine/SKILL.md
│       │   ├── arch/SKILL.md
│       │   └── refine/SKILL.md
│       ├── hooks/
│       │   ├── hooks.json
│       │   ├── run-hook.cmd          # polyglot wrapper (cmd.exe + bash)
│       │   ├── session-start         # extensionless POSIX bash
│       │   └── lib/                  # shared bash helpers (state.sh, probe.sh)
│       ├── templates/
│       │   ├── tech-stack.md
│       │   ├── requirements.md
│       │   ├── implementation.md
│       │   ├── tasks.md
│       │   └── LayerRulesTest.kt
│       └── migrations/
│           └── README.md             # placeholder; first migration ships when schema bumps
├── tests/
│   ├── hooks/                        # Bats fixtures + driver
│   │   ├── fixtures/
│   │   │   ├── idle/
│   │   │   ├── prove/
│   │   │   ├── engine/
│   │   │   └── stale-state/          # includes fake gradle on PATH
│   │   └── *.bats
│   ├── skills/                       # Node skill-content lint
│   │   └── lint.mjs
│   ├── state/                        # Node state-machine simulator
│   │   ├── state-machine.test.mjs
│   │   └── helpers.mjs
│   └── fixtures/
│       └── layer-violation/          # canonical Kotlin layer-violation
├── .github/workflows/ci.yml
├── docs/
│   ├── design/
│   ├── tech-stack.md
│   ├── requirements.md
│   ├── implementation.md
│   └── tasks.md
├── .gitignore
├── README.md
├── CONTRIBUTING.md
└── TESTING.md

2. Layer Dependency Rules

Even though this plugin has no compiled JVM domain code, the SPEAR layering principle is mapped onto the artifact types. spear:arch does not run against this repo's own files (the repo ships the rules; it does not consume them). The mapping below is descriptive and used by reviewers.

"Layer" Concrete files May depend on
domain/ (rules-of-the-game) docs/requirements.md, docs/tech-stack.md nothing
application/ (workflow prose) plugins/spear/skills/**/SKILL.md, plugins/spear/templates/** domain/ only — skills cite REQ-IDs but contain no script paths
infrastructure/ (executable shell + JS) plugins/spear/hooks/**, tests/**, .github/workflows/** anything

A skill SKILL.md referencing a hook script path by absolute path (e.g. embedding ${CLAUDE_PLUGIN_ROOT}/hooks/... in skill prose) is a layer violation: skills speak in terms of REQ-IDs and procedural intent; the hook layer owns paths. Reviewers enforce this; CI does not (no AST to scan in markdown).

Forbidden Domain Annotations

(Empty — this plugin has no JVM domain code. Section retained because the spear:init template emits an empty section in this exact shape, and the meta-test in REQ-121 will diff against this file.)

# Add JVM-style framework package globs here when SPEAR is applied to a JVM project.
forbidden: []

3. Component design

3.1 Marketplace manifest (.claude-plugin/marketplace.json)

Schema reference: context7 /websites/code_claude_en_plugins-reference + superpowers@5.0.4 example. Required fields: name, owner, plugins[]. Each plugins[] entry needs at minimum name, description, version, source. source: "./plugins/spear" because the plugin lives at that path.

3.2 Plugin manifest (plugins/spear/.claude-plugin/plugin.json)

Required field: name. Carry version here (per docs, plugin.json takes priority over marketplace entry when both set). Carry description, author, repository, license, keywords, and the hooks pointer "./hooks/hooks.json". Skills are auto-discovered under ./skills/.

3.3 Hook configuration (plugins/spear/hooks/hooks.json)

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start",
            "async": false
          }
        ]
      }
    ]
  }
}

Mirrors superpowers@5.0.4 exactly. async: false ensures stdout reaches the session before the first user turn.

3.4 Polyglot wrapper (plugins/spear/hooks/run-hook.cmd)

Mirrors superpowers/hooks/run-hook.cmd byte-for-byte (modulo plugin-name comments). Rationale: Claude Code on Windows auto-prepends bash to any command containing .sh, so we use extensionless POSIX scripts and a .cmd dispatcher; the .cmd is also a valid bash script via the : << 'CMDBLOCK' … CMDBLOCK heredoc trick.

3.5 Session-start script (plugins/spear/hooks/session-start)

POSIX bash, set -euo pipefail. Responsibilities (REQ-080..REQ-085, REQ-049, REQ-050, REQ-021):

1. Resolve PLUGIN_ROOT and PROJECT_ROOT (PROJECT_ROOT = $PWD).
2. If PROJECT_ROOT lacks both docs/requirements.md and docs/tasks.md → emit short notice, exit 0.
3. Run any pending state-file migrations from plugins/spear/migrations/.
4. Read .claude/spear-state.json if present.
5. If state.testFile is set: re-run the test; reconcile state.testStatus.
6. Probe for context7 / mgrep / semgrep (presence-only check via env / known cache paths).
7. Read using-spear/SKILL.md body.
8. Compose payload: cycle rules + probe results + current task summary + current phase + deferral list.
9. Truncate per REQ-014 if payload approaches 4096 bytes.
10. Escape for JSON (mirroring escape_for_json from superpowers).
11. Emit hookSpecificOutput.additionalContext (Claude Code) or additional_context (Cursor).
12. Exit 0 even on internal error (REQ-085).

Shared bash helpers go in hooks/lib/ so each helper is unit-testable in isolation by Bats (see §4.2).

3.6 State helpers (plugins/spear/hooks/lib/state.sh)

Functions exposed to skills (skills shell out to these via ${CLAUDE_PLUGIN_ROOT}/hooks/lib/state.sh <fn> <args>):

  • state_read — print current phase, currentTaskId, reqId as JSON.
  • state_assert_phase <expected> — exit 1 with REQ-043 message if mismatch.
  • state_set_phase <new> — atomic write of .claude/spear-state.json.
  • state_record_test <file> <name> <status> — used by prove/engine.
  • state_clear — REQ-048 reset.

All jq-backed for atomicity. Atomic write = write to .claude/spear-state.json.tmp then mv.

3.7 Skills (plugins/spear/skills/<name>/SKILL.md)

Each skill is a markdown file with YAML frontmatter:

---
name: spear:<name>
description: <one-line trigger>
---

The body describes the procedure in prose. Where state mutation is required, the skill instructs the agent to invoke ${CLAUDE_PLUGIN_ROOT}/hooks/lib/state.sh <fn> via the Bash tool. Skills do not re-implement state logic.

using-spear/SKILL.md is special: its body is the canonical session-start payload and is pulled by the hook script. It must stay under 4096 bytes (REQ-013).

3.8 Templates (plugins/spear/templates/)

Five files, used only by spear:init. Each is a literal copy of the v0.1.0-bootstrap form of the four SPEAR docs plus the Konsist template. The tasks.md template is parameterised on the detected language (Kotlin vs TS vs Py vs Go vs Rust) — language-specific blocks are gated by {{#if jvm}} … {{/if}} style conditionals processed by a small bash templater (hooks/lib/template.sh).

3.9 Migrations (plugins/spear/migrations/)

Empty for v1.0.0 (schema starts at version 1). When the schema bumps, a 1-to-2.sh script lands here that takes a state-file path, transforms it, writes it back. The session-start hook iterates migrations/ in lex order and runs any whose source version matches the current state file's version field.

4. Data flows

4.1 Session start

Claude Code starts
  → fires SessionStart hook
  → run-hook.cmd dispatches session-start (bash)
    → migrate state if needed
    → reconcile testStatus
    → probe optional tools
    → emit JSON to stdout
  → Claude Code injects additionalContext into session
  → agent sees "using-spear" and tool probe results

4.2 TDD task cycle

agent invokes /spear:spec
  → reads requirements.md, picks REQ-ID
  → drafts tasks.md entry with References + Evidence
  → state.sh state_set_phase spec-done

agent invokes /spear:prove
  → state.sh state_assert_phase spec-done
  → writes failing test, runs it
  → import-diff vs Evidence (REQ-031)
  → state.sh state_record_test <file> <name> red
  → state.sh state_set_phase prove-done

agent invokes /spear:engine
  → state.sh state_assert_phase prove-done
  → writes minimum impl, runs test
  → import-diff vs Evidence
  → state.sh state_record_test <file> <name> green
  → state.sh state_set_phase engine-done

agent invokes /spear:arch
  → state.sh state_assert_phase engine-done
  → scans imports in changed files vs Layer Dependency Rules
  → checks denylist
  → state.sh state_set_phase arch-done

agent invokes /spear:refine
  → state.sh state_assert_phase arch-done
  → refactor + full suite green
  → marks task [x] in tasks.md
  → state.sh state_clear

4.3 DOC/INFRA path

spec → spec-done then directly to arch → arch-done → refine → idle. The spec-done → arch edge is added explicitly (REQ-045) so the same arch skill code does not need to know task tag — it asserts phase ∈ {engine-done, spec-done}.

5. Briefing contract for subagent dispatch (this project's own usage)

When Opus dispatches Sonnet/Haiku workers to write hook scripts, skill bodies, or test files for this plugin, every Agent tool prompt carries the design §5.3 fields:

  • Files to create/modify: absolute paths.
  • Pre-verified signatures: for hook helpers, the bash function signatures from §3.6; for skill markdown, the frontmatter shape from §3.7.
  • Failing test: path + test name (when TDD task).
  • Acceptance criteria: which Bats / Node test must go green; "no other files changed".
  • Forbidden actions: "do not edit any file outside the listed paths"; "do not add error handling not asserted by the test"; "do not invent fields not in plugin.json schema".
  • Evidence block: copied verbatim from the task in tasks.md.

Worker model picked per design §5.3 table: Sonnet for prove/refine; Haiku for engine when test fully constrains; Opus retained for arch and spec.

6. Versioning

Plugin starts at 0.1.0 (bootstrap milestone). Bumps per semver. State-file schema starts at version: 1; bump only on a non-additive change.

7. Out of scope (this doc)

  • Per-skill prose bodies — owned by plugins/spear/skills/**/SKILL.md. Architecture only specifies the contract (frontmatter shape, where state I/O goes).
  • CI YAML — owned by .github/workflows/ci.yml. Architecture only specifies which jobs exist (§7 of tech-stack.md).
  • Konsist test internals — owned by plugins/spear/templates/LayerRulesTest.kt. This doc only states it must reflect the layer map.