plugins/dev/skills/setup-catalyst/SKILL.md
Diagnose and fix Catalyst setup issues. Validates tools, database, config, OTel, direnv, and thoughts. Automatically fixes what it can — creates directories, initializes the database, sets WAL mode, runs migrations. Use for new installs, upgrades, or when something isn't working.
npx skillsauth add coalesce-labs/catalyst setup-catalystInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Diagnose the full Catalyst environment, fix everything fixable, and verify the fixes worked.
Locate and run the health check script:
SCRIPT=""
if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" && -f "${CLAUDE_PLUGIN_ROOT}/scripts/check-setup.sh" ]]; then
SCRIPT="${CLAUDE_PLUGIN_ROOT}/scripts/check-setup.sh"
elif [[ -f "plugins/dev/scripts/check-setup.sh" ]]; then
SCRIPT="plugins/dev/scripts/check-setup.sh"
fi
bash "$SCRIPT" 2>&1 || true
Parse the output. Categorize every warning and failure into:
For auto-fixable issues, fix them immediately — don't ask, just do it. These are safe, local, reversible operations.
Exception: thoughts/ repair is NOT a bare mkdir. The humanlayer thoughts system expects
thoughts/shared and thoughts/global to be symlinks into a central thoughts repo. A bare
mkdir over a clobbered symlink silently routes all subsequent agent writes to a non-syncing
local directory. Always route thoughts repair through catalyst-thoughts.sh, and treat a
regular-directory-where-a-symlink-should-be as fatal — surface the recovery command to the
user rather than overwriting anything.
| Issue | Fix |
|-------|-----|
| ~/catalyst/ missing | mkdir -p ~/catalyst/{wt,events,history} |
| ~/catalyst/wt/ missing | mkdir -p ~/catalyst/wt |
| ~/catalyst/events/ missing | mkdir -p ~/catalyst/events |
| Database missing or schema incomplete | Run catalyst-db.sh init (locating it the same way as the check script) |
| schema_migrations table missing | Run catalyst-db.sh init — it's idempotent |
| WAL mode not set | sqlite3 ~/catalyst/catalyst.db 'PRAGMA journal_mode=WAL;' |
| thoughts/shared/<dir> missing | Run bash plugins/dev/scripts/catalyst-thoughts.sh init-or-repair (re-uses humanlayer when configured; warns loudly when no thoughts repo is set up) |
| ~/.catalyst/bin/ missing OR any catalyst-* symlink absent/broken | Run bash plugins/dev/scripts/install-cli.sh — idempotent, safe to re-run. If $HOME/.catalyst/bin is not on $PATH, the script prints the exact line to add to ~/.zshrc or ~/.bashrc — relay that to the user so they can finish the one-time PATH setup. |
| thoughts/shared is a regular directory (not a symlink) | Fatal — do not auto-fix. Tell the user the humanlayer symlink was clobbered and show recovery: mv thoughts/shared thoughts/shared.orphaned-$(date +%Y%m%d) then bash plugins/dev/scripts/catalyst-thoughts.sh init-or-repair |
| Drift detected: keys present in plugins/dev/templates/config.template.json but missing from .catalyst/config.json (CTL-489) | Enumerate via bash plugins/dev/scripts/check-config-drift.sh --json. Generate a preview merge to a temp file via --merge-into /tmp/merged.json and show the user diff -u .catalyst/config.json /tmp/merged.json. On user confirmation, jq deep-merge into the real file: bash plugins/dev/scripts/check-config-drift.sh --merge-into .catalyst/config.json.new && mv .catalyst/config.json.new .catalyst/config.json. Merge preserves every existing user value (project on the right of jq's * recursive merge). |
| Profile drift between .catalyst/config.json and humanlayer mapping | Run bash plugins/dev/scripts/catalyst-thoughts.sh init-or-repair — it now auto-repairs drift by running humanlayer thoughts uninit --force && humanlayer thoughts init --profile <config profile> --directory <config directory>. (Plain humanlayer thoughts init --force does NOT update an existing repo→profile mapping, so the uninit step is required.) |
Config-template drift (CTL-489). When the template gains a key that an existing project's
.catalyst/config.json lacks (the original CTL-487 silent-fallback bug — catalyst itself ran in
oneshot-legacy for two months because orchestration.dispatchMode was absent), Phase 2 surfaces
a unified diff and asks for confirmation before merging. Concretely:
bash plugins/dev/scripts/check-config-drift.sh --json --config .catalyst/config.json --template <template> to enumerate missing leaves. The template path comes from
$CLAUDE_PLUGIN_ROOT/templates/config.template.json in production, or
plugins/dev/templates/config.template.json when dogfooding from the repo.bash plugins/dev/scripts/check-config-drift.sh --merge-into /tmp/merged.json and show the user
the unified diff: diff -u .catalyst/config.json /tmp/merged.json..catalyst/config.json (mv from a
sibling .tmp file). The merge uses jq's * operator with the project on the right —
existing values always win, missing keys are added.catalyst.filter.groqModel, the template's default is NOT applied to that key..catalyst/config.json untouched. The drift warning continues to
appear on subsequent workflow invocations, providing passive nagging until resolved.Execution-core state contract (CTL-564). When a repo's
catalyst.orchestration.dispatchMode is execution-core, setup-catalyst.sh
runs an extra step — setup_execution_core_states — right after the Linear
workflow-state fetch. That step delegates to the standalone
plugins/dev/scripts/setup-execution-core-states.sh, which ensures the team's
contract workflow states exist (Ready + Research, Plan, Implement,
Validate, PR; Triage already exists), writes the 9-phase → 5-state
collapse stateMap, refreshes stateIds, and upserts the team's entry in the
central ~/catalyst/execution-core/registry.json. The step is a silent no-op
for phase-agents / oneshot-legacy repos, and a Linear-permission failure in
the standalone script never aborts setup. The standalone script is also
idempotent and can be run directly per team (setup-execution-core-states.sh --config .catalyst/config.json [--dry-run] [--json]).
Linear git automations (CTL-759). As its last Linear step,
setup-execution-core-states.sh reconciles the team's git automations —
Linear's built-in "move ticket on git event" rules. It pins exactly two
(start → PR, merge → Done) and deletes any review automation, so the
execution-core daemon stays the single authority on ticket state. The reconcile
is best-effort and tolerant: a Linear permission/transport failure prints a
WARNING and continues — it never aborts setup and never alters the script's exit
codes. check-project-setup.sh (hot path) warns on drift via a TTL-gated cached
read; a missing per-project token is a silent skip. Separately, Linear's
branch-name "magic words" toggle (Settings → Team → Workflow → Git) has no
API surface and cannot be reconciled — it must be turned OFF by hand, or it
races the daemon and re-introduces the CTL-758 backward state-write.
For issues needing user input, explain what's needed and how to provide it:
| Issue | What to tell the user |
|-------|----------------------|
| Linear API token not set | Show the secrets file path, explain where to get the token from Linear settings |
| No project config | Suggest running setup-catalyst.sh or offer to create a minimal .catalyst/config.json interactively |
| direnv not installed | Show brew install direnv and the shell hook setup |
| Linear "magic words" auto-move ON | Tell the user to turn it OFF in Settings → Team → Workflow → Git — it races the execution-core daemon and causes backward state writes (CTL-758). No API surface; must be toggled by hand. |
| Linear review git automation set | Run setup-execution-core-states.sh to remove it; the pipeline owns the Validate/review state, not Linear. |
| Personal git automations override team ones | Remind the user that Linear lets each member set personal git automations that shadow the team defaults — check Settings → Account → Git if drift persists after the team reconcile. |
Observability (OTel) is optional. If Docker or OTel containers aren't found, note it as informational — don't treat it as an issue. Point the user to https://github.com/ryanrozich/claude-code-otel if they want to set it up.
After fixing, run the health check script again:
bash "$SCRIPT" 2>&1 || true
Re-run the drift check independently to confirm zero remaining drift (CTL-489):
bash plugins/dev/scripts/check-config-drift.sh \
--config .catalyst/config.json \
--template plugins/dev/templates/config.template.json
Compare the before/after results. Report:
── Catalyst Setup ──────────────────────────────
[Phase 1 output from check-setup.sh]
── Fixing Issues ───────────────────────────────
✅ Created ~/catalyst/events/
✅ Initialized session database
✅ Set WAL mode
✅ Created thoughts/shared/reports/
── Config Drift ────────────────────────────────
Detected 2 missing template keys:
• catalyst.orchestration.dispatchMode → "phase-agents"
• catalyst.filter.groqModel → "llama-3.1-8b-instant"
Preview diff:
--- .catalyst/config.json
+++ /tmp/merged.json
+ "orchestration": { "dispatchMode": "phase-agents" },
+ "filter": { "groqModel": "llama-3.1-8b-instant" },
Apply these template additions? [y/N] y
✅ Merged 2 keys into .catalyst/config.json (existing values preserved)
── Verification ────────────────────────────────
[Phase 3 output from check-setup.sh]
── Summary ─────────────────────────────────────
Fixed 4 issues automatically.
Still needs attention:
• Linear API token — add to ~/.config/catalyst/config-<project>.json
• OTel stack — run: docker compose up -d
testing
Phase-agent that fixes a failing verify verdict so the pipeline self-heals instead of stalling to needs-human (CTL-653). Reads `${ORCH_DIR}/workers/<ticket>/verify.json`, fixes the `findings[]` (every severity:"high" plus the regression_risk drivers) directly via Edit/Write, commits the remediation, and emits `phase.remediate.complete.<ticket>`. The scheduler's router then re-dispatches `verify` to re-check (the verify⇄remediate cycle, cap 3). Dispatched as a `claude --bg` job by `phase-agent-dispatch`, which invokes it via slash command — hence `user-invocable: true`.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies genuine blockers (a semantic second-pass over the backlog — NOT a prose scrape; CTL-838), estimates scope, writes triage.json, and posts a triage analysis comment to Linear. Triage completion is signaled by that comment plus the local triage.json — there is no `triaged` label. Emits phase.triage.complete.<TICKET> on success and phase.triage.fai
tools
Phase agent for the research step of the 9-phase orchestrator pipeline (CTL-450). Wraps /catalyst-dev:research-codebase and produces thoughts/shared/research/<date>-<ticket>.md, then emits phase.research.complete.<ticket>. Reads triage.json from the worker dir as its prior-phase artifact. Spawned via plugins/dev/scripts/phase-agent-dispatch, which invokes it via slash command — hence `user-invocable: true`.
development
Phase-agent wrapper that opens the pull request after implementation completes (CTL-449 Initiative 1 Phase 3). Delegates to `/catalyst-dev:create-pr` (which already auto-runs `describe-pr` and transitions Linear to `inReview`), then writes the PR number + URL into the phase signal file so the downstream `phase-monitor-merge` agent can read it without re-querying GitHub. Dispatched as a `claude --bg` job by `phase-agent-dispatch`, which invokes it via slash command — hence `user-invocable: true`.