create-claude-rails 0.4.0 → 0.4.2

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
@@ -183,10 +183,12 @@ async function run() {
183
183
  // --- Directory detection ---
184
184
  const dirState = detectProjectState(projectDir);
185
185
 
186
+ let existingManifest = {};
186
187
  if (dirState === 'existing-install') {
187
188
  const existing = readMetadata(projectDir);
189
+ existingManifest = existing.manifest || {};
188
190
  console.log(` Found existing installation (v${existing.version}, installed ${existing.installedAt.split('T')[0]})`);
189
- console.log(' Will add new files only. Use /cor-upgrade in Claude Code to update existing files.');
191
+ console.log(' Updating upstream-managed files and adding new files.');
190
192
  if (!flags.yes && !flags.lean) {
191
193
  const { proceed } = await prompts({
192
194
  type: 'confirm',
@@ -401,6 +403,7 @@ async function run() {
401
403
  skipConflicts: flags.yes || dirState === 'existing-install',
402
404
  skipPhases: isSkill,
403
405
  projectRoot: projectDir,
406
+ existingManifest,
404
407
  });
405
408
  totalCopied += results.copied.length;
406
409
  totalSkipped += results.skipped.length;
@@ -429,8 +432,14 @@ async function run() {
429
432
  }
430
433
 
431
434
  if (flags.yes || dirState === 'existing-install') {
432
- // --yes or existing install: keep existing files (safe default)
433
- totalSkipped++;
435
+ // If file is in the old manifest, it's upstream-managed — overwrite.
436
+ // If not, it's project-created — skip.
437
+ if (existingManifest[mPath]) {
438
+ if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
439
+ totalOverwritten++;
440
+ } else {
441
+ totalSkipped++;
442
+ }
434
443
  allManifest[mPath] = incomingHash;
435
444
  } else {
436
445
  const response = await prompts({
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 } = {}) {
14
+ async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false, projectRoot = null, existingManifest = {} } = {}) {
15
15
  const results = { copied: [], skipped: [], overwritten: [], manifest: {} };
16
- await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot);
16
+ await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
17
17
  return results;
18
18
  }
19
19
 
20
- async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot) {
20
+ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest) {
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);
40
+ await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases, projectRoot, existingManifest);
41
41
  } else {
42
42
  const incoming = fs.readFileSync(srcPath, 'utf8');
43
43
  const incomingHash = hashContent(incoming);
@@ -52,8 +52,20 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
52
52
  }
53
53
 
54
54
  if (skipConflicts) {
55
- results.skipped.push(relPath);
56
- results.manifest[relPath] = incomingHash;
55
+ // Check if file is upstream-managed (in the old manifest).
56
+ // If so, overwrite — it can't be customized (hook enforces this).
57
+ // If not, skip — it's project-created content.
58
+ const manifestKey = projectRoot
59
+ ? path.relative(projectRoot, destPath)
60
+ : relPath;
61
+ if (existingManifest[manifestKey]) {
62
+ if (!dryRun) fs.copyFileSync(srcPath, destPath);
63
+ results.overwritten.push(relPath);
64
+ results.manifest[relPath] = incomingHash;
65
+ } else {
66
+ results.skipped.push(relPath);
67
+ results.manifest[relPath] = incomingHash;
68
+ }
57
69
  continue;
58
70
  }
59
71
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-rails",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Claude on Rails — opinionated process scaffolding for Claude Code projects",
5
5
  "bin": {
6
6
  "create-claude-rails": "bin/create-claude-rails.js"
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: opus
2
3
  name: audit
3
4
  description: |
4
5
  Systematic quality audit. Selects perspectives, loads triage suppression,
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: opus
2
3
  name: cor-upgrade
3
4
  description: |
4
5
  Conversational upgrade when new Claude on Rails skeletons arrive. Detects current
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: opus
2
3
  name: execute
3
4
  description: |
4
5
  Execute a single plan with perspective-based guardrails at structured
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: opus
2
3
  name: investigate
3
4
  description: |
4
5
  Structured codebase exploration before planning. Four phases: observe
@@ -1,4 +1,5 @@
1
1
  ---
2
+ model: opus
2
3
  name: plan
3
4
  description: |
4
5
  Create a structured implementation plan with perspective-based critique and