crewforth 0.0.1 → 3.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +3784 -0
  2. package/LICENSE +21 -0
  3. package/README.md +37 -7
  4. package/VERSION +1 -0
  5. package/adopt.sh +1670 -0
  6. package/bin/cli.js +497 -0
  7. package/kit/AGENT_TEMPLATE.md +92 -0
  8. package/kit/CLAUDE.md +228 -0
  9. package/kit/README.md +107 -0
  10. package/kit/agents/crew-backend-expert.md +95 -0
  11. package/kit/agents/crew-commit-agent.md +96 -0
  12. package/kit/agents/crew-database-expert.md +84 -0
  13. package/kit/agents/crew-devops-expert.md +82 -0
  14. package/kit/agents/crew-frontend-expert.md +85 -0
  15. package/kit/agents/crew-performance-expert.md +78 -0
  16. package/kit/agents/crew-planner.md +60 -0
  17. package/kit/agents/crew-privacy-agent.md +71 -0
  18. package/kit/agents/crew-review-agent.md +95 -0
  19. package/kit/agents/crew-security-expert.md +73 -0
  20. package/kit/agents/crew-session-manager.md +77 -0
  21. package/kit/agents/crew-test-expert.md +64 -0
  22. package/kit/eval/doctor.sh +465 -0
  23. package/kit/eval/gate-report.sh +137 -0
  24. package/kit/eval/golden-holdout.txt +53 -0
  25. package/kit/eval/golden-routing.txt +199 -0
  26. package/kit/eval/lib/crew-env.sh +14 -0
  27. package/kit/eval/lib/settings-json.awk +154 -0
  28. package/kit/eval/lib/star.sh +70 -0
  29. package/kit/eval/parser-conformance.sh +612 -0
  30. package/kit/eval/preflight.sh +186 -0
  31. package/kit/eval/routing-eval.sh +209 -0
  32. package/kit/eval/scan-skill.sh +162 -0
  33. package/kit/eval/smoke-test.sh +6693 -0
  34. package/kit/eval/update-guard.sh +81 -0
  35. package/kit/eval/utilization.sh +130 -0
  36. package/kit/hooks/board-sync.sh +106 -0
  37. package/kit/hooks/board.sh +962 -0
  38. package/kit/hooks/commit-msg +49 -0
  39. package/kit/hooks/context-usage.sh +353 -0
  40. package/kit/hooks/floor-blocklist.txt +124 -0
  41. package/kit/hooks/guard-bash.sh +1431 -0
  42. package/kit/hooks/guard-commit-scan.sh +511 -0
  43. package/kit/hooks/guard-write.sh +513 -0
  44. package/kit/hooks/pre-commit +577 -0
  45. package/kit/hooks/route-hint.sh +185 -0
  46. package/kit/hooks/secret-blocklist.txt +82 -0
  47. package/kit/hooks/session-guard.sh +149 -0
  48. package/kit/hooks/session-rehydrate.sh +41 -0
  49. package/kit/hooks/session-stats.sh +252 -0
  50. package/kit/hooks/session-update-check.sh +236 -0
  51. package/kit/hooks/skill-trust.sh +150 -0
  52. package/kit/hooks/trace-blocklist.txt +53 -0
  53. package/kit/settings.json +132 -0
  54. package/kit/skills/a11y/SKILL.md +44 -0
  55. package/kit/skills/adr/SKILL.md +74 -0
  56. package/kit/skills/api-design/SKILL.md +50 -0
  57. package/kit/skills/automode-policy/SKILL.md +91 -0
  58. package/kit/skills/automode-policy/references/policy.json +13 -0
  59. package/kit/skills/automode-policy/scripts/apply.sh +84 -0
  60. package/kit/skills/automode-policy/scripts/check.sh +102 -0
  61. package/kit/skills/backend-architecture/SKILL.md +103 -0
  62. package/kit/skills/brainstorm/SKILL.md +47 -0
  63. package/kit/skills/ci-pipeline/SKILL.md +38 -0
  64. package/kit/skills/commit-message/SKILL.md +82 -0
  65. package/kit/skills/commit-message/references/examples.md +8 -0
  66. package/kit/skills/confidence-check/SKILL.md +86 -0
  67. package/kit/skills/crew-board/SKILL.md +55 -0
  68. package/kit/skills/crew-brainstorm/SKILL.md +19 -0
  69. package/kit/skills/crew-code-review/SKILL.md +151 -0
  70. package/kit/skills/crew-code-review/references/history-search.md +19 -0
  71. package/kit/skills/crew-code-review/references/panel-mode.md +32 -0
  72. package/kit/skills/crew-code-review/references/receiving.md +23 -0
  73. package/kit/skills/crew-doctor/SKILL.md +36 -0
  74. package/kit/skills/crew-gates/SKILL.md +25 -0
  75. package/kit/skills/crew-handoff/SKILL.md +14 -0
  76. package/kit/skills/crew-plan/SKILL.md +18 -0
  77. package/kit/skills/crew-review/SKILL.md +23 -0
  78. package/kit/skills/crew-ship/SKILL.md +19 -0
  79. package/kit/skills/crew-skill/SKILL.md +61 -0
  80. package/kit/skills/crew-studio/SKILL.md +129 -0
  81. package/kit/skills/crew-update/SKILL.md +43 -0
  82. package/kit/skills/db-migration/SKILL.md +120 -0
  83. package/kit/skills/db-migration/references/backup-restore.md +33 -0
  84. package/kit/skills/db-migration/references/raw-sql.md +30 -0
  85. package/kit/skills/db-migration/references/tool-matrix.md +35 -0
  86. package/kit/skills/dependency-audit/SKILL.md +61 -0
  87. package/kit/skills/dependency-upgrade/SKILL.md +82 -0
  88. package/kit/skills/deploy/SKILL.md +100 -0
  89. package/kit/skills/deploy/references/proxy-ssl.md +56 -0
  90. package/kit/skills/deploy/references/rollback.md +18 -0
  91. package/kit/skills/deploy/references/scripts.md +9 -0
  92. package/kit/skills/deploy/references/topology-a.md +128 -0
  93. package/kit/skills/deploy/references/topology-b.md +44 -0
  94. package/kit/skills/docs-writer/SKILL.md +53 -0
  95. package/kit/skills/docs-writer/references/coauthoring.md +34 -0
  96. package/kit/skills/eval-grader/SKILL.md +75 -0
  97. package/kit/skills/eval-grader/references/method.md +51 -0
  98. package/kit/skills/frontend/SKILL.md +56 -0
  99. package/kit/skills/frontend/references/verify-contract.md +41 -0
  100. package/kit/skills/frontend-design/SKILL.md +71 -0
  101. package/kit/skills/frontend-design/references/design-review.md +34 -0
  102. package/kit/skills/frontend-rn-expo/SKILL.md +25 -0
  103. package/kit/skills/handoff/SKILL.md +71 -0
  104. package/kit/skills/handoff/references/cold-reader.md +51 -0
  105. package/kit/skills/i18n-integrity/SKILL.md +31 -0
  106. package/kit/skills/incident-runbook/SKILL.md +40 -0
  107. package/kit/skills/incident-runbook/references/postmortem.md +16 -0
  108. package/kit/skills/iterate/SKILL.md +40 -0
  109. package/kit/skills/mcp-builder/SKILL.md +54 -0
  110. package/kit/skills/mcp-builder/references/building.md +71 -0
  111. package/kit/skills/observability/SKILL.md +50 -0
  112. package/kit/skills/performance/SKILL.md +38 -0
  113. package/kit/skills/performance/references/bottlenecks.md +10 -0
  114. package/kit/skills/privacy-compliance/SKILL.md +75 -0
  115. package/kit/skills/red-team/SKILL.md +48 -0
  116. package/kit/skills/reflect/SKILL.md +57 -0
  117. package/kit/skills/release/SKILL.md +44 -0
  118. package/kit/skills/security-scan/SKILL.md +99 -0
  119. package/kit/skills/security-scan/references/ai-agents.md +107 -0
  120. package/kit/skills/security-scan/references/fronts.md +99 -0
  121. package/kit/skills/security-scan/references/prompting.md +23 -0
  122. package/kit/skills/security-scan/references/reporting.md +75 -0
  123. package/kit/skills/security-scan/references/verify.md +131 -0
  124. package/kit/skills/sonarqube-check/SKILL.md +108 -0
  125. package/kit/skills/sonarqube-check/references/local-engines.md +32 -0
  126. package/kit/skills/sonarqube-check/references/stand-up-server.md +25 -0
  127. package/kit/skills/spec-planning/SKILL.md +92 -0
  128. package/kit/skills/systematic-debugging/SKILL.md +57 -0
  129. package/kit/skills/systematic-debugging/references/techniques.md +44 -0
  130. package/kit/skills/teamboard/SKILL.md +88 -0
  131. package/kit/skills/teamboard/references/setup.md +37 -0
  132. package/kit/skills/testing/SKILL.md +49 -0
  133. package/kit/skills/testing/references/flaky-triage.md +51 -0
  134. package/kit/skills/threat-model/SKILL.md +57 -0
  135. package/kit/skills/threat-model/references/interview.md +51 -0
  136. package/kit/skills/threat-model/references/schema.md +42 -0
  137. package/kit/skills/token-budget/SKILL.md +51 -0
  138. package/kit/skills/trace-scan/SKILL.md +43 -0
  139. package/kit/skills/worktree/SKILL.md +40 -0
  140. package/kit/studio/README.md +266 -0
  141. package/kit/studio/ensure-node.sh +471 -0
  142. package/kit/studio/package.json +14 -0
  143. package/kit/studio/server/hooks/studio-gate.sh +169 -0
  144. package/kit/studio/server/index.js +902 -0
  145. package/kit/studio/server/lib/crew-env.js +11 -0
  146. package/kit/studio/server/lib/fleet.js +146 -0
  147. package/kit/studio/server/lib/graph.js +533 -0
  148. package/kit/studio/server/lib/instance.js +135 -0
  149. package/kit/studio/server/lib/kit-telemetry.js +182 -0
  150. package/kit/studio/server/lib/kit.js +158 -0
  151. package/kit/studio/server/lib/palette.js +118 -0
  152. package/kit/studio/server/lib/peers.js +72 -0
  153. package/kit/studio/server/lib/permissions.js +150 -0
  154. package/kit/studio/server/lib/projects.js +294 -0
  155. package/kit/studio/server/lib/roster.js +191 -0
  156. package/kit/studio/server/lib/session.js +309 -0
  157. package/kit/studio/server/lib/terminal.js +91 -0
  158. package/kit/studio/server/lib/transcript.js +100 -0
  159. package/kit/studio/web/app.js +1098 -0
  160. package/kit/studio/web/canvas.js +1179 -0
  161. package/kit/studio/web/chat.js +543 -0
  162. package/kit/studio/web/index.html +86 -0
  163. package/kit/studio/web/md.js +110 -0
  164. package/kit/studio/web/storage-migrate.js +23 -0
  165. package/kit/studio/web/style.css +1453 -0
  166. package/package.json +46 -8
  167. package/start.sh +748 -0
  168. package/bin/crewforth.js +0 -10
