Contributing to Session Orchestrator
July 19, 2026 · View on GitHub
This guide explains how to extend and modify the Session Orchestrator plugin. It covers the plugin architecture, conventions, and step-by-step instructions for adding new components.
Table of Contents
- Overview
- Development Setup
- Plugin Architecture
- Skill Anatomy
- Adding a New Skill
- Adding a New Command
- Adding a New Agent
- Modifying Existing Skills
- Session Config Convention
- Label Taxonomy
- Platform Abstraction
- Pull Request Guidelines
- Good First Contribution Areas
- Code of Conduct
Overview
Session Orchestrator is a multi-harness local orchestration plugin that adds session-level orchestration to any project. It works with Claude Code, Codex CLI, Cursor IDE, and Pi — the same skills and commands across all four, with platform-adapted hooks and enforcement. It manages wave planning, VCS integration (GitHub and GitLab), parallel subagent dispatch, and quality gates. For user-facing documentation, see README.md and User Guide; the full component inventory lives in docs/components.md.
How it works:
- The plugin is loaded by the harness at startup. Claude Code uses its marketplace flow; Codex uses the public
codex plugin marketplace add/codex plugin addlifecycle throughscripts/codex-install.mjs; Cursor IDE and Pi use their installers underscripts/. There is no build or compilation step. - Skill, command, and agent surfaces are Markdown or JSON files the harness reads directly. Scripts, hooks, installers, and tests are Node ESM (
.mjs) and run on Node.js 24+. - You extend the plugin by editing or adding files. Changes take effect the next time the harness loads the plugin.
The user workflow is:
/session [type] --> Research --> Q&A --> Plan --> /go --> 5 Waves --> /close --> Verify --> Commit
Development Setup
-
Clone the repository:
git clone https://github.com/Kanevry/session-orchestrator.git -
Install dependencies (requires Node.js 24 or later — check with
node --version):cd session-orchestrator && npm installThis repo is npm-canonical (
package-lock.jsonis the committed lockfile) — always usenpm, neverpnpmoryarn. The runtime dependencies (zx,ajv,remark, …) are required by the hooks and scripts. -
Validate your environment:
npm test # full vitest suite npm run lint # ESLint npm run typecheck # typecheck node scripts/validate-plugin.mjs # optional: structural plugin validation -
Install as a local plugin — run these slash commands inside a Claude Code session (not in your shell):
/plugin marketplace add /absolute/path/to/your/clone /plugin install session-orchestrator@kanevryUse the absolute path to your clone. After the install confirmation, reload Claude Code so the commands register.
For Codex CLI, run
node scripts/codex-install.mjs; it validates the tracked contract, drives the public marketplace/add/list commands, and leaves hook trust to a fresh task plus/hooks. Usenode scripts/cursor-install.mjs /path/to/projectfor Cursor IDE andnode scripts/pi-install.mjs /path/to/project --settings-onlyfor Pi (setup guides underdocs/). -
Test your changes:
Open any project repository and run
/session feature. This invokes the full session flow and exercises most plugin components.For targeted testing:
/session housekeeping-- tests the lightweight session path/go-- tests wave execution (requires an active session plan)/close-- tests session close-out and verification/discovery [scope]-- tests systematic quality discovery/plan new-- tests structured project planning and PRD generation
There is no build or compilation step — edit the files, reload the harness, and test. Dependency installation (npm install) is required once after cloning and again whenever package-lock.json changes.
Git Blame Hygiene
Run once after cloning: git config blame.ignoreRevsFile .git-blame-ignore-revs
Skips large mechanical sweeps (import-alias rollouts, mass migrations) in
git blame — see .git-blame-ignore-revs for the current list.
Plugin Architecture
The plugin has four component types, each in its own directory:
Skills (skills/<name>/SKILL.md)
Skills are the core logic units. Each skill is a Markdown file with YAML frontmatter that provides instructions to Claude.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Unique identifier for the skill |
description | Yes | Multi-line description of what the skill does |
Key characteristics:
- Loaded by the harness when invoked by commands or other skills
- Self-contained -- each skill includes all the context it needs to operate
- Skills can invoke other skills (e.g.,
session-startinvokessession-plan) - A skill directory may contain supporting files (e.g.,
soul.mdalongsideSKILL.md)
Current skills: see the full inventory in docs/components.md — the single source of truth for skill, command, agent, and hook counts.
Commands (commands/<name>.md)
Commands are user-facing entry points that map slash commands to skill invocations.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
description | Yes | Shown in Claude Code's command list |
allowed-tools | Yes | List of tools the command can use, or "*" for all |
argument-hint | No | Shown as placeholder text (e.g., [housekeeping|feature|deep]) |
Key characteristics:
- The
$ARGUMENTSvariable receives whatever the user types after the command name - Commands should be thin wrappers -- delegate logic to skills
- Each command maps to one primary skill
Current commands: see docs/components.md for the full slash-command list.
Agents (agents/<name>.md)
Agents are subagent definitions dispatched by the wave-executor during parallel execution.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Agent identifier |
description | Yes | Must include <example> blocks showing invocation context |
model | Yes | Model to use (e.g., sonnet) |
color | Yes | Terminal color for agent output (e.g., cyan) |
tools | Yes | List of tools the agent can access (e.g., ["Read", "Grep"]) |
Key characteristics:
- Agents run with zero prior context -- they receive only the prompt from wave-executor
- Must be fully self-contained: include all review criteria, output formats, and instructions
- The
<example>blocks in the description are required by Claude Code for agent dispatch
Current agents: see docs/components.md for the full roster. The authoritative authoring spec — frontmatter contract, sandbox-tier, output-schema, validation commands — is agents/AGENTS.md.
Hooks (hooks/hooks.json)
Hooks fire on specific harness events. The plugin currently wires 10 hook event types (see docs/components.md for the full list); hooks/hooks.json is the Claude Code wiring, with platform variants in hooks/hooks-codex.json, hooks/hooks-cursor.json, and hooks/hooks-pi.json.
Format: JSON following the Claude Code hooks specification. Hook implementations are Node ESM (.mjs) files invoked through the hooks/run-node.sh shim. Excerpt from the real hooks/hooks.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|clear|compact",
"hooks": [
{
"type": "command",
"command": "sh \"$CLAUDE_PLUGIN_ROOT/hooks/run-node.sh\" \"$CLAUDE_PLUGIN_ROOT/hooks/on-session-start.mjs\"",
"timeout": 5,
"async": true
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "sh \"$CLAUDE_PLUGIN_ROOT/hooks/run-node.sh\" \"$CLAUDE_PLUGIN_ROOT/hooks/pre-bash-destructive-guard.mjs\"",
"timeout": 5
}
]
}
]
}
}
Current hook wiring (excerpt — the full 10-event-type Claude inventory is in docs/components.md):
SessionStart— version banner + session initialization (hooks/on-session-start.mjs)PreToolUse(Edit/Write) — scope enforcement viahooks/enforce-scope.mjsPreToolUse(Bash) — destructive-command guard (hooks/pre-bash-destructive-guard.mjs), command restrictions (hooks/enforce-commands.mjs), templates-first gate, staging fence, and more
Codex deliberately exposes only the six validated project event slots in hooks/hooks-codex.json: SessionStart, PreToolUse, PostToolUse, SubagentStart, SubagentStop, and Stop. Claude-only events are absent because Codex does not support them as project events. The Claude Edit/Write handlers are also absent because Codex supplies canonical apply_patch payloads; do not wire those handlers until a real adapter translates that payload into the contract they enforce.
Codex hook commands must use native ${PLUGIN_ROOT} and the exact wrapper shape validated by scripts/lib/codex/plugin-contract.mjs: set SO_PLATFORM=codex, export CODEX_PLUGIN_ROOT="${PLUGIN_ROOT}" for shared compatibility code, then invoke hooks/run-node.sh. Plugin installation and hook trust are independent: contributors must test in a fresh Codex task and review /hooks; installers must never write or bypass trust state.
Skill Anatomy
Skills are the most common thing you will add or modify. Here is a detailed breakdown.
Structure
---
name: my-skill
description: >
Multi-line description of what the skill does
and when it should be invoked. This text appears
in Claude Code's skill listing.
---
# Skill Title
## Section 1: Setup / Prerequisites
Instructions for initial checks, config reading, etc.
## Section 2: Core Logic
The main work the skill performs. Be specific about
which tools to use, what commands to run, and what
output to produce.
## Section 3: Output Format
Define the exact format for the skill's output.
Use code blocks for structured output templates.
Writing Principles
-
Skills are instructions for Claude, not documentation for humans. Write them as directives: "Run this command", "Check this condition", "Output this format".
-
Be specific. Include exact CLI commands, file paths, tool names, and expected outputs. Do not say "check the git state" -- say "run
git status --shortandgit log origin/main..HEAD --oneline". -
Include acceptance criteria. Each major step should define what "done" looks like. For example: "Pipeline status must be green (exit code 0). If red, report the failure and pause."
-
Reference Session Config for configurable behavior. If a value might differ across repos (number of agents, VCS type, CLI tools), read it from Session Config rather than hardcoding it.
-
Use tables for structured data. Config fields, CLI commands, label mappings -- tables make these scannable.
-
Degrade gracefully. If a config field is missing or a tool is unavailable, skip the relevant section and note it in the output. Do not fail the entire skill.
Example: Minimal Skill
---
name: lint-check
description: >
Runs project linters and reports findings. Invoked
during wave-executor quality checks.
---
# Lint Check
## Step 1: Detect Linter
Check `package.json` for lint scripts:
- If `scripts.lint` exists: use `pnpm lint`
- If `scripts.eslint` exists: use `pnpm eslint .`
- Otherwise: report "No linter configured" and exit
## Step 2: Run Linter
```bash
pnpm lint 2>&1 | tail -50
Capture the exit code. If non-zero, parse the output for error count and file locations.
Step 3: Output
## Lint Report
- **Status**: PASS / FAIL
- **Errors**: [count]
- **Warnings**: [count]
- **Files**: [list of files with errors]
## Adding a New Skill
1. **Create the skill directory and file:**
skills/my-skill/SKILL.md
2. **Add YAML frontmatter** with `name` and `description`:
```yaml
---
name: my-skill
description: >
What this skill does and when it should be invoked.
---
-
Write the skill instructions. Follow the principles in Skill Anatomy. Structure the skill in numbered phases or steps.
-
Add supporting files if needed. Place them in the same directory (e.g.,
skills/my-skill/templates.md). Reference them from SKILL.md with instructions like "Readtemplates.mdin this skill directory." -
If the skill should be user-invocable, create a command (see Adding a New Command).
-
Test the skill by invoking it in a project repo. If it is called by another skill, trigger the parent skill and verify the chain works end to end.
Adding a New Command
-
Create the command file:
commands/my-command.md -
Add frontmatter:
--- description: One-line description shown in Claude Code's command list allowed-tools: Bash, Read, Glob, Grep, Agent argument-hint: "[arg1|arg2]" ---Use
"*"forallowed-toolsif the command needs unrestricted tool access. Otherwise, list only the tools required. -
Write the command body. Keep it short. A command should:
- State what the user has requested
- Reference
$ARGUMENTSfor user input - Invoke the corresponding skill
- Set expectations ("Do NOT re-plan", "Follow the skill instructions precisely")
-
Example:
--- description: Run ecosystem health checks allowed-tools: Bash, Read, Glob, Grep argument-hint: "[all|services|pipelines]" --- # Ecosystem Health The user wants to check ecosystem health. Scope: **$ARGUMENTS** (default: all). Invoke the ecosystem-health skill. Report findings in the standard health report format. -
Test by running
/<my-command>in Claude Code.
Adding a New Agent
-
Create the agent file:
agents/my-agent.md -
Add frontmatter with all required fields:
--- name: my-agent description: > What this agent does and when it is dispatched. <example> Context: Describe the situation where the agent is invoked. user: "User message that triggers dispatch" assistant: "Coordinator's response explaining the dispatch" <commentary> Why this agent is the right choice for this situation. </commentary> </example> model: sonnet color: green tools: ["Read", "Grep", "Glob", "Bash"] ---The
<example>blocks are required. Include at least one. They help Claude Code understand when to dispatch the agent. -
Write the agent's instructions. Remember:
- The agent runs with zero prior context. It knows nothing about the session unless you tell it.
- Include all review criteria, output formats, and decision logic in the file.
- The wave-executor's dispatch prompt must provide the full task context.
-
Define the output format. Agents should produce structured output so the wave-executor can parse results.
-
Test by having wave-executor dispatch the agent, or by invoking it directly via the Agent tool in Claude Code.
Custom Agents for Your Project
Session Orchestrator ships generic base agents (code-implementer, test-writer, ui-developer, db-specialist, security-reviewer, …) that work in any project — see docs/components.md for the full roster. For domain-specific needs, define custom agents in your project's .claude/agents/ directory.
How Agent Resolution Works
When the wave-executor dispatches agents, it follows this priority:
- Project agents (
.claude/agents/) — highest priority - Plugin agents (
session-orchestrator:*) — generic fallback general-purpose— last resort
Setting Up Project Agents
- Create
.claude/agents/in your project - Add agent
.mdfiles with YAML frontmatter (name,descriptionwith<example>blocks,tools,model) - Optionally add
agent-mappingto your Session Config for explicit role binding:agent-mapping: { impl: code-editor, test: test-specialist, db: database-architect }
When to Use Project Agents vs. Plugin Agents
- Plugin agents are sufficient for most projects — they cover the common roles (code, tests, UI, DB, security)
- Project agents are valuable when you need domain-specific knowledge (e.g., Austrian tax compliance, specific design system rules, custom DB tooling)
- Projects can mix both: use project agents for specialized tasks and let plugin agents handle the rest
Modifying Existing Skills
Before changing an existing skill:
-
Read the entire skill file. Understand all phases, dependencies, and output formats before making changes.
-
Maintain backward compatibility with Session Config. If a skill reads a config field, do not rename or remove it. Add new fields instead and handle the case where they are not present.
-
Preserve the skill's contract. Other skills and commands depend on specific output formats and behaviors. If
session-startproduces a findings summary thatsession-planconsumes, do not change the summary format without updatingsession-plan. -
Test in multiple project repos if possible. Different repos have different Session Config values, VCS providers, and toolchains. A change that works in one repo may break in another.
-
Key convention: All configurable behavior flows through Session Config. If you are tempted to hardcode a value that might vary across repos, add it to Session Config instead.
Session Config Convention
Session Config is the per-repo configuration mechanism. It lives in each project's CLAUDE.md under a ## Session Config heading.
When adding new configurable behavior:
-
Add the field to
docs/session-config-reference.md— the canonical type/default reference all skills point to. Include: field name, type, default value, and description. -
Add the field to
docs/session-config-template.md. Theclaude-md-drift-checkskill (Check 6) enforces top-level-key parity between this repo's Session Config and the template — a field missing from the template will be flagged. Add usage guidance todocs/USER-GUIDE.mdSection 4 if the field is user-facing. -
Use graceful degradation. If the field is not present in a repo's config, the skill should either skip the related functionality or use a sensible default. Never fail because a config field is missing.
-
Example: To add a
my-tool-commandfield, add it todocs/session-config-reference.mdanddocs/session-config-template.md, and in the consuming skill write: "Readmy-tool-commandfrom Session Config. If not set, default tonpm run my-tool."
Label Taxonomy
The standard label taxonomy is defined in the Label Taxonomy section of skills/gitlab-ops/SKILL.md. This is the single source of truth for all label definitions.
When your skill or agent interacts with VCS labels, reference the gitlab-ops taxonomy. If you need to add a new label category, add it to gitlab-ops first, then use it in your skill.
Platform Abstraction
Session Orchestrator supports Claude Code, Codex CLI, Cursor IDE, and Pi. When contributing, follow these guidelines to maintain cross-platform compatibility:
File Location Conventions
| Category | Location | Notes |
|---|---|---|
| Plugin manifests | .claude-plugin/, .codex-plugin/ | Platform-specific, separate files |
| Skills | skills/ | Shared — one SKILL.md serves all platforms |
| Commands | commands/ | Shared — Claude Code native, Codex via AGENTS.md, Cursor via rules |
| Hooks (Claude Code) | hooks/hooks.json | Uses $CLAUDE_PLUGIN_ROOT |
| Hooks (Codex) | hooks/hooks-codex.json | Uses native ${PLUGIN_ROOT}; wrapper exports CODEX_PLUGIN_ROOT and SO_PLATFORM=codex |
| Hooks (Cursor) | hooks/hooks-cursor.json | Reference; configure in Cursor Settings |
| Hooks (Pi) | hooks/hooks-pi.json | Installed via scripts/pi-install.mjs |
| Extension (Pi) | pi/extensions/session-orchestrator.ts | Pi manifest lives in the package.json pi key |
| Rules (Cursor) | .cursor/rules/*.mdc | Cursor-native format, one per skill |
| Agents (Claude Code) | agents/ | Markdown format |
| Agents (Codex) | .codex-plugin/agents/ | TOML format |
| Node.js scripts | scripts/ | Shared — use detectPlatform() from scripts/lib/platform.mjs |
Writing Platform-Portable Skills
- Reference
skills/_shared/platform-tools.mdfor tool mappings between platforms - Model preferences: Add
model-preference,model-preference-codex, andmodel-preference-cursorto SKILL.md frontmatter - AskUserQuestion: Add fallback note: "On Codex CLI / Cursor, present as numbered list"
- Agent dispatch: Document all three patterns — Claude Code (
Agent()tool), Codex (agent roles), Cursor (sequential execution) - State paths: Use
<state-dir>/(.claude/,.codex/,.cursor/) — never hardcode a single platform - Metrics paths: Use
.orchestrator/metrics/(shared) for learnings and sessions - Config file: Reference "Session Config in CLAUDE.md or AGENTS.md" — not just CLAUDE.md
Writing Platform-Portable Scripts (Node.js)
- Import the actual exports from
platform.mjs:detectPlatform()returns the platform string;SO_PLATFORM,SO_STATE_DIR,SO_CONFIG_FILE, andSO_PLUGIN_ROOTare module constants computed at load time. - Honor explicit hook context. Codex wrappers set
SO_PLATFORM=codexandCODEX_PLUGIN_ROOTfrom native${PLUGIN_ROOT}. Code that receives an explicit platform override must prefer it over ambient multi-harness detection. - Never hardcode
.claude/— useresolveStateDir(platform)orSO_STATE_DIR. - Use the shared project resolver rather than inventing a new env chain. It supports
CLAUDE_PROJECT_DIR,CODEX_PROJECT_DIR,CURSOR_PROJECT_DIR, andPI_PROJECT_DIR. - Resolve plugin roots through shared helpers. Native
PLUGIN_ROOTwins first; a validSO_PLATFORMthen promotes its matching compatibility variable; remaining compatibility roots retain Claude → Codex → Cursor → Pi order before filesystem walk. Shell snippets should follow the same precedence.
Scripts & Testing
Script Infrastructure
| Script | Purpose |
|---|---|
scripts/parse-config.mjs | Parse Session Config from CLAUDE.md/AGENTS.md into validated JSON (CLI wrapper over scripts/lib/config.mjs) |
scripts/run-quality-gate.mjs | Run quality gate checks (4 variants: baseline, incremental, full-gate, per-file) |
scripts/validate-wave-scope.mjs | Validate wave-scope.json before enforcement hooks consume it |
scripts/token-audit.sh | Cross-project token efficiency audit |
scripts/codex-install.mjs | Validate and install through Codex's public marketplace/add/list lifecycle |
scripts/cursor-install.mjs | Install Cursor rules via symlinks |
scripts/lib/platform.mjs | Platform detection and platform-specific root/state/config resolvers |
scripts/lib/common.mjs | Shared ESM utilities — die(), warn(), findProjectRoot(), resolvePluginRoot(), makeTmpPath(), utcTimestamp() |
scripts/lib/worktree.mjs | Git worktree helpers for isolated agent work |
Running Tests
npm test # Run the full vitest suite
npx vitest run <pattern> # Run a specific test file / subset
Test files are in tests/. Add new tests as tests/<area>/<name>.test.mjs following existing patterns (hooks, lib, unit, integration, skills).
Platform Variables
scripts/lib/platform.mjs exports these as module constants computed at load time via detectPlatform():
SO_PLATFORM:claude|codex|cursor|piSO_STATE_DIR:.claude|.codex|.cursor|.piSO_CONFIG_FILE:CLAUDE.md(Claude Code, Cursor) |AGENTS.md(Codex, Pi)SO_SHARED_DIR:.orchestrator(all platforms)
Do not confuse the exported SO_PLATFORM constant with the SO_PLATFORM environment override used by hook wrappers. Codex sets the environment value explicitly so downstream handlers can prefer Codex semantics even in a process that also exposes another harness variable.
Codex Refresh and Versioning
node scripts/codex-install.mjs intentionally calls codex plugin add session-orchestrator@kanevry on every run. This refreshes the installed bundle after local edits or a pull. For an explicit cache identity change, commit a new UTC suffix in .codex-plugin/plugin.json using <package-version>+codex.<YYYYMMDDHHmmss>. The installer only validates this tracked value; it must never generate or write the manifest version during installation.
Pull Request Guidelines
Note on process: active planning happens on a private GitLab tracker; the public GitHub repository shows a mirrored subset of that work. Small, self-contained PRs that cite concrete observed drift (a stale path, a wrong count, a broken example) are the best entry point for external contributions.
-
One logical change per PR. Do not bundle unrelated skill changes.
-
Use Conventional Commit messages:
feat(session-start): add ecosystem health integration fix(wave-executor): handle agent timeout gracefully docs(readme): update Session Config table refactor(gitlab-ops): extract label helpersFormat:
type(scope): descriptionTypes:
feat,fix,docs,refactor,test,chore -
Test in at least one project repo before submitting. Run the full session flow (
/sessionthrough/close) if your changes touch core skills. -
Update README.md if you add or change user-facing features (new commands, new config fields, changed behavior).
-
Update docs/USER-GUIDE.md if you change workflow steps or configuration options.
-
Keep skills self-contained. If your PR adds a cross-skill dependency, document it clearly in both skills.
JSON Schema Validation
The plugin manifests (.claude-plugin/plugin.json, .claude-plugin/marketplace.json) carry $schema keys pointing to canonical schemastore.org definitions. Local validation:
npx ajv-cli validate -c ajv-formats -s "https://json.schemastore.org/claude-code-plugin-manifest.json" -d .claude-plugin/plugin.json --strict=false
npx ajv-cli validate -c ajv-formats -s "https://json.schemastore.org/claude-code-marketplace.json" -d .claude-plugin/marketplace.json --strict=false
The same checks run in CI via the plugin-schema-validate job. Editors that respect $schema (VS Code, JetBrains) provide live autocomplete and on-save validation.
Good First Contribution Areas
- Setup-guide reproduction — follow Development Setup on a fresh machine and file an issue/PR for any step that does not work as written.
- Docs-drift fixes — counts, paths, and examples drift as the plugin evolves. Mechanical guards exist (the
claude-md-drift-checkdocs-parity check, the opt-indocs-stalenessprobe), but human-spotted drift with a concrete citation is always welcome. - Small parity tests — tests that pin a doc claim to the filesystem (e.g. "every hook referenced in
hooks/hooks.jsonexists") are low-risk and high-value. - Low-risk wording fixes in
.claude/rules/orskills/— clarify a confusing sentence, fix a stale cross-reference; pair the change with a test when the wording is load-bearing.
Code of Conduct
This project follows a simple standard: be respectful, constructive, and welcoming.
- Provide constructive feedback in code reviews. Focus on the change, not the person.
- Welcome newcomers. Answer questions patiently.
- Assume good intent. If a contribution seems off, ask for clarification before criticizing.
- Keep discussions technical and focused. Disagreements about design are healthy; personal attacks are not.
We are building a tool that helps developers work more effectively. The same principle applies to how we work with each other.