create-claude-cabinet 0.33.1 → 0.34.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 (33) hide show
  1. package/lib/engagement-setup.js +114 -14
  2. package/package.json +1 -1
  3. package/templates/engagement/OVERVIEW.md +2 -2
  4. package/templates/engagement/__tests__/engagement.test.mjs +466 -1
  5. package/templates/engagement/__tests__/pibdb-adapter.test.mjs +52 -1
  6. package/templates/engagement/engagement-schema.md +123 -11
  7. package/templates/engagement/engagement.mjs +172 -7
  8. package/templates/engagement/pib-db-patches/pib-db-lib.mjs +26 -6
  9. package/templates/engagement/pib-db-patches/pib-db-mcp-server.mjs +18 -0
  10. package/templates/engagement/pib-db-patches/pib-db-schema.sql +2 -1
  11. package/templates/engagement/pib-db-patches/pib-db.mjs +8 -1
  12. package/templates/engagement/pibdb-adapter.mjs +23 -0
  13. package/templates/engagement/secure-input-dialog.swift +72 -0
  14. package/templates/engagement/secure-input.mjs +45 -2
  15. package/templates/scripts/pib-db-lib.mjs +26 -6
  16. package/templates/scripts/pib-db-mcp-server.mjs +18 -0
  17. package/templates/scripts/pib-db-schema.sql +2 -1
  18. package/templates/scripts/pib-db.mjs +8 -1
  19. package/templates/skills/engagement/SKILL.md +12 -4
  20. package/templates/skills/engagement-add/SKILL.md +30 -9
  21. package/templates/skills/engagement-create/SKILL.md +22 -3
  22. package/templates/skills/engagement-edit/SKILL.md +18 -8
  23. package/templates/skills/engagement-message/SKILL.md +13 -7
  24. package/templates/skills/engagement-status/SKILL.md +22 -15
  25. package/templates/skills/engagement-sync/SKILL.md +138 -40
  26. package/templates/skills/verify/SKILL.md +65 -15
  27. package/templates/verify-runtime/src/cli/report-last.ts +13 -6
  28. package/templates/verify-runtime/src/cli/report-status.ts +7 -3
  29. package/templates/verify-runtime/src/human-verdict.ts +8 -4
  30. package/templates/verify-runtime/src/output.ts +6 -2
  31. package/templates/verify-runtime/src/verdict-recorder.ts +13 -7
  32. package/templates/verify-runtime/test/fresh-pass-cache.test.ts +32 -0
  33. package/templates/verify-runtime/test/verdict-recorder.test.ts +48 -0
@@ -1,23 +1,37 @@
1
1
  /**
2
- * engagement-setup.js — extend pib-db files with engagement schema v5
2
+ * engagement-setup.js — extend pib-db files with engagement schema
3
3
  * when the engagement module is installed.
4
4
  *
5
- * Dispatched from cli.js's postInstall pipeline. Idempotent: skips
6
- * files that already contain the engagement code (detected by marker).
5
+ * Dispatched from cli.js's postInstall pipeline. Two responsibilities:
7
6
  *
8
- * The base pib-db templates (owned by work-tracking) ship schema v1-v4.
9
- * The engagement module stores the complete engagement-inclusive versions
10
- * of the pib-db files in templates/engagement/pib-db-patches/. When
11
- * engagement is selected, this handler copies those over the base
12
- * versions, keeping the base templates clean for consumers who don't
13
- * opt into engagement.
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).
14
23
  */
15
24
 
16
25
  const fs = require('fs');
17
26
  const path = require('path');
18
27
 
19
- const MARKER = 'ENGAGEMENT_EVENT_KINDS';
20
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
+ }
21
35
 
22
36
  function setupEngagement({ dryRun, projectDir } = {}) {
23
37
  const results = [];
@@ -29,6 +43,15 @@ function setupEngagement({ dryRun, projectDir } = {}) {
29
43
  return { results };
30
44
  }
31
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;
32
55
  for (const file of FILES) {
33
56
  const target = path.join(scriptsDir, file);
34
57
  const source = path.join(patchDir, file);
@@ -43,18 +66,95 @@ function setupEngagement({ dryRun, projectDir } = {}) {
43
66
  continue;
44
67
  }
45
68
 
46
- const existing = fs.readFileSync(target, 'utf8');
47
- if (existing.includes(MARKER)) {
48
- results.push(`${file}: already has engagement code — skipped`);
49
- continue;
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
+ }
50
82
  }
51
83
 
52
84
  if (!dryRun) {
53
85
  fs.copyFileSync(source, target);
86
+ filesUpdated = true;
54
87
  }
55
88
  results.push(`${file}: applied engagement-inclusive version`);
56
89
  }
57
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
+
58
158
  return { results };
59
159
  }
60
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.33.1",
3
+ "version": "0.34.1",
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
  ```