@@ -0,0 +1,32 @@
1
+ # Panel mode — adversarial expert panel
2
+
3
+ For a **consequential, hard-to-reverse** decision (an architecture choice, a public API contract, a security
4
+ boundary) a single-lens review under-covers. Run an **adversarial expert panel**: evaluate the change from
5
+ **several independent lenses in parallel**, each arguing on its own terms, then synthesize.
6
+ - **Distinct lenses, not repetition:** e.g. *correctness/edge cases · security & privacy · maintainability &
7
+ altitude (YAGNI) · operational cost/perf*. Give each lens a real advocate whose job is to find where the
8
+ change fails on *that* axis — diversity catches failure modes redundancy can't.
9
+ - **Adversarial by default:** each lens tries to refute "this is the right change", not to bless it. A concern
10
+ raised by one lens isn't overruled by the others' approval — it's recorded and weighed.
11
+ - **Synthesize, don't average:** collect every lens's findings, keep the sharpest objection from each, and
12
+ reach one decision (proceed / proceed-with-changes / reject) with the reasoning. This is the distilled
13
+ judge-panel pattern — reserve it for high stakes; routine diffs get the single-lens review above (don't
14
+ burn a panel on a one-liner).
15
+
16
+ ## The lenses (named, first-class)
17
+
18
+ Pick the lenses that fit the change — not all six every time. Each is a distinct advocate; assign one concern per
19
+ lens so they don't collapse into the same review twice.
20
+
21
+ | Lens | Asks | Fails the change when |
22
+ |---|---|---|
23
+ | **Correctness** | Does it do the right thing on every path? | An edge case, race, off-by-one, or error path is unhandled |
24
+ | **Security & privacy** | Can it be abused; does data leak? | Injection, missing authz, secret exposure, PII beyond purpose (→ `security-scan`, `privacy-compliance`) |
25
+ | **Simplicity & altitude** | Is it more than the problem needs? | Over-engineering, speculative generality, YAGNI, wrong abstraction level |
26
+ | **Contract & compatibility** | What breaks downstream? | A public API / schema / event shape changes without a migration or version (→ `api-design`) |
27
+ | **Operability** | What happens at 3am in prod? | No logs/metrics on the new path, silent failure, no rollback (→ `observability`, `incident-runbook`) |
28
+ | **Verifier integrity** | Is the *check* still honest? | A test/assertion/threshold was weakened to pass, instead of the behavior being fixed |
29
+
30
+ **Adversarial pairing:** the strongest panel pits lenses that pull against each other — Simplicity vs. Contract
31
+ (don't over-build, but don't break callers), Correctness vs. Operability (handle every case, but stay debuggable).
32
+ When two lenses conflict, that tension IS the decision to surface — resolve it explicitly, don't let one silently win.
@@ -0,0 +1,23 @@
1
+ # Receiving a review — an inbound comment is a candidate, not an instruction
2
+
3
+ Read this on the skill's other mode: the review is someone else's and the code is yours — a teammate's comments, a
4
+ quality gate's report, a bot's PR review.
5
+
6
+ **Read every item before changing any line.** Comments are written one per symptom, and two of them
7
+ often share one cause; applying them in arrival order yields a patch per symptom instead of one fix at the cause.
8
+ Group by cause, then decide.
9
+
10
+ Each item then earns the same disprove pass as a finding of your own. Any "no" below is a reason to answer in the
11
+ thread, not to edit:
12
+ 1. **Defect or preference?** Give each comment a severity and a category from SKILL.md — inbound prose arrives
13
+ unranked, you rank it, and only a `critical` or `high` blocks.
14
+ 2. **Does it hold where the reviewer did not look?** Check the call sites and callers the comment never opened.
15
+ 3. **Does it contradict a decision already recorded?** An `adr` or a documented constraint outranks the comment —
16
+ reopen the decision, do not quietly edit around it.
17
+ 4. **Does the real check still pass with it applied?** Run it, don't re-read it. A suggestion that turns a check
18
+ red is reported back, and never satisfied by weakening the check (Verifier integrity, in SKILL.md).
19
+ 5. **Is it against the current revision?** A comment on an older one may already be answered by a later commit.
20
+
21
+ Every inbound item leaves with a disposition from SKILL.md's triage table; none is left merely read. **A reasoned
22
+ refusal is an answer** — say why in the thread and let the reviewer press it or drop it. Not doing it quietly is not
23
+ an answer: it reads as agreement, and the same comment returns on the next review.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: crew-doctor
3
+ description: Health-check the install — hooks executable, core.hooksPath set, gates wired, discipline loaded.
4
+ metadata:
5
+ kind: command
6
+ ---
7
+ # /crew-doctor
8
+ Verify Crewforth is actually *active* in this project (not just present on disk):
9
+ 1. Run `bash .claude/eval/doctor.sh`.
10
+ 2. Read its report. It checks: VERSION present · every hook executable · the required git hooks (pre-commit,
11
+ commit-msg) present · **guard-bash actually blocks a force-push** (catches a hook that is present but neutered) ·
12
+ `core.hooksPath` points at `.claude/hooks` (else the §4.1/§4.2 commit trace + secret/bloat scan never runs) ·
13
+ `settings.json` valid and wiring the PreToolUse / UserPromptSubmit / Stop gates to **non-empty** hook arrays
14
+ (an empty `[]` wires nothing); SessionStart (rehydration) is reported as a warning if absent · **`./CLAUDE.md`
15
+ actually imports `.claude/DISCIPLINE.md`** — without that line the discipline sits on disk and never loads, which
16
+ every other check is blind to.
17
+ 3. For each ❌, apply the printed fix. Anything that changes git config or file permissions needs approval first —
18
+ show the exact command and wait.
19
+ 4. Read the **Readiness** block too. It is advisory (never changes the verdict) and asks a different question — is
20
+ this *project* set up to be worked on by agents: CLAUDE.md project section filled in · a project-specific skill
21
+ alongside Crewforth's generic ones · a devcontainer to sandbox agent commands · an MCP server · CLAUDE.md not
22
+ drifted behind the code. Report the gaps as suggestions, not as failures, and never "fix" them unasked — adding
23
+ a devcontainer or an MCP server is the user's call.
24
+ 5. Summarise: **healthy**, or the precise fixes applied/needed, plus the readiness score. If it's not a git repo,
25
+ note that the commit-time gates need `git init` + `git config core.hooksPath .claude/hooks`.
26
+ If the output contains a line starting with ⭐, pass it to the user verbatim as the LAST line of your summary —
27
+ it prints once per Crewforth version, and it is meant for the user, not for you.
28
+
29
+ If `.claude/eval/doctor.sh` doesn't exist, this is not a full (start.sh / adopt.sh) install — Crewforth is likely
30
+ running as a **plugin**, whose hooks are managed by Claude Code itself; there's nothing for the doctor to check.
31
+
32
+ **The eval scripts are installer-only, by decision.** `eval/` — `doctor.sh`, `smoke-test.sh`, `routing-eval.sh`,
33
+ `scan-skill.sh`, `utilization.sh` — ships with `start.sh` and `adopt.sh` and NOT with the plugin edition. The eval
34
+ scripts are developer instruments: they inspect an installation from outside it, and the plugin edition has no
35
+ installation to inspect. Say this plainly when someone asks why `/crew-doctor` reports nothing on a plugin install,
36
+ rather than treating it as a defect. The Studio panel ships in both editions (`/crew-studio`).
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: crew-gates
3
+ description: Gate report — what the tool-level gates actually did, and which rules nothing has tripped.
4
+ argument-hint: "[--log <path>]"
5
+ disable-model-invocation: true
6
+ metadata:
7
+ kind: command
8
+ ---
9
+
10
+ Run the report and read it back to the user:
11
+
12
+ ```bash
13
+ bash .claude/eval/gate-report.sh $ARGUMENTS
14
+ ```
15
+
16
+ Then interpret it in two sentences, in the user's language:
17
+
18
+ - **Exit 3 (no log).** Say plainly that gate activity is *not measured* — not that the gates never fired. Offer
19
+ the one-line opt-in the report prints. Do not present the rule count as if it were evidence of anything.
20
+ - **Exit 0.** Lead with the verdict split (how often the gates asked vs blocked). Then the notable rules: a rule
21
+ firing repeatedly is worth a conversation ("something keeps reaching for this"), and the not-observed list is
22
+ a prompt to check whether a rule *can* still match, not a delete list. `smoke-test` is what proves a rule can
23
+ fire; this only shows what tripped it.
24
+
25
+ Never infer that a gate works because it appears in the report, or that it is broken because it does not.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: crew-handoff
3
+ description: Session handover — SESSION_STATE.md + /clear suggestion.
4
+ metadata:
5
+ kind: command
6
+ ---
7
+ # /crew-handoff
8
+ applies the `handoff` skill:
9
+ 1. Read the measured fill: the `🔋 Session` line, or `bash .claude/hooks/context-usage.sh --verbose`.
10
+ 2. Write an actionable handover to `docs/SESSION_STATE.md`: Done · In progress (exactly where) · Next step · Open decisions · File pointers · Blockers.
11
+ 3. Preserve decision rationale; the next session should not start from scratch.
12
+ 4. **Cold-reader pass** (`handoff/references/cold-reader.md`): the questions are written from the work BEFORE the
13
+ file, then answered from the file alone. Anything unanswerable goes back into the handover and is re-checked.
14
+ 5. Once written — and only once every question is answerable — suggest `/clear`.
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: crew-plan
3
+ description: Spec-first planning — break down ambiguously scoped work with crew-planner.
4
+ argument-hint: "[task/feature description]"
5
+ metadata:
6
+ kind: command
7
+ ---
8
+ # /crew-plan
9
+ Argument: $ARGUMENTS
10
+
11
+ Delegate to the @agent-crew-planner agent (spec-planning skill). For ambiguously scoped work:
12
+ 1. Clarify the goal + what's out of scope.
13
+ 2. Split into vertical slices; give each task a measurable acceptance criterion.
14
+ 3. Order by dependency + put the riskiest first.
15
+ 4. Ask about ambiguities with **explicit options**; write down the assumptions.
16
+
17
+ Write the output to `docs/PLAN.md`, return a summary + pointer to the main thread (token-budget).
18
+ Do NOT write code — this is a planning step.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: crew-review
3
+ description: Review pass — review + security + quality gates.
4
+ metadata:
5
+ kind: command
6
+ ---
7
+ # /crew-review
8
+ Run the change set through the review trio (read-only). **The audits go out in parallel; the reviewer closes.**
9
+ 1. **At once, in ONE message** — several `Agent` calls, because none of these writes code and so there is
10
+ nothing to serialise:
11
+ - @agent-crew-security-expert (security-scan) — auth/IDOR/injection/secret; findings with severity.
12
+ - @agent-crew-performance-expert (`performance`) — hot path, query/loop, render, payload. Reports
13
+ **candidates** (reasoned) and **findings** (measured) separately; an unmeasured claim is never a verdict.
14
+ - @agent-crew-privacy-agent (`privacy-compliance`) — **when the diff touches personal data**: legal basis,
15
+ minimisation, retention, transfer.
16
+ 2. (if SonarQube is in use) **sonarqube-check** — 0/0/0/0 gate (language-agnostic).
17
+ 3. **Last, and only once 1-2 are clean:** @agent-crew-review-agent (code-review) — "does it improve overall code
18
+ health"; severity-ranked comments. It is the closing reviewer in every writing agent's Coordination ("at
19
+ closure, report findings to crew-review-agent"), so it reads a diff the audits have already cleared — not the
20
+ other way round.
21
+
22
+ Each agent returns a **short summary** to the main thread; raw output goes to `docs/` if needed. Do NOT modify code;
23
+ collect findings in severity order, and leave the fix to the relevant expert.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: crew-ship
3
+ description: DoD gate + commit proposal (waits for approval).
4
+ metadata:
5
+ kind: command
6
+ ---
7
+ # /crew-ship
8
+ Closure flow — in order:
9
+ 1. **DoD gate:** was `/simplify` applied · are tests green (@agent-crew-test-expert) · (if SonarQube is used) `sonarqube-check` 0/0/0/0.
10
+ If any one is red, **STOP**, report what is missing; no deferral.
11
+ 2. If clean, @agent-crew-commit-agent (commit-message) **proposes** an atomic Conventional Commit from the staged diff.
12
+ 3. Commit runs ONLY if the user says "commit" (§4.4). "Done" is not approval.
13
+ 4. **Close the board item too** (`teamboard`), when one is held. Closing work is one movement: ask what shipped,
14
+ run `board.sh done <id> "<note>"`, name the items it unblocked, and offer the next one as options. Do not
15
+ leave this to the user to remember — a claim nobody closes reads to the whole team as still in progress, and
16
+ the items behind it stay unclaimable. If a decision came out of this work that changes how other items must
17
+ be done, record it with `board.sh decide` in the same breath; it is worthless in a session nobody else reads.
18
+
19
+ No destructive operations (§4.5). No AI trace / vendor name in the message (§4.1/§4.2 — the hook already scans).
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: crew-skill
3
+ description: Author or change a Crewforth component (skill / agent / command) against the template and its gates.
4
+ disable-model-invocation: true
5
+ metadata:
6
+ kind: command
7
+ ---
8
+ # /crew-skill
9
+
10
+ `AGENT_TEMPLATE.md` states the contract for a component. Full installs carry it (refreshed on `update`); the
11
+ plugin edition does not. No gate routes to it, so this command is the route, and it ends in the gates.
12
+
13
+ **A component is not finished when it is written. It is finished when the suite says so.**
14
+
15
+ ## 1. Decide the shape before writing anything
16
+ Read `.claude/AGENT_TEMPLATE.md`. Then answer, in one line each:
17
+
18
+ - **What is the smallest form that works?** A rule in an existing skill's body beats a new skill; a new skill beats
19
+ a new agent. Every new skill costs its NAME in every session, forever, for every user — and its description too,
20
+ until the listing overflows its budget, at which point descriptions start being dropped from the skills you
21
+ invoke least. Say what that buys.
22
+ - **Who reaches it?** An agent, a command, or the discipline's trigger map. If the answer is "the model will notice
23
+ the description", stop — that is the dark component §3b exists to catch.
24
+ - **Read or write?** A component that reports and a component that changes files do not belong together: different
25
+ risk, different done-criterion. Split them.
26
+ - **Is it stack-neutral?** Anything shipped to every profile names no language, framework or vendor as *the* case.
27
+ The example you have in mind is not the scope.
28
+
29
+ ## 2. Write it
30
+ - Skill: `.claude/skills/<name>/SKILL.md` — the directory name and the `name:` field must match. Keep the body
31
+ lean; depth goes to `references/*.md` and is loaded on demand.
32
+ - Agent: a thin trigger — *who and when*. The *how* lives in the skill it applies; do not copy the method in.
33
+ - The `description` says **when to reach for this**, not what its author knows.
34
+
35
+ ## 3. Register it — the cascade, in this order
36
+ Skipping one of these is how a component ships half-installed:
37
+ 1. **Route it.** Name it in an agent body, a command, or the trigger map. §3b checks exactly this.
38
+ 2. **Golden case.** Add a positive line to `.claude/eval/golden-routing.txt` — and a **negative** one
39
+ (`prompt|!target`) for a neighbour it must NOT steal.
40
+ 3. **Catalog + counts** *(Crewforth repository only — `packaging/` is not installed anywhere)*.
41
+ Add the Turkish summary line (`packaging/skill-summaries.tr.tsv`; agents and commands have their own `.tr.tsv`),
42
+ which the site build requires (`cd site && npm ci && npm run build`), and the diagrams if the component set changed:
43
+ `node packaging/gen-diagrams.mjs` writes `assets/`, and smoke fails while any of them is stale.
44
+ 4. **Plugin edition** *(Crewforth repository only)*. `bash packaging/build-plugin.sh` — in the same commit, or the
45
+ release stops at the sync gate.
46
+ 5. **Budget.** A new skill moves `BUDGET_SKILLS`; raise it in the same commit with the justification comment the
47
+ file's convention requires. Never raise it to make a red gate green without saying why.
48
+
49
+ ## 4. Prove it — run all four, in this order
50
+ ```
51
+ bash .claude/eval/scan-skill.sh .claude/skills/<name> # supply-chain: SAFE, and rc=3 means NOT scanned
52
+ bash .claude/eval/routing-eval.sh # the golden case, positive and negative
53
+ bash .claude/eval/smoke-test.sh # structure, budget, §3b routed, cross-links
54
+ CREW_NO_STAR=1 bash .claude/eval/doctor.sh # the live install still healthy (star line off here)
55
+ ```
56
+
57
+ In the plugin edition none of the four commands above exist — it ships no `eval/` at all — so there the proof is
58
+ the Crewforth repository's own suite, not a local run.
59
+
60
+ **Do not finish while §3b is red.** "It works when I invoke it by name" is not the claim being tested — the claim
61
+ is that something reaches it without being told to. If the suite skips a case, that is not a pass: chase why.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: crew-studio
3
+ description: Open Crewforth Studio — the visual panel for what agents are doing, what they spent, and which gates fired.
4
+ argument-hint: "[--port <n>]"
5
+ disable-model-invocation: true
6
+ metadata:
7
+ kind: command
8
+ ---
9
+ # /crew-studio
10
+
11
+ Start the panel and hand the user its URL. Never leave a half-started server behind: if any step below
12
+ fails, say which one and stop — do not improvise a different launch.
13
+
14
+ 1. **Resolve the panel.** Both editions carry it, in different places, and this one file serves both.
15
+
16
+ ```
17
+ PLUGIN_PANEL=${CLAUDE_PLUGIN_ROOT}/studio/server/index.js
18
+ ```
19
+
20
+ Claude Code replaces `${CLAUDE_PLUGIN_ROOT}` in this text **before you read it**, and only when this
21
+ command came from the plugin. So read the line above literally:
22
+
23
+ - It names a real absolute path → **plugin edition**. That is the panel. Use it.
24
+ - It still contains the characters `${CLAUDE_PLUGIN_ROOT}` → this is a **full install**. The panel is
25
+ `.claude/studio/server/index.js`.
26
+
27
+ The variable is not in the shell's environment — it is text substituted into this file — so never try
28
+ to read it with `printenv` or let a shell expand it. Never write the unbraced `$CLAUDE_PLUGIN_ROOT`
29
+ either; only the braced form is substituted, and the unbraced one reaches bash unset and collapses the
30
+ path to `/studio/server/index.js`.
31
+
32
+ Neither path is there? Then this is an install older than the release that added the panel:
33
+ `npx crewforth@latest update --here --yes` at the project root, or `/crew-update`.
34
+ Then stop. Do not go hunting for the panel anywhere else on disk.
35
+
36
+ **What the plugin edition cannot show.** A plugin install puts no `.claude/VERSION`, `kit.conf` or Crewforth
37
+ scripts into a project. So that project's row in the list has no version badge (each row reads its own
38
+ project), and for the project you opened the panel from, the inspector's gates, stats and board tabs
39
+ say "Not measured" with the reason, rather than zero. The gates tab can still list observed entries
40
+ below that. The plugin's Bash guard logs its blocks, approval prompts and `CLAUDE_GIT_OK` pre-authorised
41
+ git actions, and its gate-file write guard logs its blocks, to `.claude/gate-log.tsv` when the project
42
+ has a `.claude/` directory and the file is git-ignored or the project is not a repo. With `CREW_GATE_LOG`
43
+ set they write wherever it points instead, and the panel reads only the project's own
44
+ `.claude/gate-log.tsv`, so a log sent anywhere else does not show. The commit scan and the board gate
45
+ refuse without writing a line. Say this once when you hand over the URL, so an honest blank is not
46
+ read as a broken panel. Everything else — the live agent graph, owned sessions, the permission bridge —
47
+ works the same in both editions.
48
+
49
+ 2. **Resolve a runtime — do not ask whether a name resolves.** `ensure-node.sh` sits one level ABOVE the panel
50
+ — beside the `server/` directory, not inside it. Both spellings, in full, so there is nothing to guess:
51
+
52
+ ```bash
53
+ bash .claude/studio/ensure-node.sh --explain # full install
54
+ bash ${CLAUDE_PLUGIN_ROOT}/studio/ensure-node.sh --explain # plugin edition (step 1 decided which)
55
+ ```
56
+
57
+ It prints the absolute path of a Node 18+ that actually runs, and exits 1 when there is none. Use that path;
58
+ do not assume it is `node`. It looks in places PATH does not reach — a version manager puts node on
59
+ PATH from a login shell only, so "nvm is installed" and "this shell can see node" are different
60
+ facts. And it runs what it finds rather than trusting the name: the Windows Store ships a stub that
61
+ satisfies `command -v`, prints nothing and exits 49.
62
+
63
+ **No `ensure-node.sh` beside the panel?** Then the panel predates the fetcher. Fall back to running
64
+ `node --version` and reading the output — 18+ required — and tell the user `/crew-update` brings the
65
+ fetcher along with everything else.
66
+
67
+ 3. **No runtime? Crewforth gets one — after the user says so.** Show them what it would do, then ask:
68
+
69
+ ```bash
70
+ bash <the same path as step 2> --plan # url, size, checksum source, target directory
71
+ ```
72
+
73
+ It fetches the current Node LTS from nodejs.org into `~/.claude/studio-runtime`, verifies it against
74
+ the published SHA-256 and refuses to install on a mismatch. Nothing else on the machine is touched:
75
+ no admin rights, no package manager, no PATH or profile edit. Deleting that one directory undoes it.
76
+
77
+ Ask with `AskUserQuestion`, and offer the alternative honestly — some people would rather their own
78
+ package manager owned it (`winget install OpenJS.NodeJS.LTS`, `brew install node`, or their distro's).
79
+ On yes:
80
+
81
+ ```bash
82
+ bash <the same path as step 2> --install # prints the node path on success
83
+ ```
84
+
85
+ Never run `--install --yes` on the user's behalf without that answer, and never install anything
86
+ system-wide.
87
+
88
+ 4. **Start it in the background**, from the project root, with the node from step 2 or 3:
89
+
90
+ ```bash
91
+ "$NODE" <panel> --open
92
+ ```
93
+
94
+ It prints a tokenised URL (`http://127.0.0.1:7777/?token=…`) and keeps running. **Report that URL**
95
+ whether or not a browser opened — over SSH or headless nothing can open, and a running server with no
96
+ way in is the worst outcome here. Tell the user it runs until they stop it, and how (Ctrl-C in that
97
+ shell, or kill the process). Do not hold it in a foreground tool call.
98
+
99
+ 5. **Port already in use.** Start it anyway and read what it says — the panel checks whether the holder is
100
+ another crewforth-studio first, and there is nothing for you to decide before it does. Two answers:
101
+
102
+ - `already running on port <n> (pid …) — reusing it`, followed by a tokenised URL and exit 0. That is a
103
+ panel someone else started, usually another session in this project. **Report that URL**; it is the same
104
+ panel, and the token in it is the running instance's own. Tell the user it belongs to whoever started it,
105
+ so closing this shell does not stop it.
106
+ - `held by something that is not a crewforth-studio panel` and exit 1. Then retry once with `--port <n+1>` and
107
+ report the new URL. If that port is taken too, stop and say both are taken rather than walking up the
108
+ range — something is listening that the user should look at.
109
+
110
+ The record that makes the first answer possible lives in `~/.claude/studio-runtime/instance-<port>.json`,
111
+ 0600, and holds the token. A panel that was killed leaves one behind; the next start probes it, gets no
112
+ answer, and deletes it — so a stale file reports as the second answer, never as a URL that does not open.
113
+
114
+ **Quoting the panel's output.** It prints two things that belong to this machine and nowhere else: the machine
115
+ NAME, and a URL carrying a freshly generated token. Handing both to the user is the whole point — they are
116
+ sitting at that machine. Putting either into anything that leaves it is not: a commit message, a PR, a
117
+ CHANGELOG, an issue, a report to another session. The name is a §4.3 private term, and the token is a
118
+ credential, loopback-scoped but still live for as long as that panel runs. So when you quote panel output
119
+ anywhere but to the user, drop the machine line and replace the token with `…`. `.private-terms.txt` catches
120
+ the name only if someone thought to add it; nothing catches the token.
121
+
122
+ **What the panel's scope actually is.** It reads `~/.claude/projects` — every Claude Code session on this
123
+ machine, not this project's. Starting it from a project root only decides which project it opens on. Say
124
+ that when you hand over the URL, so nobody reports "it shows other projects" as a bug.
125
+
126
+ Offline diagnostic, no browser and no tokens: `"$NODE" <panel> --selftest`.
127
+
128
+ **In the plugin edition this command is namespaced** — `/crewforth:crew-studio`, not
129
+ `/crew-studio`. Measured: the bare name does not resolve for a plugin command.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: crew-update
3
+ description: Check for a newer version, update, report what changed, then prompt /clear to reload.
4
+ metadata:
5
+ kind: command
6
+ ---
7
+ # /crew-update
8
+ Bring the Crewforth install up to the latest published version:
9
+ 1. **Detect install type.** If `.claude/VERSION` exists → a full install (steps below). If Crewforth runs as a
10
+ **plugin** (no `.claude/VERSION`), it updates through the plugin system: run
11
+ `claude plugin update crewforth@crewforth`, tell the user it applies when they restart Claude Code, and stop here.
12
+ 2. **Current version:** read `.claude/VERSION`.
13
+ 3. **Latest version:** `npm view crewforth version` (needs network). If it can't be reached,
14
+ say so and stop — don't guess.
15
+ 4. **Compare.** Already on the latest → report "up to date (vX)" and stop. Otherwise show **old → new**.
16
+ 5. **Before:** run `bash .claude/eval/update-guard.sh pre`. It records what is on disk (in `.claude/.state/`, nothing
17
+ else) and prints one of three verdicts:
18
+ - `clean` → nothing to report, go on.
19
+ - `UNCOMMITTED:` with the changed paths in `.claude/` and `CLAUDE.md` → show the user that list and **ask** —
20
+ with your question tool — whether to go ahead (the update rewrites Crewforth-owned files under `.claude/`), commit
21
+ or stash first, or stop. Continue only on their yes.
22
+ - `NOT IN GIT:` (a private install gitignores `.claude/` and `CLAUDE.md`, so edits there cannot be detected) →
23
+ pass its two lines to the user and go on; there is nothing to commit.
24
+ 6. **Update** — this rewrites Crewforth-owned files under `.claude/` (your `./CLAUDE.md` and project skills are
25
+ preserved). **Show this plan first, then run:** `npx --yes crewforth@latest update --here --yes`.
26
+ Two DIFFERENT `--yes` flags, both required for a non-interactive run: the one **before** the package name is
27
+ **npx's own** — it auto-confirms npx's `Ok to proceed?` install prompt, which reads the real TTY and IGNORES piped
28
+ input, so without it the command hangs before Crewforth even starts. The `--here --yes` **after** the package go to
29
+ the updater (`--here` = apply on the current branch, `--yes` = accept the smart defaults). Together they let it run
30
+ to completion instead of blocking on a prompt your shell can't answer.
31
+ If you'd rather review each handover decision yourself, tell the user to run `npx crewforth@latest update`
32
+ (no flags) in **their own terminal**, where both the npx and the interactive prompts work.
33
+ 7. **After:** run `bash .claude/eval/update-guard.sh post` and show the user its lists — added, changed, moved,
34
+ removed.
35
+ 8. **Verify:** run `/crew-doctor` (or `bash .claude/eval/doctor.sh`) so a bad/partial update surfaces immediately.
36
+ If the updater's or doctor's output contains a line starting with ⭐, pass it to the user verbatim as the LAST
37
+ line of your summary — it prints once per Crewforth version, and it is meant for the user, not for you.
38
+ 9. **Report** old → new + the headline changes. Take them from `.claude/.state/whats-new.md` — the updater writes
39
+ there the installed package's own CHANGELOG sections between the old and the new version. Do not fetch release
40
+ notes from the network. No file → say no release notes were included.
41
+ 10. **Reload (manual — a command can't do it itself):** the discipline in the running session is still the OLD one.
42
+ Tell the user to run **`/clear`** (or quit and relaunch Claude Code): a new session loads the updated
43
+ `.claude/DISCIPLINE.md`.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: db-migration
3
+ description: |
4
+ Apply schema migrations safely: detect the tool, classify the change by risk, gate destructive ones behind
5
+ approval, back up in prod, preview-apply-verify, roll back on failure.
6
+ Use when a schema change is about to be applied to any environment.
7
+ ---
8
+
9
+ # Database Migration
10
+
11
+ <!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
12
+ Trigger phrases: "migration", "schema change", "update database", "add column", "create table", "alter table", "add a column to", "adding a column to", "drop a column", "rename the table", "change the column type", "backfill"
13
+
14
+ A migration is very often a **one-way gate**: once applied, going back is expensive or impossible
15
+ unless it was planned for. That is why the heart of the flow is not running a command, but **classifying
16
+ the change by risk class before it passes through the gate** — let the safe one flow through, stop and
17
+ get approval for the destructive one, and keep a rollback path ready in every case. The skill works with
18
+ all common ORM/migration tools.
19
+
20
+ > **Crewforth adaptation (local, .claude/):** Engine and tool come from `CLAUDE.md ## Stack` or repo detection
21
+ > (`backend-architecture`). `crew-database-expert` applies it; **a destructive migration requires explicit approval (§4.5)**, commit/push with explicit approval (§4.4). Authorization/IDOR
22
+ > impact → **crew-security-expert**; personal-data retention → **crew-privacy-agent**. §4 Prohibitions apply.
23
+
24
+ ## Checklist
25
+ - [ ] Tool detected, pending migrations listed
26
+ - [ ] Every change classified (additive / destructive / ambiguous)
27
+ - [ ] Destructive/ambiguous changes approved by the user
28
+ - [ ] Backup taken (mandatory in prod, verified)
29
+ - [ ] Preview (dry-run) shown and approved
30
+ - [ ] Applied, schema verified post-apply
31
+ - [ ] Rollback path (tool or backup) ready
32
+
33
+ ---
34
+
35
+ ## 1. Detect the tool
36
+
37
+ Determine the tool from the file trace; if none is found, fall back to **raw SQL mode** (below); if several are found, ask the user.
38
+
39
+ | Trace | Tool |
40
+ |---|---|
41
+ | `prisma/schema.prisma` | Prisma |
42
+ | `knexfile.{js,ts}` | Knex |
43
+ | `.sequelizerc` / `config/config.json` + `models/` | Sequelize |
44
+ | `data-source.ts` (typeorm) / `ormconfig.ts` | TypeORM |
45
+ | `drizzle.config.ts` | Drizzle |
46
+ | `alembic.ini` / `alembic/` | Alembic |
47
+ | `manage.py` + `django` dependency | Django |
48
+ | `db/migrate/` + `rails` in Gemfile | Rails |
49
+ | `Microsoft.EntityFrameworkCore` in `*.csproj` + `Migrations/` | EF Core |
50
+
51
+ ---
52
+
53
+ ## 2. Classify the change — the heart of the flow
54
+
55
+ Extract the SQL to be applied (the "Preview" column in the matrix below) and place it into one of three classes:
56
+
57
+ | Class | Example operations | How it is handled |
58
+ |---|---|---|
59
+ | **Additive** (safe) | `CREATE TABLE`, nullable/defaulted `ADD COLUMN`, `CREATE INDEX`, non-destructive `ADD CONSTRAINT`, seed `INSERT` | Proceed in the normal flow |
60
+ | **Destructive** (dangerous) | `DROP TABLE/COLUMN/INDEX/CONSTRAINT`, `ALTER COLUMN … TYPE`, `TRUNCATE`, `DELETE` without WHERE, `RENAME` (breaks references) | **Always the approval gate** (below) |
61
+ | **Ambiguous** (human judgment) | `ALTER COLUMN`, `ADD COLUMN NOT NULL` without a default (blows up on a populated table), `UPDATE`, mixed migration | **Treat as destructive**, warn and ask |
62
+
63
+ **Approval gate (destructive/ambiguous):** list the operations one by one, show the environment and the target, wait for explicit approval. Even if "run" was said in the general flow, this gate requires separate approval.
64
+ ```
65
+ WARNING — this migration contains a destructive operation:
66
+ · DROP COLUMN "legacy_email" (table: users)
67
+ · ALTER COLUMN "status" TYPE integer (table: orders)
68
+ CANNOT BE UNDONE without a backup. Target: production (myapp_db @ db.example.com)
69
+ Continue? (yes / no)
70
+ ```
71
+ If rejected, stop immediately and propose a safer path (e.g. a new column + background copy instead of changing the type).
72
+
73
+ ---
74
+
75
+ ## 3. Backup (mandatory in prod)
76
+
77
+ Back up before any migration; it cannot be skipped in prod, and can be skipped in dev with approval. Then verify
78
+ **the backup was created and is not empty** — otherwise abort:
79
+ ```bash
80
+ [ -s "$BACKUP_FILE" ] || { echo "ERROR: Backup empty/not created — migration aborted."; exit 1; }
81
+ ```
82
+ Backup + restore + post-apply verify commands **per engine** (PostgreSQL · MySQL · SQLite):
83
+ **`references/backup-restore.md`** — read the one for your database, not all three.
84
+
85
+ ---
86
+
87
+ ## 4. Tool × life-cycle matrix
88
+
89
+ Once classification and backup are complete: **Preview → (approval) → Apply → Verify**.
90
+
91
+ Per-tool commands for those four steps and for rollback — Prisma, Knex, Sequelize, TypeORM, Drizzle, Alembic, Django, Rails, EF Core: **`references/tool-matrix.md`**. Look up the one the project uses.
92
+
93
+ **Rules:** **Always** show the preview before applying and get it approved (if the tool does not support a dry-run, treat the migration file itself as the preview). A destructive migration is not applied without passing the approval gate (§2). Show the full apply output; if there is an error, roll back.
94
+
95
+ **Additional post-apply verification** — compare against the expected schema using your engine's inspect command (`references/backup-restore.md`).
96
+
97
+ ---
98
+
99
+ ## 5. Rollback
100
+
101
+ First the tool-specific rollback (last column of the matrix); if that fails, restore from the backup (per-engine
102
+ restore command in **`references/backup-restore.md`**); then re-verify. If neither works, warn the user with the full error detail.
103
+
104
+ ---
105
+
106
+ ## No tool — raw SQL mode
107
+
108
+ If no tool is detected, manage numbered up/down files (every `up` has a `down`, each SQL inside `BEGIN`/`COMMIT`)
109
+ with a `_migrations` tracking table. Full pattern + example + the apply loop: **`references/raw-sql.md`**.
110
+
111
+ ---
112
+
113
+ ## Invariant rules
114
+ 1. **Classify first** — additive/destructive/ambiguous; treat the ambiguous as destructive.
115
+ 2. **Destructive = human approval** — a separate gate even in an automated flow.
116
+ 3. **Prod backup mandatory** — no apply without a verified backup.
117
+ 4. **Preview on by default** — opting out requires an explicit request.
118
+ 5. **Always verify after applying.**
119
+ 6. **Rollback path always ready** — tool or backup.
120
+ 7. **Show the target** — DB name/host/environment, before applying.
@@ -0,0 +1,33 @@
1
+ # Backup & restore — per engine
2
+
3
+ Take the backup **before** any migration (mandatory in prod; dev may skip with approval), then verify it is
4
+ non-empty or abort. Rollback order: the tool-specific rollback first (matrix, last column) → if it fails, restore
5
+ from this backup → re-verify. Read **only the section for your database**.
6
+
7
+ ## PostgreSQL
8
+ ```bash
9
+ # backup — compressed full dump
10
+ pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME -F c -f backup_$(date +%Y%m%d_%H%M%S).dump
11
+ # restore (rollback)
12
+ pg_restore -h $DB_HOST -U $DB_USER -d $DB_NAME --clean --if-exists $BACKUP_FILE
13
+ # post-apply verify
14
+ psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c "\d $TABLE"
15
+ ```
16
+
17
+ ## MySQL
18
+ ```bash
19
+ # backup
20
+ mysqldump -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME > backup_$(date +%Y%m%d_%H%M%S).sql
21
+ # restore (rollback)
22
+ mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME < $BACKUP_FILE
23
+ # post-apply verify
24
+ mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e "DESCRIBE $TABLE;"
25
+ ```
26
+
27
+ ## SQLite
28
+ ```bash
29
+ # backup
30
+ sqlite3 $DB_PATH ".backup 'backup_$(date +%Y%m%d_%H%M%S).db'"
31
+ # restore (rollback)
32
+ cp $BACKUP_FILE $DB_PATH
33
+ ```
@@ -0,0 +1,30 @@
1
+ # No tool — raw SQL mode
2
+
3
+ If no migration tool is detected, manage numbered up/down files; every `up` has a `down`, and every SQL runs
4
+ inside `BEGIN`/`COMMIT`.
5
+ ```
6
+ migrations/001_create_users.up.sql + 001_create_users.down.sql
7
+ ```
8
+ ```sql
9
+ -- 001_create_users.up.sql
10
+ BEGIN;
11
+ CREATE TABLE IF NOT EXISTS users (
12
+ id SERIAL PRIMARY KEY,
13
+ email VARCHAR(255) NOT NULL UNIQUE,
14
+ created_at TIMESTAMP NOT NULL DEFAULT NOW()
15
+ );
16
+ COMMIT;
17
+ -- 001_create_users.down.sql
18
+ BEGIN;
19
+ DROP TABLE IF EXISTS users;
20
+ COMMIT;
21
+ ```
22
+ Keep the applied ones in a tracking table; run only those with no record:
23
+ ```bash
24
+ psql … -c "CREATE TABLE IF NOT EXISTS _migrations(id SERIAL PRIMARY KEY, name TEXT UNIQUE, applied_at TIMESTAMP DEFAULT NOW())"
25
+ for f in migrations/*.up.sql; do
26
+ n=$(basename "$f" .up.sql)
27
+ [ "$(psql … -tAc "SELECT count(*) FROM _migrations WHERE name='$n'")" = 0 ] || continue
28
+ psql … -f "$f" && psql … -c "INSERT INTO _migrations(name) VALUES('$n')"
29
+ done
30
+ ```