create-claude-cabinet 0.47.0 → 0.49.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.
Files changed (106) hide show
  1. package/README.md +1 -0
  2. package/lib/CLAUDE.md +240 -0
  3. package/lib/cli.js +202 -349
  4. package/lib/copy.js +75 -1
  5. package/lib/engagement-server-setup.js +1 -17
  6. package/lib/engagement-setup.js +1 -1
  7. package/lib/installer-gate.js +135 -0
  8. package/lib/metadata.js +20 -0
  9. package/lib/modules.js +292 -0
  10. package/lib/mux-setup.js +1 -17
  11. package/lib/project-context.js +16 -96
  12. package/lib/settings-merge.js +148 -9
  13. package/lib/site-audit-setup.js +23 -7
  14. package/lib/verify-setup.js +20 -9
  15. package/lib/watchtower-setup.js +1 -5
  16. package/package.json +1 -1
  17. package/templates/CLAUDE.md +1159 -0
  18. package/templates/briefing/_briefing-template.md +6 -11
  19. package/templates/cabinet/eval-protocol.md +27 -10
  20. package/templates/cabinet/watchtower-contracts.md +124 -0
  21. package/templates/cabinet/worktree-invocation-contract.md +59 -0
  22. package/templates/engagement/__tests__/checklist-visibility.test.mjs +76 -0
  23. package/templates/engagement/engagement-checklist.mjs +26 -2
  24. package/templates/engagement/engagement-schema.md +8 -6
  25. package/templates/engagement/pib-db-patches/pib-db-lib.mjs +102 -18
  26. package/templates/engagement/pib-db-patches/pib-db-schema.sql +1 -1
  27. package/templates/engagement/pib-db-patches/pib-db.mjs +4 -1
  28. package/templates/engagement/sql-constants.mjs +9 -1
  29. package/templates/engagement-server/__tests__/cross-tenant-auth.test.mjs +87 -0
  30. package/templates/engagement-server/server.mjs +65 -40
  31. package/templates/hooks/action-completion-gate.sh +10 -0
  32. package/templates/hooks/action-quality-gate.sh +10 -0
  33. package/templates/hooks/skill-telemetry.sh +10 -5
  34. package/templates/hooks/skill-tool-telemetry.sh +11 -5
  35. package/templates/mcp/pib-db.json +1 -1
  36. package/templates/mux/__tests__/mux-fail-loud.fixture.sh +4 -5
  37. package/templates/mux/__tests__/worktree-lifecycle.fixture.sh +214 -0
  38. package/templates/mux/__tests__/worktree-lifecycle.test.mjs +44 -0
  39. package/templates/mux/__tests__/worktree-status-clean.fixture.sh +245 -0
  40. package/templates/mux/__tests__/worktree-status-clean.test.mjs +89 -0
  41. package/templates/mux/bin/mux +44 -28
  42. package/templates/mux/config/worktree-cleanup.sh +57 -12
  43. package/templates/mux/config/worktree-dirty-check.sh +72 -2
  44. package/templates/mux/config/worktree-session-health.sh +401 -17
  45. package/templates/rules/enforcement-pipeline.md +0 -12
  46. package/templates/scripts/__tests__/api-usage-idle-gate.test.mjs +81 -0
  47. package/templates/scripts/__tests__/assessment-recalibration.test.mjs +387 -0
  48. package/templates/scripts/__tests__/batch-disposition.test.mjs +19 -0
  49. package/templates/scripts/__tests__/branch-diverged-reconcile.test.mjs +374 -0
  50. package/templates/scripts/__tests__/bsql-loader-verify.test.mjs +47 -0
  51. package/templates/scripts/__tests__/completion-review-reconcile.test.mjs +173 -0
  52. package/templates/scripts/__tests__/cross-ring-reader.test.mjs +12 -4
  53. package/templates/scripts/__tests__/detector-registry.test.mjs +152 -0
  54. package/templates/scripts/__tests__/draft-surfacing.test.mjs +433 -0
  55. package/templates/scripts/__tests__/generated-state-classification.test.mjs +228 -0
  56. package/templates/scripts/__tests__/inbox-assessment.test.mjs +341 -0
  57. package/templates/scripts/__tests__/memory-reachability.test.mjs +150 -0
  58. package/templates/scripts/__tests__/mirror-parity.test.mjs +54 -0
  59. package/templates/scripts/__tests__/resolve-project-slug.test.mjs +186 -0
  60. package/templates/scripts/__tests__/ring1-pib-error-surfacing.test.mjs +69 -0
  61. package/templates/scripts/__tests__/ring1-stale-open-counts.test.mjs +143 -0
  62. package/templates/scripts/__tests__/ring3-attribution.test.mjs +267 -0
  63. package/templates/scripts/__tests__/ring3-completion-filter.test.mjs +176 -0
  64. package/templates/scripts/__tests__/ring3-coverage-debt.test.mjs +377 -0
  65. package/templates/scripts/__tests__/ring3-last-session-pointer.test.mjs +80 -0
  66. package/templates/scripts/__tests__/ring3-thread-capture.test.mjs +213 -0
  67. package/templates/scripts/__tests__/watchtower-status-ring4.test.mjs +89 -0
  68. package/templates/scripts/audit-coherence-check.mjs +127 -0
  69. package/templates/scripts/audit-synth.mjs +117 -0
  70. package/templates/scripts/load-triage-history.js +23 -2
  71. package/templates/scripts/merge-findings.js +13 -3
  72. package/templates/scripts/patterns-scope-check.mjs +122 -0
  73. package/templates/scripts/pib-db-lib.mjs +116 -18
  74. package/templates/scripts/pib-db-mcp-server.mjs +2 -1
  75. package/templates/scripts/pib-db.mjs +2 -1
  76. package/templates/scripts/review-server.mjs +7 -12
  77. package/templates/scripts/triage-server.mjs +7 -8
  78. package/templates/scripts/watchtower-cross-ring-reader.mjs +38 -7
  79. package/templates/scripts/watchtower-inbox-assessment.mjs +857 -0
  80. package/templates/scripts/watchtower-lib.mjs +550 -9
  81. package/templates/scripts/watchtower-queue.mjs +197 -2
  82. package/templates/scripts/watchtower-ring1.mjs +567 -106
  83. package/templates/scripts/watchtower-ring2.mjs +213 -15
  84. package/templates/scripts/watchtower-ring3-close.mjs +679 -90
  85. package/templates/scripts/watchtower-status.sh +17 -0
  86. package/templates/scripts/work-tracker-server.mjs +26 -22
  87. package/templates/skills/audit/SKILL.md +24 -8
  88. package/templates/skills/audit/phases/structural-checks.md +22 -0
  89. package/templates/skills/briefing/SKILL.md +8 -0
  90. package/templates/skills/cabinet-accessibility/SKILL.md +1 -1
  91. package/templates/skills/cabinet-anthropic-insider/SKILL.md +29 -8
  92. package/templates/skills/cabinet-process-therapist/SKILL.md +45 -3
  93. package/templates/skills/cc-publish/SKILL.md +41 -7
  94. package/templates/skills/debrief/phases/audit-pattern-capture.md +13 -0
  95. package/templates/skills/inbox/SKILL.md +124 -6
  96. package/templates/skills/orient/SKILL.md +4 -0
  97. package/templates/skills/pulse/SKILL.md +10 -8
  98. package/templates/skills/session-handoff/SKILL.md +6 -8
  99. package/templates/skills/spring-clean/SKILL.md +1 -1
  100. package/templates/skills/spring-clean/phases/execute-decisions.md +1 -1
  101. package/templates/skills/validate/phases/validators.md +86 -0
  102. package/templates/skills/watchtower/SKILL.md +18 -26
  103. package/templates/watchtower/config.json.template +2 -1
  104. package/templates/workflows/deliberative-audit.js +258 -87
  105. package/templates/workflows/execute-group-complete.js +16 -2
  106. package/templates/workflows/execute-group-implement.js +16 -2
