create-claude-cabinet 0.33.0 → 0.34.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 (32) hide show
  1. package/lib/cli.js +4 -1
  2. package/lib/engagement-setup.js +161 -0
  3. package/package.json +1 -1
  4. package/templates/engagement/OVERVIEW.md +2 -2
  5. package/templates/engagement/__tests__/engagement.test.mjs +466 -1
  6. package/templates/engagement/__tests__/pibdb-adapter.test.mjs +52 -1
  7. package/templates/engagement/engagement-schema.md +123 -11
  8. package/templates/engagement/engagement.mjs +172 -7
  9. package/templates/engagement/pib-db-patches/pib-db-lib.mjs +705 -0
  10. package/templates/engagement/pib-db-patches/pib-db-mcp-server.mjs +461 -0
  11. package/templates/engagement/pib-db-patches/pib-db-schema.sql +130 -0
  12. package/templates/engagement/pib-db-patches/pib-db.mjs +246 -0
  13. package/templates/engagement/pibdb-adapter.mjs +23 -0
  14. package/templates/scripts/pib-db-lib.mjs +26 -6
  15. package/templates/scripts/pib-db-mcp-server.mjs +18 -0
  16. package/templates/scripts/pib-db-schema.sql +2 -1
  17. package/templates/scripts/pib-db.mjs +8 -1
  18. package/templates/skills/engagement/SKILL.md +12 -4
  19. package/templates/skills/engagement-add/SKILL.md +30 -9
  20. package/templates/skills/engagement-create/SKILL.md +22 -3
  21. package/templates/skills/engagement-edit/SKILL.md +18 -8
  22. package/templates/skills/engagement-message/SKILL.md +13 -7
  23. package/templates/skills/engagement-status/SKILL.md +22 -15
  24. package/templates/skills/engagement-sync/SKILL.md +138 -40
  25. package/templates/skills/verify/SKILL.md +65 -15
  26. package/templates/verify-runtime/src/cli/report-last.ts +13 -6
  27. package/templates/verify-runtime/src/cli/report-status.ts +7 -3
  28. package/templates/verify-runtime/src/human-verdict.ts +8 -4
  29. package/templates/verify-runtime/src/output.ts +6 -2
  30. package/templates/verify-runtime/src/verdict-recorder.ts +13 -7
  31. package/templates/verify-runtime/test/fresh-pass-cache.test.ts +32 -0
  32. package/templates/verify-runtime/test/verdict-recorder.test.ts +48 -0
package/lib/cli.js CHANGED
@@ -9,6 +9,7 @@ const { create: createMetadata, read: readMetadata } = require('./metadata');
9
9
  const { setupDb } = require('./db-setup');
10
10
  const { setupVerifyRuntime } = require('./verify-setup');
11
11
  const { setupSiteAuditRuntime } = require('./site-audit-setup');
12
+ const { setupEngagement } = require('./engagement-setup');
12
13
  const { reset } = require('./reset');
13
14
 
14
15
  const VERSION = require('../package.json').version;
