create-claude-rails 0.1.2 → 0.2.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.
- package/lib/cli.js +47 -3
- package/lib/copy.js +16 -2
- package/lib/metadata.js +2 -1
- package/lib/reset.js +193 -0
- package/package.json +1 -1
- package/templates/EXTENSIONS.md +32 -32
- package/templates/README.md +2 -2
- package/templates/skills/onboard/SKILL.md +55 -22
- package/templates/skills/onboard/phases/detect-state.md +21 -39
- package/templates/skills/onboard/phases/generate-context.md +1 -1
- package/templates/skills/onboard/phases/interview.md +22 -2
- package/templates/skills/onboard/phases/modularity-menu.md +17 -14
- package/templates/skills/onboard/phases/options.md +98 -0
- package/templates/skills/onboard/phases/post-onboard-audit.md +19 -1
- package/templates/skills/onboard/phases/summary.md +1 -1
- package/templates/skills/onboard/phases/work-tracking.md +231 -0
- package/templates/skills/perspectives/_groups-template.yaml +1 -1
- package/templates/skills/perspectives/architecture/SKILL.md +275 -0
- package/templates/skills/perspectives/box-health/SKILL.md +8 -8
- package/templates/skills/perspectives/data-integrity/SKILL.md +2 -2
- package/templates/skills/perspectives/documentation/SKILL.md +4 -5
- package/templates/skills/perspectives/historian/SKILL.md +250 -0
- package/templates/skills/perspectives/process/SKILL.md +3 -3
- package/templates/skills/perspectives/skills-coverage/SKILL.md +294 -0
- package/templates/skills/perspectives/system-advocate/SKILL.md +191 -0
- package/templates/skills/perspectives/usability/SKILL.md +186 -0
- package/templates/skills/seed/phases/scan-signals.md +7 -3
- package/templates/skills/upgrade/SKILL.md +15 -15
- package/templates/skills/upgrade/phases/apply.md +3 -3
- package/templates/skills/upgrade/phases/detect-current.md +7 -7
- package/templates/skills/upgrade/phases/diff-upstream.md +3 -3
package/lib/cli.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const prompts = require('prompts');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const crypto = require('crypto');
|
|
4
5
|
const { copyTemplates } = require('./copy');
|
|
5
6
|
const { mergeSettings } = require('./settings-merge');
|
|
6
7
|
const { create: createMetadata, read: readMetadata } = require('./metadata');
|
|
7
8
|
const { setupDb } = require('./db-setup');
|
|
9
|
+
const { reset } = require('./reset');
|
|
8
10
|
|
|
9
11
|
const VERSION = require('../package.json').version;
|
|
10
12
|
|
|
@@ -23,8 +25,8 @@ const MODULES = {
|
|
|
23
25
|
templates: ['hooks/git-guardrails.sh', 'hooks/skill-telemetry.sh', 'hooks/skill-tool-telemetry.sh'],
|
|
24
26
|
},
|
|
25
27
|
'work-tracking': {
|
|
26
|
-
name: 'Work Tracking (pib-db)',
|
|
27
|
-
description: '
|
|
28
|
+
name: 'Work Tracking (pib-db or markdown)',
|
|
29
|
+
description: 'Track work items for orient/debrief. Default: SQLite (pib-db). Also supports markdown (tasks.md) or external systems. Choice made during /onboard.',
|
|
28
30
|
mandatory: false,
|
|
29
31
|
default: true,
|
|
30
32
|
templates: ['scripts/pib-db.js', 'scripts/pib-db-schema.sql'],
|
|
@@ -101,6 +103,8 @@ function parseArgs(argv) {
|
|
|
101
103
|
noDb: false,
|
|
102
104
|
dryRun: false,
|
|
103
105
|
help: false,
|
|
106
|
+
reset: false,
|
|
107
|
+
force: false,
|
|
104
108
|
targetDir: '.',
|
|
105
109
|
};
|
|
106
110
|
|
|
@@ -109,6 +113,8 @@ function parseArgs(argv) {
|
|
|
109
113
|
else if (arg === '--no-db') flags.noDb = true;
|
|
110
114
|
else if (arg === '--dry-run') flags.dryRun = true;
|
|
111
115
|
else if (arg === '--help' || arg === '-h') flags.help = true;
|
|
116
|
+
else if (arg === '--reset') flags.reset = true;
|
|
117
|
+
else if (arg === '--force') flags.force = true;
|
|
112
118
|
else if (!arg.startsWith('-')) flags.targetDir = arg;
|
|
113
119
|
}
|
|
114
120
|
|
|
@@ -123,6 +129,8 @@ function printHelp() {
|
|
|
123
129
|
--yes, -y Accept all defaults, no prompts
|
|
124
130
|
--no-db Skip work tracking database setup
|
|
125
131
|
--dry-run Show what would be copied without writing
|
|
132
|
+
--reset Remove CoR files (uses manifest for safety)
|
|
133
|
+
--force With --reset: remove even customized files
|
|
126
134
|
--help, -h Show this help
|
|
127
135
|
|
|
128
136
|
Examples:
|
|
@@ -131,6 +139,8 @@ function printHelp() {
|
|
|
131
139
|
npx create-claude-rails --yes Install everything, no questions
|
|
132
140
|
npx create-claude-rails --yes --no-db Install everything except DB
|
|
133
141
|
npx create-claude-rails --dry-run Preview what would be installed
|
|
142
|
+
npx create-claude-rails --reset Remove CoR files safely
|
|
143
|
+
npx create-claude-rails --reset --dry-run Preview what --reset would do
|
|
134
144
|
`);
|
|
135
145
|
}
|
|
136
146
|
|
|
@@ -142,6 +152,12 @@ async function run() {
|
|
|
142
152
|
return;
|
|
143
153
|
}
|
|
144
154
|
|
|
155
|
+
if (flags.reset) {
|
|
156
|
+
const projectDir = path.resolve(flags.targetDir);
|
|
157
|
+
await reset(projectDir, { dryRun: flags.dryRun, force: flags.force });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
145
161
|
console.log('');
|
|
146
162
|
console.log(' 🚂 Claude on Rails v' + VERSION);
|
|
147
163
|
console.log(' Opinionated process scaffolding for Claude Code projects');
|
|
@@ -290,6 +306,21 @@ async function run() {
|
|
|
290
306
|
let totalCopied = 0;
|
|
291
307
|
let totalSkipped = 0;
|
|
292
308
|
let totalOverwritten = 0;
|
|
309
|
+
const allManifest = {}; // relPath -> hash for all written files
|
|
310
|
+
|
|
311
|
+
function hashContent(content) {
|
|
312
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Compute the relative path from projectDir for manifest entries
|
|
316
|
+
function manifestPath(tmpl) {
|
|
317
|
+
if (tmpl.startsWith('skills/') || tmpl.startsWith('hooks/') || tmpl.startsWith('rules/')) {
|
|
318
|
+
return '.claude/' + tmpl;
|
|
319
|
+
} else if (tmpl.startsWith('scripts/')) {
|
|
320
|
+
return tmpl;
|
|
321
|
+
}
|
|
322
|
+
return '.claude/' + tmpl;
|
|
323
|
+
}
|
|
293
324
|
|
|
294
325
|
for (const modKey of selectedModules) {
|
|
295
326
|
const mod = MODULES[modKey];
|
|
@@ -323,23 +354,33 @@ async function run() {
|
|
|
323
354
|
totalCopied += results.copied.length;
|
|
324
355
|
totalSkipped += results.skipped.length;
|
|
325
356
|
totalOverwritten += results.overwritten.length;
|
|
357
|
+
// Collect manifest entries — prefix with the dest-relative path
|
|
358
|
+
const prefix = manifestPath(tmpl);
|
|
359
|
+
for (const [relFile, hash] of Object.entries(results.manifest)) {
|
|
360
|
+
allManifest[prefix + '/' + relFile] = hash;
|
|
361
|
+
}
|
|
326
362
|
} else {
|
|
327
363
|
const destDir = path.dirname(destPath);
|
|
328
364
|
if (!flags.dryRun && !fs.existsSync(destDir)) {
|
|
329
365
|
fs.mkdirSync(destDir, { recursive: true });
|
|
330
366
|
}
|
|
331
367
|
|
|
368
|
+
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
369
|
+
const incomingHash = hashContent(incoming);
|
|
370
|
+
const mPath = manifestPath(tmpl);
|
|
371
|
+
|
|
332
372
|
if (fs.existsSync(destPath)) {
|
|
333
373
|
const existingContent = fs.readFileSync(destPath, 'utf8');
|
|
334
|
-
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
335
374
|
if (existingContent === incoming) {
|
|
336
375
|
totalSkipped++;
|
|
376
|
+
allManifest[mPath] = incomingHash;
|
|
337
377
|
continue;
|
|
338
378
|
}
|
|
339
379
|
|
|
340
380
|
if (flags.yes) {
|
|
341
381
|
// --yes: keep existing files (safe default)
|
|
342
382
|
totalSkipped++;
|
|
383
|
+
allManifest[mPath] = incomingHash;
|
|
343
384
|
} else {
|
|
344
385
|
const response = await prompts({
|
|
345
386
|
type: 'select',
|
|
@@ -357,10 +398,12 @@ async function run() {
|
|
|
357
398
|
} else {
|
|
358
399
|
totalSkipped++;
|
|
359
400
|
}
|
|
401
|
+
allManifest[mPath] = incomingHash;
|
|
360
402
|
}
|
|
361
403
|
} else {
|
|
362
404
|
if (!flags.dryRun) fs.copyFileSync(srcPath, destPath);
|
|
363
405
|
totalCopied++;
|
|
406
|
+
allManifest[mPath] = incomingHash;
|
|
364
407
|
}
|
|
365
408
|
}
|
|
366
409
|
}
|
|
@@ -403,6 +446,7 @@ async function run() {
|
|
|
403
446
|
modules: selectedModules,
|
|
404
447
|
skipped: skippedModules,
|
|
405
448
|
version: VERSION,
|
|
449
|
+
manifest: allManifest,
|
|
406
450
|
});
|
|
407
451
|
console.log(' 📝 Created .pibrc.json');
|
|
408
452
|
}
|
package/lib/copy.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
const prompts = require('prompts');
|
|
4
5
|
|
|
6
|
+
function hashContent(content) {
|
|
7
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
/**
|
|
6
11
|
* Recursively copy files from src to dest, surfacing conflicts.
|
|
7
12
|
* Returns { copied: string[], skipped: string[], overwritten: string[] }
|
|
8
13
|
*/
|
|
9
14
|
async function copyTemplates(src, dest, { dryRun = false, skipConflicts = false, skipPhases = false } = {}) {
|
|
10
|
-
const results = { copied: [], skipped: [], overwritten: [] };
|
|
15
|
+
const results = { copied: [], skipped: [], overwritten: [], manifest: {} };
|
|
11
16
|
await walkAndCopy(src, dest, src, results, dryRun, skipConflicts, skipPhases);
|
|
12
17
|
return results;
|
|
13
18
|
}
|
|
@@ -32,17 +37,21 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
32
37
|
}
|
|
33
38
|
await walkAndCopy(srcRoot, destRoot, srcPath, results, dryRun, skipConflicts, skipPhases);
|
|
34
39
|
} else {
|
|
40
|
+
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
41
|
+
const incomingHash = hashContent(incoming);
|
|
42
|
+
|
|
35
43
|
if (fs.existsSync(destPath)) {
|
|
36
44
|
const existing = fs.readFileSync(destPath, 'utf8');
|
|
37
|
-
const incoming = fs.readFileSync(srcPath, 'utf8');
|
|
38
45
|
|
|
39
46
|
if (existing === incoming) {
|
|
40
47
|
results.skipped.push(relPath);
|
|
48
|
+
results.manifest[relPath] = incomingHash;
|
|
41
49
|
continue;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
if (skipConflicts) {
|
|
45
53
|
results.skipped.push(relPath);
|
|
54
|
+
results.manifest[relPath] = incomingHash;
|
|
46
55
|
continue;
|
|
47
56
|
}
|
|
48
57
|
|
|
@@ -60,6 +69,7 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
60
69
|
if (!response.action) {
|
|
61
70
|
// User cancelled
|
|
62
71
|
results.skipped.push(relPath);
|
|
72
|
+
results.manifest[relPath] = incomingHash;
|
|
63
73
|
continue;
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -77,11 +87,14 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
77
87
|
} else {
|
|
78
88
|
results.skipped.push(relPath);
|
|
79
89
|
}
|
|
90
|
+
results.manifest[relPath] = incomingHash;
|
|
80
91
|
} else if (response.action === 'overwrite') {
|
|
81
92
|
if (!dryRun) fs.copyFileSync(srcPath, destPath);
|
|
82
93
|
results.overwritten.push(relPath);
|
|
94
|
+
results.manifest[relPath] = incomingHash;
|
|
83
95
|
} else {
|
|
84
96
|
results.skipped.push(relPath);
|
|
97
|
+
results.manifest[relPath] = incomingHash;
|
|
85
98
|
}
|
|
86
99
|
} else {
|
|
87
100
|
if (!dryRun) {
|
|
@@ -90,6 +103,7 @@ async function walkAndCopy(srcRoot, destRoot, currentSrc, results, dryRun, skipC
|
|
|
90
103
|
fs.copyFileSync(srcPath, destPath);
|
|
91
104
|
}
|
|
92
105
|
results.copied.push(relPath);
|
|
106
|
+
results.manifest[relPath] = incomingHash;
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
109
|
}
|
package/lib/metadata.js
CHANGED
|
@@ -18,13 +18,14 @@ function write(projectDir, data) {
|
|
|
18
18
|
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function create(projectDir, { modules, skipped, version }) {
|
|
21
|
+
function create(projectDir, { modules, skipped, version, manifest = {} }) {
|
|
22
22
|
const data = {
|
|
23
23
|
version,
|
|
24
24
|
installedAt: new Date().toISOString(),
|
|
25
25
|
upstreamPackage: 'create-claude-rails',
|
|
26
26
|
modules: {},
|
|
27
27
|
skipped: {},
|
|
28
|
+
manifest,
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
for (const mod of modules) {
|
package/lib/reset.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const { read: readMetadata, METADATA_FILE } = require('./metadata');
|
|
5
|
+
const { DEFAULT_HOOKS } = require('./settings-merge');
|
|
6
|
+
|
|
7
|
+
// CoR-managed hook command patterns — used to identify hooks to remove
|
|
8
|
+
const COR_HOOK_PATTERNS = [
|
|
9
|
+
'.claude/hooks/git-guardrails.sh',
|
|
10
|
+
'.claude/hooks/skill-telemetry.sh',
|
|
11
|
+
'.claude/hooks/skill-tool-telemetry.sh',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function hashContent(content) {
|
|
15
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reconstruct manifest from module list for 0.1.x installs that lack one.
|
|
20
|
+
* Maps module names to their expected template paths using the MODULES
|
|
21
|
+
* definition from cli.js. Returns a best-effort manifest with no hashes.
|
|
22
|
+
*/
|
|
23
|
+
function reconstructManifest(metadata) {
|
|
24
|
+
// We don't import MODULES to avoid circular deps — use a static mapping
|
|
25
|
+
// that covers the known 0.1.x template structure.
|
|
26
|
+
console.log(' ⚠ No manifest found (0.1.x install). Reconstructing from modules...');
|
|
27
|
+
console.log(' All files will be treated as unmodified (no hash data).\n');
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Remove CoR files from a project using the manifest for safety.
|
|
33
|
+
*
|
|
34
|
+
* For each manifest entry:
|
|
35
|
+
* - Hash matches → remove (unmodified CoR file)
|
|
36
|
+
* - Hash differs → skip with [CUSTOMIZED] warning (unless --force)
|
|
37
|
+
* - File missing → skip (already removed)
|
|
38
|
+
*
|
|
39
|
+
* Files NOT in the manifest are left alone (user-created, onboard-generated).
|
|
40
|
+
*/
|
|
41
|
+
async function reset(projectDir, { dryRun = false, force = false } = {}) {
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(' 🚂 Claude on Rails — Reset');
|
|
44
|
+
console.log('');
|
|
45
|
+
|
|
46
|
+
if (dryRun) {
|
|
47
|
+
console.log(' [dry run — no files will be removed]\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const metadata = readMetadata(projectDir);
|
|
51
|
+
if (!metadata) {
|
|
52
|
+
console.log(' No .pibrc.json found — nothing to reset.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(` Found installation (v${metadata.version}, installed ${metadata.installedAt.split('T')[0]})`);
|
|
57
|
+
console.log('');
|
|
58
|
+
|
|
59
|
+
let manifest = metadata.manifest;
|
|
60
|
+
if (!manifest || Object.keys(manifest).length === 0) {
|
|
61
|
+
manifest = reconstructManifest(metadata);
|
|
62
|
+
if (Object.keys(manifest).length === 0) {
|
|
63
|
+
console.log(' Could not reconstruct manifest. Use --force to remove all CoR directories.\n');
|
|
64
|
+
if (!force) return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const removed = [];
|
|
69
|
+
const customized = [];
|
|
70
|
+
const missing = [];
|
|
71
|
+
const forced = [];
|
|
72
|
+
|
|
73
|
+
// Process each manifest entry
|
|
74
|
+
for (const [relPath, installedHash] of Object.entries(manifest)) {
|
|
75
|
+
const fullPath = path.join(projectDir, relPath);
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(fullPath)) {
|
|
78
|
+
missing.push(relPath);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const currentContent = fs.readFileSync(fullPath, 'utf8');
|
|
83
|
+
const currentHash = hashContent(currentContent);
|
|
84
|
+
|
|
85
|
+
if (currentHash === installedHash) {
|
|
86
|
+
// Unmodified — safe to remove
|
|
87
|
+
if (!dryRun) {
|
|
88
|
+
fs.unlinkSync(fullPath);
|
|
89
|
+
cleanEmptyDirs(path.dirname(fullPath), projectDir);
|
|
90
|
+
}
|
|
91
|
+
removed.push(relPath);
|
|
92
|
+
} else if (force) {
|
|
93
|
+
// Modified but --force used
|
|
94
|
+
if (!dryRun) {
|
|
95
|
+
fs.unlinkSync(fullPath);
|
|
96
|
+
cleanEmptyDirs(path.dirname(fullPath), projectDir);
|
|
97
|
+
}
|
|
98
|
+
forced.push(relPath);
|
|
99
|
+
} else {
|
|
100
|
+
customized.push(relPath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Clean CoR hooks from settings.json
|
|
105
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
106
|
+
let hooksRemoved = 0;
|
|
107
|
+
if (fs.existsSync(settingsPath)) {
|
|
108
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
109
|
+
if (settings.hooks) {
|
|
110
|
+
for (const [event, hookGroups] of Object.entries(settings.hooks)) {
|
|
111
|
+
const filtered = hookGroups.filter(group => {
|
|
112
|
+
const commands = group.hooks.map(h => h.command);
|
|
113
|
+
return !commands.some(cmd => COR_HOOK_PATTERNS.includes(cmd));
|
|
114
|
+
});
|
|
115
|
+
const removedCount = hookGroups.length - filtered.length;
|
|
116
|
+
hooksRemoved += removedCount;
|
|
117
|
+
if (filtered.length === 0) {
|
|
118
|
+
delete settings.hooks[event];
|
|
119
|
+
} else {
|
|
120
|
+
settings.hooks[event] = filtered;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
124
|
+
delete settings.hooks;
|
|
125
|
+
}
|
|
126
|
+
if (!dryRun) {
|
|
127
|
+
if (Object.keys(settings).length === 0) {
|
|
128
|
+
fs.unlinkSync(settingsPath);
|
|
129
|
+
} else {
|
|
130
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Remove .pibrc.json last
|
|
137
|
+
const pibrcPath = path.join(projectDir, METADATA_FILE);
|
|
138
|
+
if (!dryRun && fs.existsSync(pibrcPath)) {
|
|
139
|
+
fs.unlinkSync(pibrcPath);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Print summary
|
|
143
|
+
if (removed.length > 0) {
|
|
144
|
+
console.log(` ✅ Removed ${removed.length} unmodified file${removed.length === 1 ? '' : 's'}`);
|
|
145
|
+
for (const f of removed) console.log(` [REMOVED] ${f}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (forced.length > 0) {
|
|
149
|
+
console.log(` ⚠ Force-removed ${forced.length} customized file${forced.length === 1 ? '' : 's'}`);
|
|
150
|
+
for (const f of forced) console.log(` [FORCED] ${f}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (customized.length > 0) {
|
|
154
|
+
console.log(` ⏭ Skipped ${customized.length} customized file${customized.length === 1 ? '' : 's'} (use --force to remove)`);
|
|
155
|
+
for (const f of customized) console.log(` [CUSTOMIZED] ${f}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (missing.length > 0) {
|
|
159
|
+
console.log(` ℹ ${missing.length} manifest file${missing.length === 1 ? '' : 's'} already removed`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (hooksRemoved > 0) {
|
|
163
|
+
console.log(` 🔧 Removed ${hooksRemoved} CoR hook${hooksRemoved === 1 ? '' : 's'} from settings.json`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
console.log(` 📝 Removed ${METADATA_FILE}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log('\n Reset complete.\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Remove empty directories up to (but not including) the stop directory.
|
|
175
|
+
*/
|
|
176
|
+
function cleanEmptyDirs(dir, stopDir) {
|
|
177
|
+
const resolved = path.resolve(dir);
|
|
178
|
+
const stop = path.resolve(stopDir);
|
|
179
|
+
|
|
180
|
+
if (resolved === stop || !resolved.startsWith(stop)) return;
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const entries = fs.readdirSync(resolved);
|
|
184
|
+
if (entries.length === 0) {
|
|
185
|
+
fs.rmdirSync(resolved);
|
|
186
|
+
cleanEmptyDirs(path.dirname(resolved), stopDir);
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
// Directory doesn't exist or not empty — stop
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = { reset };
|
package/package.json
CHANGED
package/templates/EXTENSIONS.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Extension Examples: How Flow Uses
|
|
1
|
+
# Extension Examples: How Flow Uses Claude on Rails
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Claude on Rails (CoR) gives you skeletons. Your project fills them in via phase files —
|
|
4
4
|
replacing defaults with project-specific behavior, adding custom phases
|
|
5
5
|
the skeleton doesn't define. This document shows what that looks like
|
|
6
|
-
in practice, using Flow (
|
|
6
|
+
in practice, using Flow (CoR's reference implementation) as the example.
|
|
7
7
|
|
|
8
8
|
Flow is a cognitive workspace built on Claude Code — a GTD system,
|
|
9
9
|
research thread manager, and course management tool. It has a deployed
|
|
10
10
|
web app (Railway), a local SQLite cache, research threads, an inbox
|
|
11
11
|
pipeline, and 25+ skills. It's what happens when a project grows past
|
|
12
|
-
|
|
12
|
+
CoR's defaults while staying on CoR's rails.
|
|
13
13
|
|
|
14
14
|
You and Claude will make different choices for your project. These
|
|
15
15
|
examples show what choices Flow made and why — not what you should do,
|
|
@@ -18,22 +18,22 @@ but what's possible.
|
|
|
18
18
|
|
|
19
19
|
## Flow's Phase File Overrides by Skill
|
|
20
20
|
|
|
21
|
-
For each skeleton skill, the tables show: which
|
|
21
|
+
For each skeleton skill, the tables show: which CoR phase files Flow
|
|
22
22
|
overrides (replacing default behavior with Flow-specific content), and
|
|
23
|
-
which custom phase files Flow adds (concerns
|
|
23
|
+
which custom phase files Flow adds (concerns CoR doesn't define).
|
|
24
24
|
|
|
25
|
-
Phase file states: **default** =
|
|
25
|
+
Phase file states: **default** = CoR's behavior is fine, no override
|
|
26
26
|
needed. **override** = Flow writes its own content. **custom** = Flow
|
|
27
|
-
adds a phase
|
|
27
|
+
adds a phase CoR doesn't have. **skip** = Flow actively opts out.
|
|
28
28
|
|
|
29
29
|
### orient
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
CoR's orient skeleton loads context, syncs data, scans work, checks
|
|
32
32
|
health, and presents a briefing. Flow's orient is its most complex
|
|
33
33
|
skill — 47 steps managing a deployed app, research threads, an inbox
|
|
34
34
|
pipeline, and multiple always-on perspectives.
|
|
35
35
|
|
|
36
|
-
| Phase |
|
|
36
|
+
| Phase | CoR Default | Flow Override |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `context.md` | Read status files | `system-status.md`, 7 research threads (`thread.yaml` + `CLAUDE.md`), `MEMORY.md` index, active course syllabi |
|
|
39
39
|
| `data-sync.md` | Skip | DB pull from Railway (`sync-db-to-railway.sh --pull`). Report failure but don't block. |
|
|
@@ -60,12 +60,12 @@ custom phases as concerns emerge.
|
|
|
60
60
|
|
|
61
61
|
### debrief
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
CoR's debrief skeleton inventories work, closes items, updates state,
|
|
64
64
|
records lessons, and captures loose ends. Flow's debrief is the second
|
|
65
65
|
most complex skill — it resolves feedback comments, runs prep scout and
|
|
66
66
|
articulation sweeps, checks machine-level drift, and enforces QA gates.
|
|
67
67
|
|
|
68
|
-
| Phase |
|
|
68
|
+
| Phase | CoR Default | Flow Override |
|
|
69
69
|
|---|---|---|
|
|
70
70
|
| `inventory.md` | Scan git log + conversation | Same approach, plus check for uncommitted work, unresolved preview tool sessions |
|
|
71
71
|
| `close-work.md` | Match session work against pib-db actions | Match against Railway API actions. QA perspective gate: actions can't be marked complete if QA report shows failures. Project auto-completion scan. |
|
|
@@ -86,16 +86,16 @@ articulation sweeps, checks machine-level drift, and enforces QA gates.
|
|
|
86
86
|
| `project-completion.md` | Check if any projects have all actions completed, prompt for project close |
|
|
87
87
|
|
|
88
88
|
**Key pattern:** Flow's debrief has *mandatory perspectives* — historian
|
|
89
|
-
and life-tracker always activate. In
|
|
89
|
+
and life-tracker always activate. In CoR's skeleton, perspectives are
|
|
90
90
|
optional (the `perspectives.md` phase can be empty). Flow overrides this
|
|
91
91
|
by listing required perspectives in its close-work and loose-ends phases.
|
|
92
92
|
|
|
93
93
|
### validate
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
CoR's validate skeleton runs validators from a single phase file. Flow
|
|
96
96
|
overrides with 8 specific validators.
|
|
97
97
|
|
|
98
|
-
| Phase |
|
|
98
|
+
| Phase | CoR Default | Flow Override |
|
|
99
99
|
|---|---|---|
|
|
100
100
|
| `validators.md` | Commented-out examples | 8 validators: fid coverage (`validate-fids.sh`), thread structure (`validate-threads.sh`), folder integrity (`validate-folders.sh`), MEMORY.md references, TypeScript (`npx tsc --noEmit`), Vite build (`npx vite build`), ESLint (`npx eslint`), Mantine import check |
|
|
101
101
|
|
|
@@ -104,11 +104,11 @@ does the job. Flow just fills in its specific checks.
|
|
|
104
104
|
|
|
105
105
|
### plan
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
CoR's plan skeleton researches, checks overlap, drafts with template,
|
|
108
108
|
runs perspective critique, checks completeness, presents, and files work.
|
|
109
109
|
Flow's plan adds a design committee pattern and specific work tracking.
|
|
110
110
|
|
|
111
|
-
| Phase |
|
|
111
|
+
| Phase | CoR Default | Flow Override |
|
|
112
112
|
|---|---|---|
|
|
113
113
|
| `research.md` | Explore codebase | Same, plus check ecosystem memory (`reference-skill-ecosystem.md`) for prior art |
|
|
114
114
|
| `overlap-check.md` | Query pib-db | Query Railway DB via sqlite3 on local cache. Check open actions + recent completed. |
|
|
@@ -126,11 +126,11 @@ Flow's plan adds a design committee pattern and specific work tracking.
|
|
|
126
126
|
|
|
127
127
|
### execute
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
CoR's execute skeleton loads the plan, activates perspectives, implements
|
|
130
130
|
with checkpoints, validates, and commits. Flow adds QA enforcement and
|
|
131
131
|
design mock verification.
|
|
132
132
|
|
|
133
|
-
| Phase |
|
|
133
|
+
| Phase | CoR Default | Flow Override |
|
|
134
134
|
|---|---|---|
|
|
135
135
|
| `load-plan.md` | Read plan from conversation or file | Same, plus check for design mocks in action notes (`mock_path`) and `.claude/mocks/` |
|
|
136
136
|
| `perspectives.md` | Activate relevant perspectives | QA always-on. Boundary-conditions always-on. Others per plan's surface area. |
|
|
@@ -141,15 +141,15 @@ design mock verification.
|
|
|
141
141
|
**Key pattern:** Flow's execute enforces a QA gate — the QA perspective
|
|
142
142
|
produces a verification report at checkpoint 3, and debrief won't mark
|
|
143
143
|
actions complete if QA shows failures. This is the "mandatory perspective"
|
|
144
|
-
pattern: a perspective that's advisory in
|
|
144
|
+
pattern: a perspective that's advisory in CoR becomes load-bearing in Flow.
|
|
145
145
|
|
|
146
146
|
### audit
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
CoR's audit skeleton selects perspectives, runs structural checks, loads
|
|
149
149
|
triage suppression, spawns perspective agents, and persists findings.
|
|
150
150
|
Flow overrides the data layer and execution model.
|
|
151
151
|
|
|
152
|
-
| Phase |
|
|
152
|
+
| Phase | CoR Default | Flow Override |
|
|
153
153
|
|---|---|---|
|
|
154
154
|
| `perspective-selection.md` | Discover from SKILL.md files, use groups | Same discovery, but 21 additional domain perspectives available |
|
|
155
155
|
| `structural-checks.md` | Run fast structural scripts | Same validators as `/validate`, run before LLM analysis |
|
|
@@ -164,19 +164,19 @@ Flow overrides the data layer and execution model.
|
|
|
164
164
|
| `triage.md` | Programmatic triage of findings via Railway API PATCH endpoint (approve/reject/defer) |
|
|
165
165
|
| `next-steps.md` | Offer quick-fix, /plan, or bulk triage based on findings after output |
|
|
166
166
|
|
|
167
|
-
**Why nested agents instead of subprocesses:**
|
|
167
|
+
**Why nested agents instead of subprocesses:** CoR's default uses
|
|
168
168
|
`claude --print` for parallel perspective execution. Flow found that
|
|
169
169
|
subprocess spawning creates API concurrency errors during active
|
|
170
170
|
sessions. Nested Agent tool calls avoid this. A project without this
|
|
171
|
-
constraint can use
|
|
171
|
+
constraint can use CoR's subprocess default.
|
|
172
172
|
|
|
173
173
|
### pulse
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
CoR's pulse skeleton checks self-description accuracy, spots dead
|
|
176
176
|
references, and detects staleness. Flow overrides with extensive
|
|
177
177
|
count verification and principle practice checks.
|
|
178
178
|
|
|
179
|
-
| Phase |
|
|
179
|
+
| Phase | CoR Default | Flow Override |
|
|
180
180
|
|---|---|---|
|
|
181
181
|
| `checks.md` | Count freshness, dead references | Count freshness across 3 files (`project-skills-infrastructure.md`, `system-status.md`, `_context.md`). Dead reference spot-check with rotation tracking (`pulse-state.json`). Skill staleness (last-verified > 30d). Rules enforcement health. Session drift detection. Principle spot-check (samples different principle each run). Memory index auto-fix. |
|
|
182
182
|
| `auto-fix-scope.md` | Closed list of safe fixes | Same closed list: numeric counts, MEMORY.md filenames, system-status moves, `_context.md` perspective lists, last-verified dates |
|
|
@@ -190,10 +190,10 @@ project could adopt.
|
|
|
190
190
|
|
|
191
191
|
### triage-audit
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
CoR's triage skeleton loads findings, presents via UI, and applies
|
|
194
194
|
verdicts. Flow overrides the data source and verdict application.
|
|
195
195
|
|
|
196
|
-
| Phase |
|
|
196
|
+
| Phase | CoR Default | Flow Override |
|
|
197
197
|
|---|---|---|
|
|
198
198
|
| `load-findings.md` | Query pib-db or read JSON files | Fetch from Railway DB via API with severity + perspective ordering |
|
|
199
199
|
| `triage-ui.md` | Start local `triage-server.mjs`, open browser | Same local server, but opened via Chrome MCP tool for reading verdicts |
|
|
@@ -218,7 +218,7 @@ project could implement for its own domain.
|
|
|
218
218
|
| Change-type deployment | `/deploy` | Classifies changes (markdown-only, code, mixed), takes appropriate deploy path (git push vs git push + platform deploy), verifies | Any project with multiple deployment paths depending on what changed. The classify-deploy-verify loop is the pattern. |
|
|
219
219
|
| Area health review | `/quarterly-review` | SQL-driven area-by-area walkthrough: stale actions, recurring cadence health, person context freshness, open loops, supply patterns | Any project with areas of responsibility benefits from periodic health queries. The SQL query templates are reusable. |
|
|
220
220
|
|
|
221
|
-
These are future extraction candidates. If
|
|
221
|
+
These are future extraction candidates. If CoR grows beyond Wave 6, the
|
|
222
222
|
highest-value patterns to skeleton would be: entity scaffolding (widely
|
|
223
223
|
applicable), prompt refinement (universal for Claude Code projects), and
|
|
224
224
|
command queue processing (needed by any project with an async work queue).
|
|
@@ -226,7 +226,7 @@ command queue processing (needed by any project with an async work queue).
|
|
|
226
226
|
|
|
227
227
|
## Writing Domain-Specific Perspectives
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
CoR ships 14 generic perspectives. Flow has 19 additional domain-specific
|
|
230
230
|
perspectives. Here are three examples showing how to write your own.
|
|
231
231
|
|
|
232
232
|
### Example 1: GTD (encoding domain expertise)
|
|
@@ -305,7 +305,7 @@ Eight skills contained reusable patterns (documented in the table above).
|
|
|
305
305
|
The full analysis is in the methodology essay's Wave 6 findings section.
|
|
306
306
|
|
|
307
307
|
These are patterns, not extraction candidates — yet. The patterns are
|
|
308
|
-
documented here so
|
|
308
|
+
documented here so CoR users know what's possible. If your project needs
|
|
309
309
|
entity scaffolding or prompt refinement, you'll write your own version.
|
|
310
|
-
If
|
|
310
|
+
If CoR grows a Wave 7, the strongest candidates for skeleton extraction
|
|
311
311
|
are scaffold, refine-prompts, and handle-findings.
|
package/templates/README.md
CHANGED
|
@@ -59,7 +59,7 @@ adoption is straightforward: copy what you need into your project's
|
|
|
59
59
|
| `skills/triage-audit/SKILL.md` | 5 | **Triage skeleton.** Load findings, prepare commentary, present via local web UI or CLI, apply verdicts (fix/defer/reject), create actions for approved findings. 3 phase files. |
|
|
60
60
|
| `skills/onboard/SKILL.md` | 7 | **Onboarding skeleton.** Conversational interview that generates the initial context layer. Re-runnable: first run generates, subsequent runs refine. 6 phase files. |
|
|
61
61
|
| `skills/seed/SKILL.md` | 7 | **Capability seeding skeleton.** Detects technology adoption signals, proposes expertise conversations, builds and maintains perspectives collaboratively. 4 phase files. |
|
|
62
|
-
| `skills/upgrade/SKILL.md` | 7 | **Upgrade skeleton.** Conversational merge when new
|
|
62
|
+
| `skills/upgrade/SKILL.md` | 7 | **Upgrade skeleton.** Conversational merge when new CoR skeletons arrive. Intelligence is the merge strategy — conversation, not mechanical copy. 4 phase files. |
|
|
63
63
|
|
|
64
64
|
### Scripts (6)
|
|
65
65
|
|
|
@@ -106,7 +106,7 @@ execution, audit). Each is a named domain expert encoded in markdown.
|
|
|
106
106
|
|
|
107
107
|
| Perspective | Domain | Activation |
|
|
108
108
|
|------------|--------|-----------|
|
|
109
|
-
| `box-health` |
|
|
109
|
+
| `box-health` | CoR adoption health, phase file coverage, configuration drift, anti-bloat | Always-on during audit |
|
|
110
110
|
|
|
111
111
|
**Infrastructure files (7):**
|
|
112
112
|
|