package/lib/copy.js CHANGED
@@ -104,6 +104,80 @@ function freezeRetain({ existing, isCcOwned }) {
104
104
  return isCcOwned ? { manifestHash: hashContent(existing) } : { omit: true };
105
105
  }
106
106
 
107
+ /**
108
+ * Pure ownership classifier for a SINGLE incoming template file — the
109
+ * non-interactive install cascade in one place (elegance-0002, act:a6377c82).
110
+ *
111
+ * The rule cascade below is the same one the walkAndCopy directory walk runs
112
+ * inline; cli.js's single-file install branch had hand-inlined a copy of it
113
+ * and the copy had already drifted — it was MISSING the project-owned-seed
114
+ * guard (step 1), so the single-file path could adopt/clobber a consumer
115
+ * overlay. Making cli.js delegate to this function removes that "kept in step
116
+ * by comments" fragility for the single-file path; the shared RULES
117
+ * (isProjectOwnedSeed, freezeRetain, recordSkip's omission semantics) already
118
+ * live in one place and are reused here.
119
+ *
120
+ * Order matches walkAndCopy exactly: seed → customized-phase → identical →
121
+ * frozen-hold → skipConflicts(manifest-driven) → interactive-prompt. Purely a
122
+ * decision — the caller performs all I/O, counter, and logging side effects.
123
+ *
124
+ * @returns {{ action: string, manifest: 'omit'|'identical'|'frozen'|'hash'|null, manifestHash?: string }}
125
+ * action ∈ 'seed' | 'preserve-phase' | 'skip-identical' | 'freeze-hold' |
126
+ * 'freeze-hold-new' | 'overwrite' | 'skip-omit' | 'copy-new' | 'prompt'
127
+ */
128
+ function classifyFileOwnership({
129
+ relPath,
130
+ destExists,
131
+ existingContent,
132
+ incoming,
133
+ isPhaseFile = false,
134
+ isInstructionPhase = false,
135
+ frozen = false,
136
+ isCcOwned = false,
137
+ skipConflicts = false,
138
+ }) {
139
+ // 1. Project-owned seed — before any existsSync branch (act:2001bf54).
140
+ if (isProjectOwnedSeed(relPath)) {
141
+ return { action: 'seed', manifest: 'omit' };
142
+ }
143
+ if (destExists) {
144
+ // 2. Customized phase file → preserve (independent of skipPhases). Ahead
145
+ // of the identical check to match the walk; behavior-identical since
146
+ // identical content never trips this guard.
147
+ if (
148
+ isPhaseFile &&
149
+ !isInstructionPhase &&
150
+ existingContent.trim() !== '' &&
151
+ existingContent.trim() !== incoming.trim()
152
+ ) {
153
+ return { action: 'preserve-phase', manifest: 'omit' };
154
+ }
155
+ // 3. Byte-identical → skip, recorded as CC-owned-unchanged.
156
+ if (existingContent === incoming) {
157
+ return { action: 'skip-identical', manifest: 'identical' };
158
+ }
159
+ // 4. Frozen module (act:c1013967): hold a CC-owned file at its on-disk
160
+ // hash; a project-owned file falls through.
161
+ if (frozen) {
162
+ const decision = freezeRetain({ existing: existingContent, isCcOwned });
163
+ if (decision.manifestHash) {
164
+ return { action: 'freeze-hold', manifest: 'frozen', manifestHash: decision.manifestHash };
165
+ }
166
+ }
167
+ // 5. Non-interactive install (--yes / existing-install): manifest-driven.
168
+ if (skipConflicts) {
169
+ return isCcOwned
170
+ ? { action: 'overwrite', manifest: 'hash' }
171
+ : { action: 'skip-omit', manifest: 'omit' };
172
+ }
173
+ // 6. Interactive → the caller prompts.
174
+ return { action: 'prompt', manifest: null };
175
+ }
176
+ // New file (dest absent): a frozen module holds it back; else copy.
177
+ if (frozen) return { action: 'freeze-hold-new', manifest: null };
178
+ return { action: 'copy-new', manifest: 'hash' };
179
+ }
180
+
107
181
  /**
108
182
  * Recursively copy files from src to dest, surfacing conflicts.
109
183
  * Returns { copied: string[], skipped: string[], overwritten: string[] }
@@ -317,4 +391,4 @@ function showDiff(existing, incoming, relPath) {
317
391
  console.log('');
318
392
  }
319
393
 
320
- module.exports = { copyTemplates, recordSkip, isProjectOwnedSeed, freezeRetain };
394
+ module.exports = { copyTemplates, recordSkip, isProjectOwnedSeed, freezeRetain, classifyFileOwnership };
@@ -20,7 +20,7 @@
20
20
  const fs = require('fs');
21
21
  const path = require('path');
22
22
  const os = require('os');
23
- const crypto = require('crypto');
23
+ const { sha256, compareVersions } = require('./installer-gate');
24
24
 
25
25
  const CC_HOME = path.join(os.homedir(), '.claude-cabinet');
26
26
  const GLOBAL_MANIFEST_PATH = path.join(CC_HOME, 'global-manifest.json');
@@ -42,22 +42,6 @@ const DATA_DIRS = [
42
42
  path.join(INSTALL_DIR, 'migrations'),
43
43
  ];
44
44
 
45
- function sha256(content) {
46
- return crypto.createHash('sha256').update(content).digest('hex');
47
- }
48
-
49
- function compareVersions(a, b) {
50
- const pa = a.split('.').map(Number);
51
- const pb = b.split('.').map(Number);
52
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
53
- const va = pa[i] || 0;
54
- const vb = pb[i] || 0;
55
- if (va > vb) return 1;
56
- if (va < vb) return -1;
57
- }
58
- return 0;
59
- }
60
-
61
45
  function readGlobalManifest() {
62
46
  if (!fs.existsSync(GLOBAL_MANIFEST_PATH)) return { files: {} };
63
47
  try {
@@ -177,7 +177,7 @@ function setupEngagement({ dryRun, projectDir } = {}) {
177
177
  target_fid TEXT,
178
178
  packet_id TEXT,
179
179
  kind TEXT NOT NULL
180
- CHECK(kind IN ('client_feedback','status_push','delegation','approval','note','packet_sent','packet_opened')),
180
+ CHECK(kind IN ('client_feedback','status_push','delegation','approval','note','packet_sent')),
181
181
  author TEXT NOT NULL,
182
182
  verdict TEXT CHECK(verdict IS NULL OR verdict IN ('approve','object','comment','none')),
183
183
  body TEXT CHECK(body IS NULL OR length(body) <= 10000),
@@ -0,0 +1,135 @@
1
+ /**
2
+ * installer-gate.js — the SINGLE source of truth for the version/hash gate
3
+ * logic shared by CC's five runtime installers (mux, watchtower,
4
+ * engagement-server, verify, site-audit).
5
+ *
6
+ * Before this helper, `sha256()` was copy-pasted three times,
7
+ * `compareVersions()` twice, and the equal-version "don't hard-skip, fall
8
+ * through and copy only what changed" fix (act:df1a1dc3) was applied twice
9
+ * to the per-file-manifest installers while the two tarball installers
10
+ * (verify, site-audit) still hard-skipped on an equal version — so a
11
+ * template edit without a package.json bump never propagated. This module
12
+ * unifies all of it so the rule lives in one place ("one concept, one
13
+ * place"; technical-debt-0003, act:a6377c82).
14
+ *
15
+ * Two installer shapes delegate here:
16
+ * - Per-file manifest (mux, watchtower, engagement-server): use `sha256`
17
+ * + `compareVersions`; equal version falls through to a per-file
18
+ * hash-compare in the caller.
19
+ * - Tarball (verify, site-audit): npm-pack a runtime source dir into a
20
+ * versioned tarball. `tarballIsStale` gives them the equal-version
21
+ * fall-through — re-pack when the runtime SOURCE content changed even
22
+ * though the version is identical, instead of hard-skipping.
23
+ */
24
+
25
+ 'use strict';
26
+
27
+ const crypto = require('crypto');
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+
31
+ /** SHA-256 hex digest of a string or Buffer. */
32
+ function sha256(content) {
33
+ return crypto.createHash('sha256').update(content).digest('hex');
34
+ }
35
+
36
+ /**
37
+ * Compare two dotted numeric version strings.
38
+ * @returns 1 if a > b, -1 if a < b, 0 if equal.
39
+ */
40
+ function compareVersions(a, b) {
41
+ const pa = String(a).split('.').map(Number);
42
+ const pb = String(b).split('.').map(Number);
43
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
44
+ const va = pa[i] || 0;
45
+ const vb = pb[i] || 0;
46
+ if (va > vb) return 1;
47
+ if (va < vb) return -1;
48
+ }
49
+ return 0;
50
+ }
51
+
52
+ /**
53
+ * Deterministic content hash of a source directory tree: every file's
54
+ * relative path + bytes, in a stable sorted order. Two trees with identical
55
+ * file contents hash identically regardless of readdir order. node_modules
56
+ * and .git are ignored (build/VCS artifacts, not source).
57
+ */
58
+ function hashSourceDir(dir, { ignore = ['node_modules', '.git'] } = {}) {
59
+ const entries = [];
60
+ const walk = (d, rel) => {
61
+ const names = fs.readdirSync(d, { withFileTypes: true });
62
+ for (const ent of names) {
63
+ if (ignore.includes(ent.name)) continue;
64
+ const full = path.join(d, ent.name);
65
+ const r = rel ? `${rel}/${ent.name}` : ent.name;
66
+ if (ent.isDirectory()) walk(full, r);
67
+ else if (ent.isFile()) entries.push([r, fs.readFileSync(full)]);
68
+ }
69
+ };
70
+ walk(dir, '');
71
+ entries.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
72
+ const hash = crypto.createHash('sha256');
73
+ for (const [r, buf] of entries) {
74
+ hash.update(r);
75
+ hash.update('\0');
76
+ hash.update(buf);
77
+ hash.update('\0');
78
+ }
79
+ return hash.digest('hex');
80
+ }
81
+
82
+ const SOURCE_HASH_FILE = '.source-hash';
83
+
84
+ /** Read the stored runtime-source hash sidecar from an install dir (or null). */
85
+ function readSourceHash(installDir) {
86
+ try {
87
+ return fs.readFileSync(path.join(installDir, SOURCE_HASH_FILE), 'utf8').trim() || null;
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ /** Persist the runtime-source hash sidecar next to the packed tarball. */
94
+ function writeSourceHash(installDir, hash) {
95
+ fs.mkdirSync(installDir, { recursive: true });
96
+ fs.writeFileSync(path.join(installDir, SOURCE_HASH_FILE), hash, 'utf8');
97
+ }
98
+
99
+ /**
100
+ * Equal-version fall-through decision for the TARBALL installers.
101
+ *
102
+ * A tarball is stale (must re-pack) when it is missing/truncated OR when the
103
+ * runtime source content changed since it was last packed (the sidecar hash
104
+ * differs / is absent). Otherwise it is fresh and the caller may skip. This
105
+ * is what stops verify/site-audit from hard-skipping a same-version reinstall
106
+ * that carries a real template change.
107
+ *
108
+ * @returns {{ stale: boolean, sourceHash: string, reason: string }}
109
+ */
110
+ function tarballIsStale({ tarballPath, installDir, runtimeSourceDir, minSize = 1024 }) {
111
+ const sourceHash = hashSourceDir(runtimeSourceDir);
112
+ let size = 0;
113
+ try {
114
+ size = fs.statSync(tarballPath).size;
115
+ } catch {
116
+ size = 0;
117
+ }
118
+ if (size <= minSize) {
119
+ return { stale: true, sourceHash, reason: size === 0 ? 'missing' : 'truncated' };
120
+ }
121
+ const stored = readSourceHash(installDir);
122
+ if (!stored) return { stale: true, sourceHash, reason: 'no-source-hash' };
123
+ if (stored !== sourceHash) return { stale: true, sourceHash, reason: 'source-changed' };
124
+ return { stale: false, sourceHash, reason: 'unchanged' };
125
+ }
126
+
127
+ module.exports = {
128
+ sha256,
129
+ compareVersions,
130
+ hashSourceDir,
131
+ readSourceHash,
132
+ writeSourceHash,
133
+ tarballIsStale,
134
+ SOURCE_HASH_FILE,
135
+ };
package/lib/metadata.js CHANGED
@@ -111,6 +111,25 @@ function setFrozenModules(projectDir, mods) {
111
111
  return normalized;
112
112
  }
113
113
 
114
+ /**
115
+ * Read the project's MCP opt-out list from `.ccrc.json` (`mcpOptOut`) — the
116
+ * CC-shipped MCP server keys this consumer declines (act:b770c323; the live
117
+ * specimen is flow's no-pib-db.md: engagement→work-tracking closure ships the
118
+ * pib-db files, but the consumer's own validator requires the MCP registration
119
+ * absent). Honored by the `.mcp.json` merge in lib/cli.js: opted-out keys are
120
+ * never added and, when present from a prior install, are removed (self-heal
121
+ * toward the declared intent). Same defensive shape as `readFrozenModules`:
122
+ * non-array reads as "no opt-outs"; a malformed `.ccrc.json` still throws in
123
+ * `read()` and halts the install. The key survives reinstalls via `create()`'s
124
+ * `...existing` spread — no threading required.
125
+ */
126
+ function readMcpOptOut(projectDir) {
127
+ const m = read(projectDir);
128
+ const raw = m && m.mcpOptOut;
129
+ if (!Array.isArray(raw)) return [];
130
+ return [...new Set(raw.filter((x) => typeof x === 'string'))];
131
+ }
132
+
114
133
  module.exports = {
115
134
  read,
116
135
  write,
@@ -120,4 +139,5 @@ module.exports = {
120
139
  METADATA_FILE,
121
140
  readFrozenModules,
122
141
  setFrozenModules,
142
+ readMcpOptOut,
123
143
  };
package/lib/modules.js ADDED
@@ -0,0 +1,292 @@
1
+ // MODULES is the manifest: every template path here is copied into a
2
+ // consumer's project on install. A skill/hook/script that exists under
3
+ // templates/ but is NOT listed in any module never ships — that is the
4
+ // orphan bug that left the whole built-in-memory layer uninstalled
5
+ // before v0.27.2 (test/manifest-integrity guards against recurrence).
6
+ //
7
+ // Intentional orphans (maintainer-only, must NOT ship to consumers) are
8
+ // allowlisted in test/manifest-integrity. Currently: skills/cc-publish
9
+ // (CC-source-repo release tooling, like scripts/migrate-all-consumers.js).
10
+ //
11
+ // This registry is DATA, deliberately extracted from lib/cli.js (a ~2000-line
12
+ // orchestrator) so the module map can be read and edited in isolation
13
+ // (architecture-0005). lib/cli.js requires + re-exports MODULES for
14
+ // back-compat; existing importers (`require('../lib/cli').MODULES`) are
15
+ // unchanged.
16
+ const MODULES = {
17
+ 'session-loop': {
18
+ name: 'Session Loop (orient + debrief)',
19
+ description: 'The briefing cycle. Claude starts each session informed, ends by preparing the next briefing.',
20
+ mandatory: true,
21
+ templates: [
22
+ 'skills/orient',
23
+ 'skills/orient-quick',
24
+ 'skills/debrief',
25
+ 'skills/debrief-quick',
26
+ 'skills/session-handoff',
27
+ // Instruction phases — always ship, overriding the default skip-phases rule in copy.js
28
+ 'skills/debrief/phases/audit-pattern-capture.md',
29
+ 'skills/debrief/phases/methodology-capture.md',
30
+ 'skills/debrief/phases/record-lessons.md',
31
+ 'skills/debrief/phases/upstream-feedback.md',
32
+ 'skills/debrief/phases/qa-handoff-sweep.md',
33
+ // Reversibility scaffolding (act:6fa60c7d) — standalone classic copies
34
+ // of orient/debrief, kept invokable so the orient/debrief retirement is
35
+ // reversible during the watchtower transition. They do NOT delegate to
36
+ // the live orient/debrief (which will change at retirement); they ARE
37
+ // the old skills under a new name. orient-classic ships SKILL.md only
38
+ // (its phases use skeleton defaults, like live orient); debrief-classic
39
+ // ships its SKILL.md + the same instruction phases as live debrief.
40
+ 'skills/orient-classic',
41
+ 'skills/debrief-classic',
42
+ 'skills/debrief-classic/phases/audit-pattern-capture.md',
43
+ 'skills/debrief-classic/phases/methodology-capture.md',
44
+ 'skills/debrief-classic/phases/record-lessons.md',
45
+ 'skills/debrief-classic/phases/upstream-feedback.md',
46
+ 'skills/debrief-classic/phases/qa-handoff-sweep.md',
47
+ 'skills/menu',
48
+ ],
49
+ },
50
+ 'hooks': {
51
+ name: 'Git Guardrails + Telemetry',
52
+ description: 'Block destructive git ops (force push, hard reset). Track skill usage via JSONL telemetry.',
53
+ mandatory: false,
54
+ default: true,
55
+ lean: true,
56
+ templates: ['hooks/git-guardrails.sh', 'hooks/cc-upstream-guard.sh', 'hooks/skill-telemetry.sh', 'hooks/skill-tool-telemetry.sh', 'hooks/work-tracker-guard.sh', 'hooks/action-quality-gate.sh', 'hooks/action-completion-gate.sh', 'hooks/memory-index-guard.sh', 'scripts/cc-drift-check.cjs', 'scripts/skill-usage.mjs'],
57
+ },
58
+ 'work-tracking': {
59
+ name: 'Work Tracking (pib-db or markdown)',
60
+ description: 'Track work items for orient/debrief. Default: SQLite (pib-db). Also supports markdown (tasks.md) or external systems. Choice made during /onboard.',
61
+ mandatory: false,
62
+ default: true,
63
+ lean: false,
64
+ templates: ['scripts/pib-db.mjs', 'scripts/pib-db-path.mjs', 'scripts/pib-db-lib.mjs', 'scripts/pib-db-mcp-server.mjs', 'scripts/pib-db-schema.sql', 'scripts/work-tracker-server.mjs', 'scripts/work-tracker-ui.html', 'skills/work-tracker', 'skills/spring-clean'],
65
+ needsDb: true,
66
+ },
67
+ 'planning': {
68
+ name: 'Planning + Execution (plan + execute)',
69
+ description: 'Structured planning with cabinet critique — members weigh in before you build.',
70
+ mandatory: false,
71
+ default: true,
72
+ lean: true,
73
+ templates: ['skills/plan', 'skills/execute', 'skills/execute/phases/post-impl-checklist.md', 'skills/debrief/phases/checklist-feedback.md', 'skills/checklist-discover', 'skills/generate-plan-groups', 'skills/execute-group', 'workflows/execute-group-implement.js', 'workflows/execute-group-complete.js', 'skills/investigate', 'cabinet/checkpoint-protocol.md', 'cabinet/worktree-invocation-contract.md', 'cabinet/elicitation-methods.md', 'cabinet/qa-dimensions-template.yaml', 'cabinet/checklist-stats-schema.md', 'scripts/qa-dimensions-validator.cjs', 'skills/orient/phases/checklist-status.md', 'skills/audit/phases/checklist-pruning.md'],
74
+ },
75
+ 'compliance': {
76
+ name: 'Compliance Stack (rules + enforcement)',
77
+ description: 'Scoped instructions that load by path. Enforcement pipeline for promoting patterns to rules.',
78
+ mandatory: false,
79
+ default: true,
80
+ lean: false,
81
+ templates: ['rules/enforcement-pipeline.md', 'rules/maintainability.md', 'rules/markdown-prose.md', 'rules/verify-before-asserting.md', 'rules/plan-before-bulk-or-irreversible-actions.md', 'rules/acknowledge-when-corrected.md', 'skills/unwrap', 'memory/patterns/_pattern-template.md', 'memory/patterns/pattern-intelligence-first.md'],
82
+ },
83
+ 'memory': {
84
+ name: 'Built-In Memory (cc-remember + reader + validator)',
85
+ description: 'Curated write/validate layer over Claude Code\'s built-in file memory. /cc-remember writes indexed memories, /memory browses them, validate-memory.mjs guards MEMORY.md integrity. Replaced the retired omega engine in v0.27.',
86
+ mandatory: false,
87
+ default: true,
88
+ // lean:true is load-bearing — it keeps the skill triad, scripts, and
89
+ // rule installing together as an atomic unit. compliance is lean:false,
90
+ // so the memory-capture rule must NOT live there or lean installs get a
91
+ // partial, incoherent set.
92
+ lean: true,
93
+ templates: [
94
+ 'skills/cc-remember',
95
+ 'skills/memory',
96
+ 'rules/memory-capture.md',
97
+ 'cabinet/memory-lifecycle-contract.md',
98
+ 'scripts/write-memory-file.mjs',
99
+ 'scripts/validate-memory.mjs',
100
+ // project-context.cjs ships as a co-located sibling: the two .mjs
101
+ // scripts require('./project-context.cjs') and consumers have no lib/.
102
+ 'scripts/project-context.cjs',
103
+ ],
104
+ },
105
+ 'audit': {
106
+ name: 'Audit Loop (audit + triage + cabinet)',
107
+ description: '27 expert cabinet members review your project. Convene the full cabinet or just one committee.',
108
+ mandatory: false,
109
+ default: true,
110
+ lean: true,
111
+ templates: [
112
+ 'skills/audit', 'skills/pulse', 'skills/triage-audit', 'skills/cabinet',
113
+ 'cabinet', 'briefing',
114
+ 'skills/cabinet-accessibility', 'skills/cabinet-anti-confirmation',
115
+ 'skills/cabinet-architecture', 'skills/cabinet-automation',
116
+ 'skills/cabinet-boundary-man',
117
+ 'skills/cabinet-anthropic-insider', 'skills/cabinet-cc-health',
118
+ 'skills/cabinet-data-integrity',
119
+ 'skills/cabinet-debugger', 'skills/cabinet-deployment',
120
+ 'skills/cabinet-historian',
121
+ 'skills/cabinet-organized-mind', 'skills/cabinet-process-therapist',
122
+ 'skills/cabinet-qa', 'skills/cabinet-record-keeper',
123
+ 'skills/cabinet-roster-check', 'skills/cabinet-security',
124
+ 'skills/cabinet-seo',
125
+ 'skills/cabinet-small-screen', 'skills/cabinet-speed-freak',
126
+ 'skills/cabinet-system-advocate', 'skills/cabinet-technical-debt',
127
+ 'skills/cabinet-usability', 'skills/cabinet-workflow-cop',
128
+ 'skills/cabinet-framework-quality', 'skills/cabinet-goal-alignment',
129
+ 'skills/cabinet-information-design', 'skills/cabinet-mantine-quality',
130
+ 'skills/cabinet-ui-experimentalist', 'skills/cabinet-user-advocate',
131
+ 'skills/cabinet-vision',
132
+ 'skills/cabinet-narrative-architect', 'skills/cabinet-interactive-storyteller',
133
+ 'skills/cabinet-elegance',
134
+ 'scripts/merge-findings.js', 'scripts/load-triage-history.js',
135
+ 'scripts/pib-db-path.mjs', // load-triage-history.js imports it (declared cross-module dep; also shipped by work-tracking)
136
+ 'scripts/triage-server.mjs', 'scripts/triage-ui.html',
137
+ 'scripts/finding-schema.json', 'scripts/resolve-committees.cjs',
138
+ 'scripts/review-server.mjs', 'scripts/review-ui.html',
139
+ // audit-coherence-check.mjs backs the /validate `audit-coherence`
140
+ // validator (act:faf23e4c); audit-synth.mjs is the tested source-of-truth
141
+ // mirror of deliberative-audit.js's inline mechanical merge (kept in
142
+ // lockstep by a parity test); patterns-scope-check.mjs is the /validate
143
+ // `patterns-scope` cross-project-contamination guard (act:62608a5d).
144
+ 'scripts/audit-coherence-check.mjs', 'scripts/audit-synth.mjs',
145
+ 'scripts/patterns-scope-check.mjs',
146
+ 'workflows/deliberative-audit.js', 'cabinet/critique-contract.md',
147
+ ],
148
+ },
149
+ 'lifecycle': {
150
+ name: 'Lifecycle (onboard + seed + cc-upgrade + link)',
151
+ description: 'Onboarding prepares the briefings. Seed proposes new cabinet members when you adopt new tech.',
152
+ mandatory: false,
153
+ default: true,
154
+ lean: true,
155
+ templates: ['skills/onboard', 'skills/seed', 'skills/cc-upgrade', 'skills/cc-link', 'skills/cc-unlink', 'skills/cc-extract', 'skills/cc-feedback', 'cabinet/elicitation-methods.md'],
156
+ },
157
+ 'validate': {
158
+ name: 'Validate',
159
+ description: 'Structural validation checks with unified summary. Define your own validators.',
160
+ mandatory: false,
161
+ default: true,
162
+ lean: false,
163
+ templates: ['skills/validate', 'scripts/skill-validator.sh'],
164
+ },
165
+ 'verify': {
166
+ name: 'Verification Harness (Cucumber + Playwright)',
167
+ description: 'Walkthrough verification with human-in-the-loop verdict pauses. Replaces flat AC lists with re-runnable user-journey scenarios. Includes /verify skeleton skill + cabinet-verify npm runtime + opt-in integration phases for /plan, /execute, /debrief.',
168
+ mandatory: false,
169
+ default: false,
170
+ lean: false,
171
+ templates: [
172
+ 'skills/verify',
173
+ 'skills/plan/phases/verify-plan.md',
174
+ 'skills/execute/phases/verify-emit.md',
175
+ 'skills/debrief/phases/verify-coverage.md',
176
+ 'skills/orient/phases/verify-backfill.md',
177
+ ],
178
+ postInstall: 'verify-setup',
179
+ },
180
+ 'site-audit': {
181
+ name: 'Site Audit (deployed-site quality)',
182
+ description: '14-check audit for deployed websites: performance, accessibility (3 engines), security, SEO, DNS, privacy, sustainability. Standalone HTML report + comparison mode.',
183
+ mandatory: false,
184
+ default: false,
185
+ lean: false,
186
+ templates: [
187
+ 'skills/cc-site-audit',
188
+ 'site-audit-runtime',
189
+ ],
190
+ postInstall: 'site-audit-setup',
191
+ },
192
+ engagement: {
193
+ name: 'Engagement management (with secure credential handoff)',
194
+ description: 'Ongoing client-engagement management built on pib-db: per-recipient packets (rendered projections of the work backlog), role-gated billing, client feedback flowing back as events, and secure credential handoff (encrypted capture via OS dialog, pluggable email/MCP/file transport). Three primary skills across consultant and client sides, plus redirect stubs. Requires work-tracking (the pib-db adapter is the engine\'s data source).',
195
+ mandatory: false,
196
+ default: false,
197
+ lean: false,
198
+ requires: ['work-tracking'],
199
+ postInstall: 'engagement-setup',
200
+ templates: [
201
+ 'skills/collab-client',
202
+ 'skills/collab-consultant',
203
+ 'skills/engagement-help',
204
+ 'skills/engagement-create',
205
+ 'skills/setup-accounts',
206
+ 'skills/guide',
207
+ 'engagement',
208
+ ],
209
+ },
210
+ watchtower: {
211
+ name: 'Watchtower (continuous background state)',
212
+ description: 'Replaces orient/debrief with continuous background processing. Three rings (mechanical cron, Claude intelligence, session-aware), ambient state injection via SessionStart hook, and an inbox for extracted knowledge and signals. Sessions start informed with minimal context cost.',
213
+ mandatory: false,
214
+ default: false,
215
+ lean: false,
216
+ templates: [
217
+ 'scripts/watchtower-validate.mjs',
218
+ 'cabinet/watchtower-contracts.md',
219
+ 'scripts/watchtower-lib.mjs',
220
+ 'scripts/watchtower-queue.mjs',
221
+ 'scripts/watchtower-routines.mjs',
222
+ 'scripts/watchtower-hook-runner.mjs',
223
+ 'scripts/watchtower-phase-shim.mjs',
224
+ 'scripts/watchtower-migrate-keys.mjs',
225
+ 'scripts/watchtower-sync.mjs',
226
+ 'scripts/watchtower-advisories.mjs',
227
+ 'cabinet/advisories-state-schema.md',
228
+ 'skills/inbox',
229
+ 'hooks/watchtower-session-start.sh',
230
+ 'scripts/watchtower-build-context.mjs',
231
+ 'scripts/watchtower-snapshot.mjs',
232
+ 'scripts/watchtower-narrative-corpus.mjs',
233
+ 'scripts/watchtower-cross-ring-reader.mjs',
234
+ 'scripts/watchtower-inbox-assessment.mjs',
235
+ 'scripts/watchtower-ring1.mjs',
236
+ 'scripts/watchtower-ring1-runner.sh',
237
+ 'skills/watchtower',
238
+ 'watchtower/com.claude-cabinet.watchtower-ring1.plist',
239
+ 'watchtower/watchtower-ring1.service',
240
+ 'watchtower/watchtower-ring1.timer',
241
+ 'scripts/watchtower-ring2.mjs',
242
+ 'scripts/watchtower-ring2-runner.sh',
243
+ 'watchtower/com.claude-cabinet.watchtower-ring2-fast.plist',
244
+ 'watchtower/com.claude-cabinet.watchtower-ring2-slow.plist',
245
+ 'watchtower/watchtower-ring2-fast.service',
246
+ 'watchtower/watchtower-ring2-fast.timer',
247
+ 'watchtower/watchtower-ring2-slow.service',
248
+ 'watchtower/watchtower-ring2-slow.timer',
249
+ 'hooks/watchtower-session-end.sh',
250
+ 'scripts/watchtower-ring3-close.mjs',
251
+ 'scripts/watchtower-ring4.mjs',
252
+ 'scripts/watchtower-ring4-runner.sh',
253
+ 'scripts/watchtower-status.sh',
254
+ 'skills/briefing',
255
+ 'skills/threads',
256
+ 'skills/catch-up',
257
+ 'skills/qa-handoff',
258
+ 'skills/qa-drain',
259
+ 'skills/close',
260
+ ],
261
+ },
262
+ mux: {
263
+ name: 'Mux (tmux project manager)',
264
+ description: 'Multi-project terminal manager. Desks (tmux sessions), auto-worktrees, trail logging, sticky notes, portal color-switching, and an MCP server for Claude. Installs to user-level paths (~/.local/bin/, ~/.config/mux/).',
265
+ mandatory: false,
266
+ default: false,
267
+ lean: false,
268
+ postInstall: 'mux-setup',
269
+ templates: ['skills/orient/phases/dx-captures.md', 'skills/dx-feedback'],
270
+ },
271
+ 'engagement-server': {
272
+ name: 'Engagement Server',
273
+ description: 'Central multi-engagement API server. Deploys once to Railway/Fly, serves all client engagements. User-level infrastructure.',
274
+ mandatory: false,
275
+ default: false,
276
+ lean: false,
277
+ requires: ['engagement', 'work-tracking'],
278
+ postInstall: 'engagement-server-setup',
279
+ templates: [],
280
+ },
281
+ 'bash-compress': {
282
+ name: 'Bash Output Compression Hook',
283
+ description: 'PostToolUse hook that compresses noisy Bash stdout (git status walls, npm/yarn install output, find/ls dumps) to reclaim context in long sessions. Off by default. stderr and error/warning lines pass through verbatim; every rewrite carries a visible [compressed] marker; fail-open on any error. Requires the hooks module (it wires into .claude/settings.json).',
284
+ mandatory: false,
285
+ default: false,
286
+ lean: false,
287
+ requires: ['hooks'],
288
+ templates: ['hooks/bash-output-compress.sh'],
289
+ },
290
+ };
291
+
292
+ module.exports = { MODULES };
package/lib/mux-setup.js CHANGED
@@ -19,7 +19,7 @@
19
19
  const fs = require('fs');
20
20
  const path = require('path');
21
21
  const os = require('os');
22
- const crypto = require('crypto');
22
+ const { sha256, compareVersions } = require('./installer-gate');
23
23
 
24
24
  const CC_HOME = path.join(os.homedir(), '.claude-cabinet');
25
25
  const GLOBAL_MANIFEST_PATH = path.join(CC_HOME, 'global-manifest.json');
@@ -65,22 +65,6 @@ const DATA_DIRS = [
65
65
  path.join(os.homedir(), '.local', 'share', 'mux', 'qa-handoff'),
66
66
  ];
67
67
 
68
- function sha256(content) {
69
- return crypto.createHash('sha256').update(content).digest('hex');
70
- }
71
-
72
- function compareVersions(a, b) {
73
- const pa = a.split('.').map(Number);
74
- const pb = b.split('.').map(Number);
75
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
76
- const va = pa[i] || 0;
77
- const vb = pb[i] || 0;
78
- if (va > vb) return 1;
79
- if (va < vb) return -1;
80
- }
81
- return 0;
82
- }
83
-
84
68
  function readGlobalManifest() {
85
69
  if (!fs.existsSync(GLOBAL_MANIFEST_PATH)) return { files: {} };
86
70
  try {