Skip to content

Package Architecture ​

Spexso ships as an npm package that, when run with npx spexso init, scaffolds the entire Claude Code integration into the target repository. No global install needed — each project gets its own copy of the commands and agents.

What npx spexso init creates ​

your-project/
├── .claude/
│   ├── commands/                     # Slash commands (22 total)
│   │   ├── spexso-kickstart.md       # /spexso-kickstart
│   │   ├── spexso-wizard.md          # /spexso-wizard
│   │   ├── spexso-architecture.md    # /spexso-architecture
│   │   ├── spexso-brainstorm.md      # /spexso-brainstorm
│   │   ├── spexso-start.md           # /spexso-start
│   │   ├── spexso-plan.md            # /spexso-plan
│   │   ├── spexso-build.md           # /spexso-build
│   │   ├── spexso-review.md          # /spexso-review
│   │   ├── spexso-finish.md          # /spexso-finish
│   │   ├── spexso-sync.md            # /spexso-sync
│   │   └── …                         # + 11 more — see Complete Command Reference
│   ├── agents/                       # Custom subagents (6 total)
│   │   ├── spexso-planner.md         # Breaks spec into tasks
│   │   ├── spexso-builder.md         # TDD implementation agent
│   │   ├── spexso-reviewer.md        # Code review with severity
│   │   ├── spexso-tester.md          # Test-first generation agent
│   │   ├── spexso-researcher.md      # Competitive research (/spexso-tinder)
│   │   └── spexso-issue-runner.md    # Per-issue sequencer (/spexso-marathon)
│   └── settings.json                 # Permissions + hooks (see below)
├── .spexso/
│   ├── index.json                    # Auto-generated issue cache
│   ├── config.json                   # Provider config, models, budgets — see Storage
│   ├── rules/                        # Domain rulebooks — baseline + local pair
│   │   ├── doctrine.md               # Injected every session via SessionStart hook
│   │   ├── typescript.md, cap.md, ui5.md, ux.md, test.md, abap.md   # spexso's, refreshed on init
│   │   ├── *.local.md                # yours — additive, committed, never touched by spexso
│   │   ├── .baselines.json           # which baseline was installed (drives the refresh)
│   └── templates/                    # GitLab issue templates + registries
│       ├── spec-full.md              # M/L/XL spec template
│       ├── spec-lite.md              # XS/S lite spec template
│       ├── bug-report.md             # Bug template
│       ├── constitution.md           # Seeds .spexso/constitution.md
│       ├── btp-services.md           # BTP service-binding registry
│       ├── tenant-requirements.md    # Multi-tenant scope registry
│       └── learning-card.md          # Seeds docs/learning/ on /spexso-finish
├── AGENTS.md                         # Canonical instructions (Spexso section between spexso:start/end markers)
├── CLAUDE.md                         # @AGENTS.md import shim
└── .gitignore                        # runtime state under .spexso/ (index.json, worktrees/, plans/, marathon-log, tmp-*, demo-artifacts/, …) — added by init and update

Additionally, npx spexso init:

  • Creates all GitLab scoped labels (stage::*, type::*, size::*, priority::*, platform::*, ai::*, needs::*) via npx spexso label create — idempotent, skips labels that already exist
  • Checks if the recommended Claude Code plugin (sap-skills) is installed and shows install commands if missing — advisory only, never blocking
  • Tells the user to run /spexso-setup as the next step

The file half of init lives in lib/install.js and is also exposed alone as npx spexso update: offline, no prompts, for after an npm upgrade. It re-copies the spexso-owned files, refreshes the marked Spexso section of AGENTS.md, re-stamps per-agent models (agents sync), adds any missing spexso entries to .gitignore and writes .spexso/version. The network steps (labels, boards, sync) and project files (config.json, README, CHANGELOG, CLAUDE.md) are init-only. See onboarding.

Key design principle

Spexso keeps one small config file — .spexso/config.json (provider, target branch, models, budgets, parallel switches) — and convention for everything else. It is not user-tuned prose; it's a small, code-read settings file, distinct from the slash commands and agent definitions that carry the actual process.

.claude/settings.json also ships a UserPromptSubmit hook (spexso title) that names the session after the issue a /spexso-<cmd> <id> prompt works on — #1473 Connector Actions run history, the title taken from .spexso/index.json — so parallel terminals can be told apart. It sets the title through the hook output's sessionTitle field; free-form prompts print nothing and keep Claude Code's own automatic title. Claude Code accepts sessionTitle only from UserPromptSubmit and SessionStart, so a yolo chain moving from stage to stage can't update it: the title names the issue, never the stage. (Writing terminal-title escape codes from a hook does not work on Windows, where the hook's console is hidden.)

