create-claude-cabinet 0.27.2 → 0.27.4

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.
package/lib/cli.js CHANGED
@@ -697,8 +697,13 @@ async function run() {
697
697
  if (dirState === 'existing-install') {
698
698
  const existing = readMetadata(projectDir);
699
699
  existingManifest = existing.manifest || {};
700
- console.log(` Found existing installation (v${existing.version}, installed ${existing.installedAt.split('T')[0]})`);
701
- console.log(' Updating upstream-managed files and adding new files.');
700
+ if (existing.version) {
701
+ const installedDate = existing.installedAt ? existing.installedAt.split('T')[0] : 'unknown';
702
+ console.log(` Found existing installation (v${existing.version}, installed ${installedDate})`);
703
+ console.log(' Updating upstream-managed files and adding new files.');
704
+ } else {
705
+ console.log(' Found .ccrc.json (migration-only, no prior install). Installing fresh.');
706
+ }
702
707
  if (!flags.yes && !flags.lean) {
703
708
  const { proceed } = await prompts({
704
709
  type: 'confirm',
@@ -948,12 +953,23 @@ async function run() {
948
953
  'skills/verify',
949
954
  ];
950
955
  const isSkill = tmpl.startsWith('skills/') && !alwaysCopyPhases.some(p => tmpl.startsWith(p));
956
+ // Instruction phases listed in MODULES as explicit file paths
957
+ // under this skill dir must bypass the phase-file guard in copy.js.
958
+ const instrPhases = new Set();
959
+ for (const mod of Object.values(MODULES)) {
960
+ for (const t of (mod.templates || [])) {
961
+ if (t.startsWith(tmpl + '/') && t.includes('/phases/')) {
962
+ instrPhases.add(t.slice(tmpl.length + 1));
963
+ }
964
+ }
965
+ }
951
966
  const results = await copyTemplates(srcPath, destPath, {
952
967
  dryRun: flags.dryRun,
953
968
  skipConflicts: flags.yes || dirState === 'existing-install',
954
969
  skipPhases: isSkill,
955
970
  projectRoot: projectDir,
956
971
  existingManifest,
972
+ instructionPhases: instrPhases,
957
973
  });
958
974
  totalCopied += results.copied.length;
959
975
  totalSkipped += results.skipped.length;
@@ -986,8 +1002,11 @@ async function run() {
986
1002
  // file and the on-disk content differs from upstream (operator
987
1003
  // edited it), preserve it. This keeps the documented "customize
988
1004
  // by editing the phase file" affordance intact across upgrades.
1005
+ // Exception: instruction phases explicitly listed in MODULES are
1006
+ // CC-owned and must always overwrite (they're not customizable).
989
1007
  const isPhaseFile = tmpl.includes('/phases/') || tmpl.includes('\\phases\\');
990
- if (isPhaseFile && existingContent.trim() !== '' && existingContent.trim() !== incoming.trim()) {
1008
+ const isInstructionPhase = isPhaseFile && mod.templates.includes(tmpl);
1009
+ if (isPhaseFile && !isInstructionPhase && existingContent.trim() !== '' && existingContent.trim() !== incoming.trim()) {
991
1010
  console.log(` Preserved customized phase: ${tmpl}`);
992
1011
  totalSkipped++;
993
1012
  allManifest[mPath] = hashContent(existingContent);
package/lib/copy.js CHANGED
@@ -11,13 +11,13 @@ function hashContent(content) {
11
11
  * Recursively copy files from src to dest, surfacing conflicts.
12
12
  * Returns { copied: string[], skipped: string[], overwritten: string[] }
13
13
  */
14
- async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false, projectRoot = null, existingManifest = {} } = {}) {
14
+ async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false, projectRoot = null, existingManifest = {}, instructionPhases = new Set() } = {}) {
15
15
  const results = { copied: [], skipped: [], overwritten: [], manifest: {} };
16
- await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
16
+ await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest, instructionPhases);
17
17
  return results;
18
18
  }
19
19
 
20
- async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest) {
20
+ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest, instructionPhases) {
21
21
  const entries = fs.readdirSync(currentSrc, { withFileTypes: true });
22
22
 
23
23
  for (const entry of entries) {
@@ -37,7 +37,7 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
37
37
  if (!dryRun && !fs.existsSync(destPath)) {
38
38
  fs.mkdirSync(destPath, { recursive: true });
39
39
  }
40
- await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
40
+ await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest, instructionPhases);
41
41
  } else {
42
42
  const incoming = fs.readFileSync(srcPath, 'utf8');
43
43
  const incomingHash = hashContent(incoming);
@@ -48,13 +48,17 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
48
48
  // Phase file guard (act:98d74381) — independent of skipPhases flag.
49
49
  // If a phase file on disk has been customized (differs from template
50
50
  // and isn't empty), never overwrite it regardless of other flags.
51
+ // Exception: instruction phases (CC-owned, always overwrite) are
52
+ // exempt — they ship updated content that consumers must receive.
51
53
  if (relPath.includes('phases' + path.sep) || relPath.includes('phases/')) {
52
- const trimmedExisting = existing.trim();
53
- if (trimmedExisting !== '' && trimmedExisting !== incoming.trim()) {
54
- results.skipped.push(relPath);
55
- results.manifest[relPath] = hashContent(existing);
56
- console.log(` Preserved customized phase: ${displayPath}`);
57
- continue;
54
+ if (!instructionPhases.has(relPath)) {
55
+ const trimmedExisting = existing.trim();
56
+ if (trimmedExisting !== '' && trimmedExisting !== incoming.trim()) {
57
+ results.skipped.push(relPath);
58
+ results.manifest[relPath] = hashContent(existing);
59
+ console.log(` Preserved customized phase: ${displayPath}`);
60
+ continue;
61
+ }
58
62
  }
59
63
  }
60
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.27.2",
3
+ "version": "0.27.4",
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"
@@ -71,6 +71,34 @@ anything else. If omega is still ACTIVE (conditions 1–4 above), run the
71
71
  full migration flow below FIRST; the sweep cleans up what teardown
72
72
  leaves behind, it does not replace migration.
73
73
 
74
+ Also strip stale omega permission entries from `.claude/settings.local.json`
75
+ if present. The migration cleans `~/.claude/settings.json` (global hooks/MCP)
76
+ but misses per-project permission allowlists:
77
+
78
+ ```bash
79
+ if [ -f .claude/settings.local.json ]; then
80
+ node -e "
81
+ const fs = require('fs');
82
+ const p = '.claude/settings.local.json';
83
+ const d = JSON.parse(fs.readFileSync(p, 'utf8'));
84
+ const perms = d.permissions || {};
85
+ let removed = 0;
86
+ for (const key of Object.keys(perms)) {
87
+ if (key.startsWith('mcp__omega-memory__') || key.startsWith('mcp__omega__')) {
88
+ delete perms[key];
89
+ removed++;
90
+ }
91
+ }
92
+ if (removed > 0) {
93
+ fs.writeFileSync(p, JSON.stringify(d, null, 2) + '\n');
94
+ console.log('Removed ' + removed + ' stale omega permission entries from settings.local.json');
95
+ }
96
+ "
97
+ fi
98
+ ```
99
+
100
+ Idempotent — no-ops when no omega entries exist.
101
+
74
102
  ## User-friendly prompt
75
103
 
76
104
  Default to **dry-run** — a non-Oren user just upgrading CC shouldn't