@@ -599,6 +600,7 @@ const MODULES = {
599
600
  default: false,
600
601
  lean: false,
601
602
  requires: ['work-tracking'],
603
+ postInstall: 'engagement-setup',
602
604
  templates: [
603
605
  'skills/engagement',
604
606
  'skills/engagement-progress',
@@ -1269,6 +1271,7 @@ async function run() {
1269
1271
  const POST_INSTALL_HANDLERS = {
1270
1272
  'verify-setup': setupVerifyRuntime,
1271
1273
  'site-audit-setup': setupSiteAuditRuntime,
1274
+ 'engagement-setup': setupEngagement,
1272
1275
  };
1273
1276
  for (const moduleKey of selectedModules) {
1274
1277
  const mod = MODULES[moduleKey];
@@ -1280,7 +1283,7 @@ async function run() {
1280
1283
  }
1281
1284
  try {
1282
1285
  console.log('');
1283
- const result = handler({ dryRun: !!flags.dryRun });
1286
+ const result = handler({ dryRun: !!flags.dryRun, projectDir });
1284
1287
  for (const r of result.results || []) console.log(` 📋 ${r}`);
1285
1288
  } catch (err) {
1286
1289
  console.log(` ⚠ ${mod.postInstall} failed: ${err.message}`);
@@ -0,0 +1,161 @@
1
+ /**
2
+ * engagement-setup.js — extend pib-db files with engagement schema
3
+ * when the engagement module is installed.
4
+ *
5
+ * Dispatched from cli.js's postInstall pipeline. Two responsibilities:
6
+ *
7
+ * 1. FILE OVERLAY — copy the engagement-inclusive pib-db files over the
8
+ * base versions. Uses a version-aware check: parse SCHEMA_VERSION from
9
+ * the installed file and re-copy when below the patch's version. This
10
+ * replaces the old string-marker approach, which was version-blind and
11
+ * silently skipped upgrades (e.g., v5→v6 never landed because the v5
12
+ * marker already matched).
13
+ *
14
+ * 2. SCHEMA ENSURE — after the overlay, open pib.db and call migrate()
15
+ * so existing DBs advance to the patch's schema version. Also runs an
16
+ * idempotent CREATE TABLE IF NOT EXISTS for engagement_events, which
17
+ * covers the Path 5 case (a base-only consumer at v5 who later adds
18
+ * engagement — the v5 migration entry is gated out, so this direct
19
+ * exec is the only path to creating the table).
20
+ *
21
+ * The base pib-db templates (owned by work-tracking) ship schema v1-v5.
22
+ * The engagement patch ships v1-v6 (v5=engagement_events, v6=projects.tags).
23
+ */
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+
28
+ const FILES = ['pib-db-lib.mjs', 'pib-db-mcp-server.mjs', 'pib-db-schema.sql', 'pib-db.mjs'];
29
+ const VERSION_RE = /export const SCHEMA_VERSION\s*=\s*(\d+)/;
30
+
31
+ function parseSchemaVersion(content) {
32
+ const m = content.match(VERSION_RE);
33
+ return m ? parseInt(m[1], 10) : 0;
34
+ }
35
+
36
+ function setupEngagement({ dryRun, projectDir } = {}) {
37
+ const results = [];
38
+ const scriptsDir = path.join(projectDir, 'scripts');
39
+ const patchDir = path.join(projectDir, '.claude', 'engagement', 'pib-db-patches');
40
+
41
+ if (!fs.existsSync(patchDir)) {
42
+ results.push('engagement-setup: patch dir not found — skipping (templates not yet copied)');
43
+ return { results };
44
+ }
45
+
46
+ // --- Step 1: File overlay (version-aware) ---
47
+
48
+ const patchLibPath = path.join(patchDir, 'pib-db-lib.mjs');
49
+ let patchVersion = 0;
50
+ if (fs.existsSync(patchLibPath)) {
51
+ patchVersion = parseSchemaVersion(fs.readFileSync(patchLibPath, 'utf8'));
52
+ }
53
+
54
+ let filesUpdated = false;
55
+ for (const file of FILES) {
56
+ const target = path.join(scriptsDir, file);
57
+ const source = path.join(patchDir, file);
58
+
59
+ if (!fs.existsSync(source)) {
60
+ results.push(`${file}: engagement patch not found — skipped`);
61
+ continue;
62
+ }
63
+
64
+ if (!fs.existsSync(target)) {
65
+ results.push(`${file}: target not found (work-tracking not installed?) — skipped`);
66
+ continue;
67
+ }
68
+
69
+ if (file === 'pib-db-lib.mjs') {
70
+ const installedVersion = parseSchemaVersion(fs.readFileSync(target, 'utf8'));
71
+ if (installedVersion >= patchVersion) {
72
+ results.push(`${file}: schema v${installedVersion} >= patch v${patchVersion} — skipped`);
73
+ continue;
74
+ }
75
+ } else {
76
+ const existing = fs.readFileSync(target, 'utf8');
77
+ const source_content = fs.readFileSync(source, 'utf8');
78
+ if (existing === source_content) {
79
+ results.push(`${file}: already matches patch — skipped`);
80
+ continue;
81
+ }
82
+ }
83
+
84
+ if (!dryRun) {
85
+ fs.copyFileSync(source, target);
86
+ filesUpdated = true;
87
+ }
88
+ results.push(`${file}: applied engagement-inclusive version`);
89
+ }
90
+
91
+ // --- Step 2: Schema ensure (migrate + idempotent engagement_events) ---
92
+
93
+ const dbPath = path.join(projectDir, 'pib.db');
94
+ if (!dryRun && fs.existsSync(dbPath) && fs.existsSync(path.join(scriptsDir, 'pib-db-lib.mjs'))) {
95
+ try {
96
+ const Database = require('better-sqlite3');
97
+ const db = new Database(dbPath);
98
+ db.pragma('journal_mode = WAL');
99
+ db.pragma('foreign_keys = ON');
100
+
101
+ const libPath = path.join(scriptsDir, 'pib-db-lib.mjs');
102
+ const libContent = fs.readFileSync(libPath, 'utf8');
103
+ const currentVersion = db.pragma('user_version', { simple: true });
104
+ const targetVersion = parseSchemaVersion(libContent);
105
+
106
+ if (currentVersion < targetVersion) {
107
+ const { execSync } = require('child_process');
108
+ try {
109
+ execSync(`node "${path.join(scriptsDir, 'pib-db.mjs')}" init`, {
110
+ cwd: projectDir,
111
+ env: { ...process.env, PIB_DB_PATH: dbPath },
112
+ stdio: 'pipe',
113
+ });
114
+ results.push(`schema-ensure: migrated pib.db from v${currentVersion} to v${targetVersion}`);
115
+ } catch (e) {
116
+ results.push(`schema-ensure: migration failed — ${e.message}`);
117
+ }
118
+ }
119
+
120
+ // Idempotent engagement_events CREATE — runs UNCONDITIONALLY (not
121
+ // gated on version). Covers Path 5: a base-only consumer at v5 who
122
+ // later adds engagement. Their v5 migration entry (engagement_events)
123
+ // is gated out by user_version, and the execSync migrate above won't
124
+ // create it either. This direct exec is the only path.
125
+ try {
126
+ db.exec(`CREATE TABLE IF NOT EXISTS engagement_events (
127
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
128
+ engagement TEXT NOT NULL REFERENCES projects(fid),
129
+ target_fid TEXT,
130
+ packet_id TEXT,
131
+ kind TEXT NOT NULL
132
+ CHECK(kind IN ('client_feedback','status_push','delegation','approval','note','packet_sent')),
133
+ author TEXT NOT NULL,
134
+ verdict TEXT CHECK(verdict IS NULL OR verdict IN ('approve','object','comment','none')),
135
+ body TEXT CHECK(body IS NULL OR length(body) <= 10000),
136
+ addressed INTEGER NOT NULL DEFAULT 0 CHECK(addressed IN (0,1)),
137
+ created_at TEXT NOT NULL CHECK(created_at GLOB '????-??-??T*'),
138
+ CHECK(kind NOT IN ('client_feedback','approval')
139
+ OR (verdict IS NOT NULL AND verdict IN ('approve','object','comment')))
140
+ )`);
141
+ db.exec("CREATE INDEX IF NOT EXISTS idx_engagement_events_eng ON engagement_events(engagement, created_at DESC)");
142
+ db.exec("CREATE INDEX IF NOT EXISTS idx_engagement_events_tgt ON engagement_events(target_fid, created_at DESC)");
143
+ db.exec("CREATE INDEX IF NOT EXISTS idx_engagement_events_dedup ON engagement_events(packet_id, target_fid, verdict)");
144
+ results.push('schema-ensure: engagement_events table ensured (idempotent)');
145
+ } catch (e) {
146
+ results.push(`schema-ensure: engagement_events ensure failed — ${e.message}`);
147
+ }
148
+ db.close();
149
+ } catch (e) {
150
+ if (/Cannot find module|MODULE_NOT_FOUND/.test(e.message || '')) {
151
+ results.push('schema-ensure: better-sqlite3 not available — skipped (DB will migrate on first use)');
152
+ } else {
153
+ results.push(`schema-ensure: ${e.message}`);
154
+ }
155
+ }
156
+ }
157
+
158
+ return { results };
159
+ }
160
+
161
+ module.exports = { setupEngagement };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.33.0",
3
+ "version": "0.34.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"
@@ -88,8 +88,8 @@ to receive **before** anything is dispatched:
88
88
 
89
89
  ```
90
90
  Ready to send:
91
- Ed (principal, plugin) — 2 need response, 4 in progress, 7 done, $4,417.50 billed
92
- Sydney (delegate, email) — 1 assigned item (marketing)
91
+ Ed (principal, has Claude Code) — 2 need response, 4 in progress, 7 done, $4,417.50 billed
92
+ Sydney (delegate, no Claude Code) — 1 assigned item (marketing)
93
93
 
94
94
  Blocked (not client-ready): 1 — "DNS cutover" (no client-facing copy)
95
95
  ```