Beside it are two more hook sets: SessionStart + SubagentStart synchronously inject the doctrine rulebook (spexso doctrine — its stdout IS the injected context) so every session/subagent sees it without CLAUDE.md bloat, and fail-open async observe hooks on SubagentStart / PostToolUse / SubagentStop / Stop each pipe to spexso observe, appending the run's event trace to .spexso/observability/runs/ — see Observability. Hooks invoke the CLI directly — node "${CLAUDE_PROJECT_DIR}/node_modules/@flexso/spexso/bin/spexso.js" … — never via npx: the doctrine hook sits on the critical path of every subagent spawn and the observe hook fires on every tool call, so the npx resolution overhead (hundreds of ms per invocation on Windows) would be paid hundreds of times per issue.

npm Package Structure ​

spexso/
├── bin/
│   └── spexso.js                     # CLI entry point
├── src/                              # CANONICAL — humans edit only here
│   ├── skills/<name>/SKILL.md        # 22 workflow command definitions
│   ├── agents/                       # 6 subagent definitions
│   ├── rules/                        # Shipped domain rulebooks
│   ├── templates/                    # Spec templates
│   ├── snippets/AGENTS-snippet.md    # Spexso section for AGENTS.md (canonical; CLAUDE.md = @AGENTS.md shim)
│   └── policy/settings.json          # Claude Code permissions
├── dist/                             # GENERATED, COMMITTED — never hand-edited
│   ├── claude-code/                  # commands/, agents/, settings.json, snippet
│   ├── opencode/                     # commands/, agents/ (provider/model-ids), opencode.spexso.json
│   ├── codex/                        # skills/ (.agents/skills spec path), agents/*.toml,
│   │                                 #   hooks.json (deny hook), config.spexso.toml, AGENTS-append.md
│   └── shared/                       # rules/, templates/ (harness-independent)
├── scripts/build.js                  # src → dist renderer (exports render())
├── scaffold/
│   └── cicd/                         # CI/CD templates, shipped verbatim
├── lib/
│   ├── init.js                       # Install logic (copies from dist/)
│   ├── sync.js                       # tracker adapter → index.json sync
│   └── facade.js                     # `spexso <verb>` façade → platform adapters
└── package.json

dist/ is committed on purpose: spexso init stays a plain file copy with no build-at-install, and the npm package ships ready-made artifacts. test/dist-staleness.test.js regenerates every artifact in memory and byte-compares against the committed file — hand-editing dist/ fails CI within one push.

Harness targets ​

spexso init --harness <claude-code|opencode|codex|all> selects which harness artifacts install (default: claude-code; opencode/codex are auto-added when the project already has .opencode/opencode.json or .codex). Prose bodies are verbatim across harnesses (superpowers' rule) — only frontmatter and placement differ, and scripts/check-canaries.js asserts the load-bearing sentences survive into every rendered artifact:

FeatureClaude CodeopencodeCodex
Commands.claude/commands/*.md, /name.opencode/commands/*.md (same body + description frontmatter), /nameAgent Skills at .agents/skills/<name>/SKILL.md (spec path), $name; AGENTS.md index as discoverability floor
Subagents.claude/agents/*.md, model tier as-is (opus/sonnet).opencode/agents/*.md, mode: subagent + provider model-id from src/policy/models.json.codex/agents/*.toml, model-id from the same map; depth cap 1 fits orchestrator→worker
Permissions.claude/settings.json allow/deny arrayspermission map deep-merged into opencode.json — deny rules emitted last (last-match-wins); user keys always winNo per-command allowlist — spexso guard, a PreToolUse command hook in .codex/hooks.json, exits 2 on a deny-list match; allow-list is advisory
Config—merged opencode.jsonspexso sections appended to .codex/config.toml (marker-guarded, incl. the spexso-yolo profile for headless marathon)
Doctrine railSessionStart/SubagentStart hooks inject via stdoutinstructions: [".spexso/rules/doctrine.md"] in the merged opencode.json — loaded live every session (subagents don't get a re-injection)same hooks as Claude Code in .codex/hooks.json (spexso doctrine --hook <event>)
Observe railhooks on SubagentStart/PostToolUse/SubagentStop/Stop.opencode/plugins/spexso-observe.js on tool.execute.before — tool events only, no subagent spans/phasessame hooks as Claude Code in .codex/hooks.json
Rules/templates.spexso/ (harness-independent)samesame
Safety backstopCLI-side refusals (npx spexso verbs)samesame — harness-independent

spexso init prints one rails line per non-Claude harness saying which rails are active vs unavailable there — a degraded rail is announced, never silent. Per-rail detail: Observability → Harness support.

Codex caveats (documented degradations): all rails ride the hooks system — the user must trust the generated hooks once via /hooks, and the Phase 3 parity suite's P5 probe (force-push denial) stays the mandatory gate before relying on the deny rail; the model ids in src/policy/models.json are tunable defaults.