create-claude-cabinet 0.20.0 → 0.21.0

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.
@@ -121,6 +121,39 @@ function setupOmega() {
121
121
  results.push('Downloaded cross-encoder reranker model');
122
122
  }
123
123
  } catch { /* non-fatal */ }
124
+
125
+ // Ensure MCP server extra is installed (added in v0.21)
126
+ try {
127
+ execSync(`"${VENV_PYTHON}" -c "import mcp"`, { stdio: 'pipe' });
128
+ } catch {
129
+ console.log(' Installing MCP server support...');
130
+ try {
131
+ execSync(`"${VENV_PYTHON}" -m pip install --quiet "omega-memory[server]"`, {
132
+ stdio: 'pipe', timeout: 120000,
133
+ });
134
+ results.push('Installed MCP server support');
135
+ } catch { /* non-fatal */ }
136
+ }
137
+
138
+ // Ensure omega MCP server is registered in global settings
139
+ try {
140
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
141
+ let settings = {};
142
+ if (fs.existsSync(settingsPath)) {
143
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch { settings = {}; }
144
+ }
145
+ if (!settings.mcpServers) settings.mcpServers = {};
146
+ if (!settings.mcpServers.omega) {
147
+ settings.mcpServers.omega = {
148
+ command: path.join(VENV_DIR, 'bin', 'omega'),
149
+ args: ['serve'],
150
+ };
151
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
152
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
153
+ results.push('Registered omega MCP server (global settings)');
154
+ }
155
+ } catch { /* non-fatal */ }
156
+
124
157
  return results;
