| name | onboard-repo |
| description | Onboard a Cursor repo into the hot-path/cold-path standards system: sync second-brain-workflow rules, add a thin always-apply onboarding rule, point at relevant second-brain practices, and wire project-scoped MCP (Railway / Postgres / Logfire / PostHog) — never global mcp.json. Use when the user says onboard repo, onboard this repo, set up cursor standards, sync rules into this project, or wire best practices into a new codebase. |
Onboard repo
Works like feature complete: run immediately when triggered; do not ask for
confirmation unless a step would overwrite customized project files the user
clearly owns.
Paths
- Standards (source of truth):
~/second-brain-workflow
- Rules sync:
~/second-brain-workflow/scripts/sync-rules.sh
- Skills sync:
~/second-brain-workflow/scripts/sync-skills.sh
- Skills source:
~/second-brain-workflow/skills/{workflow,...}/
- Vault practices:
$SBW_VAULT/practices/ (default ~/vaults/second-brain)
- Knowledge skill (after sync): the
obsidian-knowledge-base skill
- MCP skill (after sync): the
mcp-per-project skill
Reference backends. Copy the MCP wiring pattern from whichever backend on
this machine is already set up — do not assume a fixed path. Find candidates
with:
ls -d ~/*/*/.cursor/bin 2>/dev/null | sed 's|/.cursor/bin$||'
Prefer the one whose stack and provider mix is closest to the target repo.
Target repo
- Prefer the current workspace root if it is a single project git repo.
- If multi-root or unclear, ask which path to onboard (one short question).
- If the folder is not a git repo, still sync rules; note that in the status.
Do not run this against the standards repo or a vault.
Steps (in order)
1. Sync hot path
~/second-brain-workflow/scripts/render.py "<TARGET>"
~/second-brain-workflow/scripts/render.py "<TARGET>" --targets cursor,agents
One canonical rule set (rules/*.md, wherever SBW_RULES_DIR resolves to) renders per agent:
| Target | Output | Scoping |
|---|
cursor | .cursor/rules/*.mdc | native globs |
claude-code | .claude/rules/*.md + root CLAUDE.md | native paths: frontmatter |
agents | AGENTS.md | always-on |
Everything is a real file carrying a generated by second-brain-workflow@<sha>
provenance comment, so it works in every clone and in CI. Never edit a rendered
file in the target — edit the source and re-render. Files lacking the comment are
treated as hand-written and are never overwritten or pruned; a hand-written
CLAUDE.md is left alone, and you should add @AGENTS.md at its top instead.
render.py "<TARGET>" --check reports drift and exits non-zero — safe for CI or
a pre-commit hook. render.py --explain shows how each rule resolves per target.
Set RENDER_TARGETS per machine (e.g. cursor,agents where only Cursor runs).
Each target prunes only its own generated files, so a repo shared with someone
using the other agent keeps working.
If AGENTS.md already existed and differed, the copy overwrites — that is
intentional for the shared portable file. Repo-specific agent guidance goes in
the onboarding .mdc or CLAUDE.md, not in a forked AGENTS.md.
2. Infer stack
From manifests (package.json, pyproject.toml/uv.lock, pubspec.yaml,
app.json / expo, Angular angular.json, etc.), classify:
| Stack | Vault folders to skim |
|---|
| FastAPI / Python API | practices/backend/ |
| Fastify / Node API | practices/backend/ (esp. fastify + env + errors) |
| Next.js | practices/frontend/ (next-* , zod-validate-next-*) |
| Angular | practices/frontend/ (prefer-signal-, event-driven-, angular-http-*) |
| Expo / RN | practices/app/ (expo-, resilient-, typed-expo-*) |
| Flutter | practices/app/ (getx-, prefer-riverpod-, handle-offline-*) |
List 3–6 enforced/trialing note titles that apply; do not paste full notes
into the repo.
Also classify MCP role for step 6:
| Role | When | MCP action |
|---|
| Backend / secret source | FastAPI, Fastify, or other API that owns Railway/Logfire/PostHog | Full .cursor/bin wrappers + mcp.json |
| Sibling app | Frontend / mobile / Flutter for a product that already has a backend | Point absolute paths at that backend’s wrappers only |
| Standalone / no infra | Docs, standards, libs with no Railway/Logfire/PostHog | Skip MCP (say so in Done) |
2b. Skills for this repo
python3 ~/second-brain-workflow/scripts/lib/skill_manifest.py relevant --repo <abs-path>
Absolute path, not a relative one: an installed skill directory holds only this
SKILL.md, so a relative path resolves to nothing and the step silently falls
back to guesswork.
Prints two lists, from the operator's own skills.json — nothing here invents a
recommendation:
- Adopted and scoped to this repo. Mention these in the Done report so the
next session knows they are there. They are already installed; do not re-install.
Each names where its scope came from — the allow entry itself, or inherited
from its source. If one looks wrong for this repo, say which of the two to
edit; they are different fixes.
- Not adopted, worth considering. Each carries the reason and its install
command. Report, never install. Adopting a skill is a standing choice about
every future session in every repo, and several write into the project — a
design linter registering an edit-time hook, a screenshot tool scaffolding a
whole Next.js app. Name it, name the cost, let the user decide.
It also prints which roster answered, as a roster: line naming the manifest
path and where that setting came from. Two machines with different manifests
print the same headings, so the counts below them mean nothing without it.
Two outcomes that are not the same thing, and the command distinguishes them:
no skills manifest configured — skill roster skipped, exit 0. No roster
was consulted. Supported state, not a gap to fill: say so in the Done report
and move on. Do not read it as "no skills apply here" — nothing was read.
- Exits non-zero. A manifest is configured and cannot be used — missing
file, bad JSON, an unknown key. That is a real fault on this machine. Report
the error verbatim rather than skipping; onboarding this repo against a roster
that failed to load would silently under-report.
If the user does adopt one, the loop is theirs to run, not yours to assume:
make fetch-skills YES=1 then make sync-skills, and a session restart —
the skill listing is read at startup, so a newly linked skill is invisible until
then.
2c. Practices this repo has never had
~/second-brain-workflow/scripts/practices-for.py --repo <abs-path>
Only enforced notes become rules, and only rules reach a repo — so on a large
vault the notes at idea and trialing are invisible to a repo that was just
onboarded. This is what closes that gap. Two tiers, and the difference matters:
- Governs files here — the note's own
applies-to glob matches real files.
A promotion delta is shown for these (-> applying here clears ENFORCED).
Use these to replace step 2's guessed list.
- Same domain, judgement required — matched on domain alone. No promotion
claim is made, because a guess that says "clears ENFORCED" would invite
adding a
repos: entry for a note that does not govern this repo.
Report these; never apply them, and never offer to apply them all. Promotion
is driven by length(repos), and the vault's own rule is that trialing must be
earned by deliberate re-application, not just counted. Applying a dozen notes in
one onboarding pass would add a dozen repos: entries and manufacture exactly the
evidence the bar exists to measure — the same defect as promoting a note on a
single repo. Onboarding is also when the repo is least understood, which is the
worst moment to change its code.
If the user picks one, apply that one, then record it through
update-second-brain so the repos: bump and any promotion are captured as the
deliberate act they were.
Sanity-check a delta before repeating it: a note whose glob is **/package.json
matches every Node repo ever written, and the rung it would clear is not evidence
of anything. Say so rather than passing it on.
3. Project onboarding rule
Create or update one always-apply rule:
<TARGET>/.cursor/rules/<slug>-onboarding.mdc
slug = short project name (directory basename, kebab-case)
- If a
*-onboarding.mdc already exists with real project content, update in
place (preserve project-specific bullets; add any missing standards pointers)
- If missing, create from the template below
---
description: <Project> agent entry — standards sync, done criteria, vault pointer
alwaysApply: true
---
# <Project> — Agent onboarding
- Start from `AGENTS.md` (synced from second-brain-workflow). Architecture/commands: `CLAUDE.md` if present.
- Shared stack rules: vendored copies under `.cursor/rules/` — generated by `~/second-brain-workflow/scripts/render.py`; edit the source rules and re-render, not here.
- Deep patterns: read matching notes in the vault via the `obsidian-knowledge-base` skill; do not copy the vault into this repo.
- Stack here: <inferred stacks>.
- Relevant practices: <wikilink-style note slugs or filenames without .md>.
- Task is done only when: <lint / typecheck / test commands for this repo>.
Fill done-criteria from the repo’s real scripts (package.json scripts, Makefile, etc.). If unknown, use a honest placeholder the user can tighten.
4. Optional CLAUDE.md
- If missing and the repo is non-trivial: create a short stub (project one-liner, setup/dev/test commands only). Do not invent architecture essays.
- If present: leave it; only add a one-line pointer to
AGENTS.md + vault if absent.
5. Vault / daily note
Append a one-line Built bullet to today's daily note — e.g. “Onboarded <repo>
into second-brain-workflow”. Daily notes need no approval (see
obsidian-knowledge-base). Do not write practices/** during onboard unless
the user asks (those still require proposal + approval).
Never write $SBW_VAULT/YYYY-MM-DD.md directly. A wrap-up in another
session may be composing that same note right now, and a whole-file write drops
whatever it had already put there. Use the same compare-and-swap path
update-second-brain uses:
printf '## Built\n- Onboarded `%s` into second-brain-workflow\n' "$repo" > /tmp/onboard-block.md
STAMP="$(~/second-brain-workflow/scripts/append-daily-block.py --stamp --quiet)"
~/second-brain-workflow/scripts/append-daily-block.py --expect "$STAMP" --block /tmp/onboard-block.md
Exit 3 means another session wrote first: re-run --stamp and re-run the
append. Commit the note as soon as it is written rather than at the end.
6. Project-scoped MCP (required for product apps)
Read and follow mcp-per-project.
Do this every time you onboard a product backend or its sibling app — not only when
the user mentions MCP.
Hard rules
- Never add Railway / Postgres / Logfire / PostHog to
~/.cursor/mcp.json
(keep mcpServers: {} for product infra).
- Secrets live in the backend
.env only; mcp.json has no tokens.
- Server names are product-suffixed (e.g.
railway-initech, logfire-acme)
— never bare railway / logfire when multiple products share the Mac.
- Gitignore
.cursor/mcp.json; commit .cursor/mcp.json.example, .cursor/bin/*,
.cursor/README.md, and .cursor/rules/mcp.mdc.
Backend (secret source)
Copy the closest already-wired backend on this machine (see Reference backends
above). The mix usually falls into one of two shapes — full product
(Railway + Postgres + Logfire + PostHog) or infra-light (Railway prod + staging
only) — so match on providers, not on project name:
-
Create .cursor/bin/ wrappers that read .env and exec the MCP:
railway-mcp.sh → RAILWAY_TOKEN → railway mcp
railway-postgres-mcp.sh → RAILWAY_TOKEN → Postgres URL via Railway vars → @modelcontextprotocol/server-postgres (read-only)
logfire-mcp.sh → LOGFIRE_READ_TOKEN → uvx mcp-proxy → https://logfire-eu.pydantic.dev/mcp (adjust region if the product is US)
posthog-mcp.sh → POSTHOG_PERSONAL_API_KEY → uvx mcp-proxy → https://mcp.posthog.com/mcp
-
Write .cursor/mcp.json (local) and .cursor/mcp.json.example (committed) with
the suffixed server names pointing at those wrappers.
-
Write .cursor/README.md (token table + “never global”) and always-apply
.cursor/rules/mcp.mdc listing exact server names + no cross-product fallback.
-
Ensure .gitignore has .cursor/mcp.json.
-
Document empty keys in .env.example and append the same keys to .env if
missing (leave values empty for the user):
| Key | Purpose |
|---|
RAILWAY_TOKEN | Railway project/environment token |
LOGFIRE_READ_TOKEN | Logfire read token (project:read) — not write LOGFIRE_TOKEN |
POSTHOG_PERSONAL_API_KEY | PostHog personal API key (MCP preset, phx_…) |
-
If tokens are already set, smoke-check with GetMcpTools / a cheap live call
after the user reloads MCP; report which servers are ready vs unauthorized.
If tokens are empty, tell the user what to create and to reload MCP — do not
block onboard on missing secrets.
Sibling app (frontend / mobile / Flutter)
Do not duplicate wrappers or put MCP tokens in the app .env.
.cursor/mcp.json + .cursor/mcp.json.example with absolute paths to the
backend’s .cursor/bin/*.sh (same suffixed server names).
- Always-apply
.cursor/rules/mcp.mdc pointing agents at those names and the
backend as the secret source.
- Gitignore
.cursor/mcp.json.
- Expo / RN repos: also add
expo-<product> → { "url": "https://mcp.expo.dev/mcp" }
in both mcp.json and mcp.json.example, list it in mcp.mdc, and remind
the user to reload MCP + complete Expo OAuth (EAS paid plan). See
mcp-per-project rule 7.
If the matching backend is not onboarded yet, onboard/MCP-wire the backend first
(or ask which backend path owns secrets).
Skip
Libs, docs, second-brain-workflow, vault — no product MCP. Say “MCP skipped” in Done.
Done means
Short status:
- Target path
- Sync result (rules linked +
AGENTS.md)
- Onboarding rule path (created/updated)
- Inferred stack + practice note slugs
- Skills scoped to this repo (already installed), and any candidate worth
considering with its cost — or "no manifest configured" if step 2b was skipped.
Nothing installed either way.
CLAUDE.md action (created stub / left alone / skipped)
- MCP action (backend wired / sibling pointed at
<backend> / skipped) + server
name suffixes + whether tokens are SET/EMPTY / live check if possible
- Daily-note path updated
Do not commit, push, or open extra PRs unless asked.