125
158
  } catch {
126
159
  // Venv is broken — nuke and rebuild (D5)
@@ -133,13 +166,13 @@ function setupOmega() {
133
166
  execSync(`"${pythonPath}" -m venv "${VENV_DIR}"`, { stdio: 'pipe' });
134
167
  results.push('Created venv at ~/.claude-cabinet/omega-venv/');
135
168
 
136
- // 4. Install omega-memory
169
+ // 4. Install omega-memory (with MCP server support)
137
170
  console.log(' Installing omega-memory...');
138
- execSync(`"${VENV_PYTHON}" -m pip install --quiet omega-memory`, {
171
+ execSync(`"${VENV_PYTHON}" -m pip install --quiet "omega-memory[server]"`, {
139
172
  stdio: 'pipe',
140
173
  timeout: 120000,
141
174
  });
142
- results.push('Installed omega-memory');
175
+ results.push('Installed omega-memory (with MCP server)');
143
176
 
144
177
  // 5. Download embedding model at install time (D4)
145
178
  console.log(' Downloading embedding model...');
@@ -179,6 +212,41 @@ function setupOmega() {
179
212
  results.push('Omega hooks setup skipped (run `omega hooks setup` manually)');
180
213
  }
181
214
 
215
+ // 8. Register omega MCP server in global settings
216
+ // This enables omega_store(), omega_query(), etc. as MCP tools
217
+ // available to Claude Code directly (not just via hooks).
218
+ console.log(' Registering omega MCP server...');
219
+ try {
220
+ // Smoke-test: verify `omega serve` can start (requires mcp package)
221
+ execSync(
222
+ `echo '{}' | "${path.join(VENV_DIR, 'bin', 'omega')}" serve 2>&1 | head -1`,
223
+ { stdio: 'pipe', timeout: 10000 }
224
+ );
225
+
226
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
227
+ let settings = {};
228
+ if (fs.existsSync(settingsPath)) {
229
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch { settings = {}; }
230
+ }
231
+ if (!settings.mcpServers) settings.mcpServers = {};
232
+
233
+ // Only add if not already configured
234
+ if (!settings.mcpServers.omega) {
235
+ settings.mcpServers.omega = {
236
+ command: path.join(VENV_DIR, 'bin', 'omega'),
237
+ args: ['serve'],
238
+ };
239
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
240
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
241
+ results.push('Registered omega MCP server (global settings)');
242
+ } else {
243
+ results.push('Omega MCP server already registered');
244
+ }
245
+ } catch {
246
+ // Non-fatal — MCP tools are a convenience, hooks still work
247
+ results.push('Omega MCP server registration skipped (run `omega serve` manually to test)');
248
+ }
249
+
182
250
  return results;
183
251
  }
184
252
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Claude Cabinet — opinionated process scaffolding for Claude Code projects",
5
5
  "bin": {
6
6
  "create-claude-cabinet": "bin/create-claude-cabinet.js"
@@ -27,14 +27,12 @@ if [ -z "$FILE_PATH" ]; then
27
27
  fi
28
28
 
29
29
  # Only care about memory directory paths
30
- case "$FILE_PATH" in
31
- *.claude/memory/*|*.claude/projects/*/memory/*)
32
- ;;
33
- *)
34
- echo '{"decision":"allow"}'
35
- exit 0
36
- ;;
37
- esac
30
+ # Note: case patterns with * don't cross / boundaries in some shells,
31
+ # so we use [[ ]] substring matching for absolute path compatibility.
32
+ if [[ "$FILE_PATH" != *"/.claude/memory/"* ]] && [[ "$FILE_PATH" != *"/.claude/projects/"*"/memory/"* ]]; then
33
+ echo '{"decision":"allow"}'
34
+ exit 0
35
+ fi
38
36
 
39
37
  # Allow MEMORY.md index files (structural, not memory content)
40
38
  BASENAME=$(basename "$FILE_PATH")
@@ -56,3 +56,24 @@ verbally, or a constraint discovered through external research).
56
56
  Over-capturing degrades retrieval quality. The test: *"Would a future
57
57
  session benefit from knowing this?"* If yes, capture it. If it's just
58
58
  noise or ephemera, skip it.
59
+
60
+ ## Known Limitation: Auto-Memory System Prompt Conflict
61
+
62
+ Claude Code's built-in auto-memory system prompt describes a file-based
63
+ `.md` memory system (`/Users/<user>/.claude/projects/<project>/memory/`).
64
+ When omega is active, this conflicts — the system prompt tells Claude to
65
+ write `.md` files while CLAUDE.md and this rules file tell it to use
66
+ omega. The system prompt's instructions are strong and may override
67
+ project-level rules in some sessions.
68
+
69
+ **Mitigations:**
70
+ - The `omega-memory-guard` PreToolUse hook blocks flat markdown writes
71
+ when omega is available (structural enforcement, ~100% reliable)
72
+ - This rules file and CLAUDE.md omega instructions provide prompt-level
73
+ guidance (~80% reliable)
74
+ - If Claude creates a `.md` memory file despite these, the guard will
75
+ block it and redirect to `omega_store()`
76
+
77
+ This is a platform limitation — the auto-memory system prompt cannot
78
+ be suppressed from project configuration. The guard hook is the
79
+ primary defense.
@@ -11,10 +11,53 @@ import { join, dirname } from 'node:path';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import { createRequire } from 'node:module';
13
13
  import { createInterface } from 'node:readline';
14
+ import { existsSync, readFileSync, statSync } from 'node:fs';
14
15
  import * as lib from './pib-db-lib.mjs';
15
16
 
16
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
17
- const DB_PATH = process.env.PIB_DB_PATH || join(process.cwd(), 'pib.db');
18
+
19
+ // Resolve pib.db path, handling git worktrees where cwd may not contain
20
+ // the canonical database. Worktrees have a .git *file* (not directory)
21
+ // pointing to the main repo's .git/worktrees/<name>/ directory.
22
+ function resolveDbPath() {
23
+ if (process.env.PIB_DB_PATH) return process.env.PIB_DB_PATH;
24
+
25
+ const cwd = process.cwd();
26
+ const localDb = join(cwd, 'pib.db');
27
+
28
+ // If local pib.db exists and is non-trivial (>8KB), use it
29
+ if (existsSync(localDb)) {
30
+ try {
31
+ const stats = statSync(localDb);
32
+ if (stats.size > 8192) return localDb;
33
+ } catch { /* fall through to worktree detection */ }
34
+ }
35
+
36
+ // Check if we're in a git worktree (.git is a file, not a directory)
37
+ const gitPath = join(cwd, '.git');
38
+ if (existsSync(gitPath)) {
39
+ try {
40
+ const stats = statSync(gitPath);
41
+ if (stats.isFile()) {
42
+ // .git file contains: "gitdir: /path/to/main/.git/worktrees/<name>"
43
+ const content = readFileSync(gitPath, 'utf-8').trim();
44
+ const match = content.match(/^gitdir:\s*(.+)/);
45
+ if (match) {
46
+ // Walk up from .git/worktrees/<name> to find the main repo
47
+ const gitdir = match[1];
48
+ const mainGitDir = join(gitdir, '..', '..');
49
+ const mainRepoDir = dirname(mainGitDir);
50
+ const mainDb = join(mainRepoDir, 'pib.db');
51
+ if (existsSync(mainDb)) return mainDb;
52
+ }
53
+ }
54
+ } catch { /* fall through to default */ }
55
+ }
56
+
57
+ return localDb;
58
+ }
59
+
60
+ const DB_PATH = resolveDbPath();
18
61
 
19
62
  // ---------------------------------------------------------------------------
20
63
  // SQLite setup
@@ -231,7 +231,7 @@ check_description() {
231
231
  # "When" component: the description must contain an explicit trigger
232
232
  # phrase. This is heuristic but catches the common cases of describing
233
233
  # only the skill's scope without saying when to invoke it.
234
- local when_phrases='(Use when|Use at|Use after|Use before|Use during|Use to |Run when|Run at|Invoke when|Invoke during|Activated during|Activated when|Activates during|Activates when|Activate when|Trigger(s|ed)? (when|during|on)|Call when|Called during|Fires when)'
234
+ local when_phrases='(Use when|Use at|Use after|Use before|Use during|Use to |Run when|Run at|Invoke when|Invoke during|Activated during|Activated when|Activates during|Activates when|Activate(s|d)?(\s+\S+){0,4}\s+(when|during)|Trigger(s|ed)? (when|during|on)|Call when|Called during|Fires when)'
235
235
  if ! printf '%s' "$desc" | grep -qE "$when_phrases"; then
236
236
  fail "$file" "description-when" "no explicit trigger phrase" \
237
237
  "Add a 'when' component. Workflow skills: 'Use when...'. Cabinet members: 'Activated during audit/plan/execute...'. See $BEST_PRACTICES_DOC §Description."
@@ -63,27 +63,22 @@ mechanism:
63
63
  - This is the dogfood case — the project IS the upstream repo, so
64
64
  feedback goes directly into the local `feedback/` directory.
65
65
 
66
- **If linked** (not the source repo, but CC package resolves to a local
67
- directory — check if
68
- `node -e "console.log(require.resolve('create-claude-cabinet'))"` points
69
- to a local path rather than `node_modules`):
66
+ **Everything else** (consuming projects, whether linked or not):
70
67
 
71
- - Write to the CC repo's `feedback/` directory
72
- - Filename: `[source-project]-[date]-[short-title].md`
73
-
74
- **If not linked**, check whether `gh` is available (`gh auth status`):
68
+ Save to `~/.claude/cc-feedback-outbox.json` (create as `[]` if missing).
69
+ Append entry with fields: `source`, `date`, `component`, `title`, `body`,
70
+ `status: "pending"`, `delivered: false`.
75
71
 
76
- - **If `gh` works**, offer the choice:
77
- > Send as a GitHub issue, or save locally?
78
- > 1. GitHub issue (developer sees it directly)
79
- > 2. Save locally
72
+ The outbox is picked up by orient in the CC source repo next session
73
+ and delivered to `feedback/`. This avoids dirtying the CC repo's working
74
+ tree from consuming projects (which caused confusion when the linked
75
+ direct-write path was used).
80
76
 
81
- If GitHub: open issue on `orenmagid/claude-cabinet` with title
82
- `Field feedback: [short title]` and label `field-feedback`.
77
+ If `gh` is available (`gh auth status` succeeds), also offer:
78
+ > Also send as a GitHub issue for faster visibility?
83
79
 
84
- - **If no `gh`**: save to `~/.claude/cc-feedback-outbox.json` (create
85
- as `[]` if missing). Append entry with fields: `source`, `date`,
86
- `component`, `title`, `body`, `status: "pending"`.
80
+ If yes: open issue on `orenmagid/claude-cabinet` with title
81
+ `Field feedback: [short title]` and label `field-feedback`.
87
82
 
88
83
  ### 4. Confirm
89
84
 
@@ -79,6 +79,9 @@ With user confirmation:
79
79
  - Update `system-status.md` if it exists
80
80
  - Report the published version and npm URL
81
81
 
82
+ **DO NOT stop here.** Step 6 is mandatory — the publish is incomplete
83
+ until all local consumers are updated. Proceed to Step 6 immediately.
84
+
82
85
  ### 6. Update Local Consumers
83
86
 
84
87
  Read `~/.claude/cc-registry.json` for all registered CC projects. For
@@ -88,10 +91,12 @@ each project that is NOT the CC source repo itself:
88
91
  2. If it's older than the just-published version, update it:
89
92
  - `cd <path> && npx create-claude-cabinet@latest --yes`
90
93
  - Verify the install succeeded (`.ccrc.json` version matches)
91
- 3. After a successful update, commit the CC-managed files in the consumer:
92
- - `git -C <path> add .claude/ .mcp.json scripts/pib-db*.mjs scripts/pib-db-schema.sql scripts/*.cjs .ccrc.json`
93
- - Commit with message: `chore: update claude-cabinet to v<version>`
94
+ 3. After a successful update, commit ONLY CC-managed files in the consumer:
95
+ - First: `git -C <path> reset HEAD -- .` (clear any pre-staged files
96
+ the consumer may have project files staged from a prior session)
97
+ - Then: `git -C <path> add .claude/ .mcp.json scripts/pib-db*.mjs scripts/pib-db-schema.sql scripts/*.cjs .ccrc.json`
94
98
  - Only stage files that are actually modified (`git -C <path> diff --name-only` to check)
99
+ - Commit with message: `chore: update claude-cabinet to v<version>`
95
100
  - Do NOT push — leave that to the user
96
101
  4. Report which consumers were updated, committed, and which were already current
97
102
 
@@ -291,6 +291,11 @@ is in a good state for next time.
291
291
 
292
292
  ### 8. Persist Work
293
293
 
294
+ **Unmerged commits check:** Run `git log --oneline main..HEAD`. If
295
+ non-empty, enumerate the commits and ask: merge to main now, keep the
296
+ branch, or discard? This prevents orphan worktree branches that sit
297
+ unmerged with no surface signal.
298
+
294
299
  Commit and push the session's changes. Work that's done but not
295
300
  committed is half-closed — it lives locally but isn't durable. Persist
296
301
  before recording lessons, so the commit captures code and doc changes
@@ -389,68 +394,26 @@ rarer than project-specific skills.
389
394
 
390
395
  ### 12. Cabinet Check (core)
391
396
 
392
- Silently reflect: is this project's expertise coverage still right
393
- for what it's actually doing?
394
-
395
- This is the anti-entropy mechanism for the cabinet. Without
396
- it, a project can adopt a framework, start handling sensitive data, or
397
- grow complex enough to need architectural review — and none of the
398
- relevant expertise ever activates because nobody ran `/seed`.
397
+ Silently check: is this project's expertise coverage still right?
399
398
 
400
399
  **Two checks, both silent unless they find something:**
401
400
 
402
- **Check A — Uncovered technology.** Quickly scan what this session
403
- touched. Did the work involve a framework, library, data store, or
404
- infrastructure that isn't covered by any existing cabinet member? Compare
405
- against the cabinet members in `.claude/skills/cabinet-*/` and the
406
- merged committees (run `node scripts/resolve-committees.cjs`).
407
-
408
- Examples of what to catch:
409
- - Session used Mantine components but there's no framework-quality
410
- cabinet member
411
- - Session wrote database queries but there's no data-integrity
412
- cabinet member
413
- - Session built UI but accessibility isn't in any active committee
414
-
415
- **Check B Dormant cabinet member that should be active.** Are there
416
- cabinet members installed in the project that aren't in any committee
417
- (check merged output from `node scripts/resolve-committees.cjs`), but based on the last few sessions, probably should be? A
418
- cabinet member sitting dormant while the project does exactly the kind of
419
- work it's designed to review is a waste.
420
-
421
- **Most sessions: nothing.** These checks should be completely silent
422
- when nothing is off. Don't mention cabinet members if everything is fine.
423
-
424
- **When there's a gap:** Explain it plainly — no jargon about
425
- "cabinet members" or "committees." Talk about what the project is missing
426
- in terms of what it would DO for them:
427
-
428
- > "You've been building UI for the last few sessions, but nothing is
429
- > checking whether it works well on phones or is usable for people
430
- > with accessibility needs. I can set that up so it gets checked
431
- > automatically when you run quality reviews. Want me to?"
432
-
433
- or:
434
-
435
- > "You're using Mantine a lot now. There's a specialist review that
436
- > checks whether you're getting the full value from it — catching
437
- > things like hand-rolling components that Mantine already provides.
438
- > Want me to turn that on?"
439
-
440
- or:
441
-
442
- > "This project has gotten complex enough that it might help to have
443
- > something watching whether the overall architecture still makes
444
- > sense as it grows. Want me to set that up?"
445
-
446
- If the user says yes, either:
447
- - Activate a dormant cabinet member (add it to `committees-project.yaml`)
448
- - Run `/seed` to build a new one
449
- - Install a Tier 3 cabinet member that isn't in the project yet
450
-
451
- If the user says no, move on. Don't re-suggest the same gap next
452
- session. Track declined suggestions in system-status.md or equivalent
453
- so you don't nag.
401
+ **Check A — Uncovered technology.** Did this session touch a framework,
402
+ library, or infrastructure not covered by any cabinet member? Compare
403
+ against `.claude/skills/cabinet-*/` and merged committees
404
+ (`node scripts/resolve-committees.cjs`).
405
+
406
+ **Check B — Dormant member that should be active.** Are there installed
407
+ cabinet members not in any committee that the recent work would benefit
408
+ from?
409
+
410
+ **Most sessions: nothing.** Silent when nothing is off.
411
+
412
+ **When there's a gap:** Explain plainly what the project is missing
413
+ in terms of what it would DO for them (no jargon about "cabinet
414
+ members" or "committees"). If the user wants it, activate the member
415
+ via `committees-project.yaml`, `/seed`, or Tier 3 install. Track
416
+ declined suggestions so you don't re-suggest next session.
454
417
 
455
418
  ### 13. Capture Loose Ends (core)
456
419
 
@@ -97,12 +97,23 @@ After closing actions, check the `feedback/` directory for field feedback
97
97
  files from consuming projects. For each file, evaluate whether this
98
98
  session's work addressed the friction described.
99
99
 
100
+ **Trigger conditions** (check ALL, not just git-log matching):
101
+ 1. **Git-log matching:** Cross-reference feedback files against this
102
+ session's changed files and commit messages.
103
+ 2. **Project-name matching:** If any closed project's name or description
104
+ contains "feedback", "remediation", or "field feedback", explicitly
105
+ scan ALL feedback files and present them for resolution — the project
106
+ was likely created to address them.
107
+ 3. **Component matching:** If this session edited files in a component
108
+ mentioned by a feedback file (e.g., session edited `cc-publish/SKILL.md`
109
+ and a feedback file has `component: skills/cc-publish`), surface it.
110
+
100
111
  **How to check:**
101
112
  1. Scan `feedback/` for `.md` files (skip `.gitkeep`)
102
113
  2. For each file, read the `component` from frontmatter and the friction
103
114
  description
104
- 3. Cross-reference against this session's git log and changed files
105
- did the session fix or address the reported friction?
115
+ 3. Apply all trigger conditions above any match means the feedback
116
+ should be presented for resolution
106
117
  4. For each resolved item, present to the user:
107
118
  > "Field feedback resolved: [title] from [source] — [one-line summary
108
119
  > of what fixed it]. Delete the feedback file? (yes/no)"
@@ -195,11 +195,21 @@ Group the plan's implementation steps by logical file groups
195
195
 
196
196
  For each group:
197
197
  1. Make the changes
198
- 2. **Checkpoint 2: File Group Review** — if cabinet members are active,
198
+ 2. **Post-change verification:**
199
+ - **After component extraction or file splitting:** Immediately run
200
+ the project's type checker (`tsc --noEmit`, `mypy`, `pyright`, etc.)
201
+ before writing any further code. Extraction frequently orphans
202
+ imports — the type checker catches this before it compounds.
203
+ - **Before using unfamiliar UI component props:** Read the component's
204
+ `.d.ts` file from `node_modules/<package>/` or check the type
205
+ definitions. Never guess prop names from memory of prior framework
206
+ versions — prop APIs change between major versions and guessing
207
+ wastes build cycles.
208
+ 3. **Checkpoint 2: File Group Review** — if cabinet members are active,
199
209
  spawn agents for ONLY cabinet members matching the changed files. Each
200
210
  receives the git diff for this file group + plan context. Same
201
211
  escalation rules as Checkpoint 1.
202
- 3. If all continue, move to the next group
212
+ 4. If all continue, move to the next group
203
213
 
204
214
  File-group granularity keeps reviews focused. A cabinet member reviewing
205
215
  3 changed files gives better feedback than one reviewing 30.
@@ -76,6 +76,14 @@ The skeleton always does something reasonable when a phase file is absent.
76
76
  Phase files customize, not enable. Use `skip: true` when you actively
77
77
  don't want a phase to run — not even the default.
78
78
 
79
+ **Phase separation principle:** Phases that both gather data and act on
80
+ the results should use clear structural separation. Without it, the
81
+ model tends to treat data-gathering as completion — running a query
82
+ satisfies the "do something" impulse and the acting step gets skipped.
83
+ Use numbered steps with explicit transitions: "1. Query X. 2. **Now
84
+ act on the results above:** [specific action]." If the acting step is
85
+ critical, mark it as `**BLOCKING**` — querying is not processing.
86
+
79
87
  ## Why This Matters
80
88
 
81
89
  If Claude Code starts a session without reading what happened last time,
@@ -133,6 +141,20 @@ sessions, and project-specific context.
133
141
  > ⚠ Found N unmigrated memory files in .claude/memory/.
134
142
  > Run: `python3 scripts/migrate-memory-to-omega.py --dry-run`
135
143
 
144
+ - **Deployment method detection:** Check for deployment indicators and
145
+ surface the deploy command in the briefing so sessions don't default
146
+ to wrong deployment methods (e.g., `git push` when the project uses
147
+ `railway up`):
148
+ - `railway.toml` → Railway (`railway up --detach`)
149
+ - `fly.toml` → Fly.io (`fly deploy`)
150
+ - `vercel.json` or `.vercel/` → Vercel (`vercel --prod`)
151
+ - `netlify.toml` → Netlify (`netlify deploy --prod`)
152
+ - `.github/workflows/deploy*` → GitHub Actions (push triggers deploy)
153
+ - `Dockerfile` alone → manual container deploy (surface as "Docker-based,
154
+ check deployment docs")
155
+
156
+ If found, include in the briefing: "**Deployment:** [method] via [command]"
157
+
136
158
  The goal: build a mental model of where things stand before doing
137
159
  anything else.
138
160
 
@@ -277,6 +299,34 @@ Surface missing plugins as health warnings:
277
299
 
278
300
  Advisory only — do not block orient for missing plugins.
279
301
 
302
+ ### Unmerged branch check
303
+
304
+ Scan for branches with commits ahead of the main branch that may
305
+ represent unmerged work from prior sessions (especially worktree
306
+ sessions that ended without merging):
307
+
308
+ ```bash
309
+ git for-each-ref --format='%(refname:short)' refs/heads/ | while read branch; do
310
+ ahead=$(git log --oneline main..$branch 2>/dev/null | wc -l | tr -d ' ')
311
+ if [ "$ahead" -gt 0 ]; then
312
+ echo "$branch: $ahead commits ahead"
313
+ fi
314
+ done
315
+ ```
316
+
317
+ Also check `git worktree list` for active worktrees whose branches
318
+ have diverged from main.
319
+
320
+ Surface as advisory:
321
+ > ⚠ Branch `feature-x` has N commits ahead of main (last touched
322
+ > [date]). Merge, continue working, or discard?
323
+
324
+ **Known platform limitation:** The Claude Code Agent tool with
325
+ `isolation: "worktree"` branches from the remote tracking ref, not
326
+ local HEAD. Unpushed commits are invisible to worktree agents. Always
327
+ push before spawning worktree agents, or manually review their diffs
328
+ for spurious deletions of unpushed work.
329
+
280
330
  > **Orient vs Pulse vs Audit:** Orient health checks verify *operational*
281
331
  > state — is the system running, is data fresh, are processes alive?
282
332
  > Pulse (embedded in orient) verifies *descriptive* accuracy — do counts
@@ -42,8 +42,14 @@ Phase files have three states:
42
42
  Read `phases/validators.md` for the list of project-specific checks.
43
43
  Each validator has a name, command, and description of what it catches.
44
44
 
45
- If `phases/validators.md` is empty or missing, report that no validators
46
- are configured and suggest the user define some.
45
+ **Default (absent/empty):** Auto-discover validators by scanning for
46
+ `scripts/*-validator.sh` (or `scripts/*-validator.cjs`). For each found,
47
+ treat the filename as the validator name and run it against the
48
+ appropriate targets (e.g., `skill-validator.sh` runs against all
49
+ `templates/skills/*/SKILL.md` files if they exist, or `.claude/skills/*/SKILL.md`).
50
+
51
+ If no phase file exists AND no validator scripts are discovered, report
52
+ that no validators are configured and suggest the user define some.
47
53
 
48
54
  ### 2. Run Each Validator
49
55