@friedbotstudio/create-baseline 0.5.0 → 0.6.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/README.md +1 -1
- package/bin/cli.js +33 -6
- package/obj/template/.claude/manifest.json +916 -230
- package/obj/template/.claude/skills/audit-baseline/audit.sh +4 -1
- package/obj/template/.claude/skills/upgrade-project/SKILL.md +121 -0
- package/obj/template/CLAUDE.md +6 -5
- package/obj/template/docs/init/seed.md +4 -4
- package/package.json +1 -1
- package/src/CLAUDE.template.md +6 -5
- package/src/cli/diff-render.js +54 -0
- package/src/cli/install.js +33 -2
- package/src/cli/manifest.js +7 -3
- package/src/cli/merge.js +80 -13
- package/src/cli/tui/upgrade.js +122 -25
- package/src/cli/upgrade-tiers.js +234 -0
- package/src/seed.template.md +4 -4
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ A discipline layer for Claude Code. Hooks at every tool boundary, a workflow tha
|
|
|
41
41
|
|
|
42
42
|
## What this is
|
|
43
43
|
|
|
44
|
-
The Claude Code Baseline is a repository overlay shipped via `npx @friedbotstudio/create-baseline ./target`. It installs **22 hooks** at Claude's tool boundaries, **
|
|
44
|
+
The Claude Code Baseline is a repository overlay shipped via `npx @friedbotstudio/create-baseline ./target`. It installs **22 hooks** at Claude's tool boundaries, **38 skills** organised into ten categories, **1 subagent** for parallel work in isolated worktrees, an **11-phase workflow** from intake to commit, and **3 user-typed consent gates** that Claude cannot forge.
|
|
45
45
|
|
|
46
46
|
Every soft engineering rule a team usually repeats every session — *don't push, don't `--amend`, don't self-approve specs, don't skip phases, don't mock internal modules* — becomes a structural guarantee because the hooks run **outside Claude's tool boundary**. Claude cannot disable a hook with a flag, cannot write a consent marker, cannot reorder the phases without an explicit exception that triage records on disk.
|
|
47
47
|
|
package/bin/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ const PKG_ROOT = resolve(__dirname, '..');
|
|
|
18
18
|
|
|
19
19
|
const HELP_TEXT = `Usage:
|
|
20
20
|
create-baseline <target> [options] install the baseline
|
|
21
|
-
create-baseline upgrade [target] three-
|
|
21
|
+
create-baseline upgrade [target] three-tier merge (mechanical / semantic / binary-prompt)
|
|
22
22
|
create-baseline doctor [target] report drift in an installed target
|
|
23
23
|
|
|
24
24
|
Materializes the Claude Code baseline (.claude/, CLAUDE.md, .mcp.json,
|
|
@@ -31,10 +31,15 @@ Install modes:
|
|
|
31
31
|
|
|
32
32
|
Upgrade:
|
|
33
33
|
Replaces the prior --merge flag. Reads <target>/.claude/.baseline-manifest.json
|
|
34
|
-
and runs a three-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
and runs a three-tier merge against the shipped template:
|
|
35
|
+
- tier 1 (binary prompt): customized files prompt "Keep your version / Use
|
|
36
|
+
new baseline / Show diff" in TTY mode (exit 3 on any skipped).
|
|
37
|
+
- tier 2 (mechanical): files routed through git merge-file --diff3 with
|
|
38
|
+
BASE recovered from .claude/.baseline-prior/ cache or npm fallback;
|
|
39
|
+
clean merges land silently, conflicts surface with markers (exit 4).
|
|
40
|
+
- tier 3 (semantic): staged at .claude/state/upgrade/<ts>/ for the
|
|
41
|
+
/upgrade-project Claude Code skill to reconcile (exit 5).
|
|
42
|
+
Prunes baseline files removed upstream that the user hadn't touched.
|
|
38
43
|
--dry-run Print intended actions without writing.
|
|
39
44
|
|
|
40
45
|
Doctor:
|
|
@@ -227,6 +232,13 @@ async function dispatchUpgrade(target, values, templateDir) {
|
|
|
227
232
|
await usageError(`No baseline manifest at ${manifestPath}. Run a fresh install first.`);
|
|
228
233
|
return 2;
|
|
229
234
|
}
|
|
235
|
+
const { findPendingStage } = await import('../src/cli/upgrade-tiers.js');
|
|
236
|
+
const pending = await findPendingStage(target);
|
|
237
|
+
if (pending) {
|
|
238
|
+
const fileLines = pending.files.map((f) => ` - ${f}`).join('\n');
|
|
239
|
+
io.log(`Pending semantic-merge stage at ${pending.stage_ts}.\n${pending.files.length} file(s) awaiting reconciliation:\n${fileLines}\nOpen Claude Code and run /upgrade-project to reconcile.`);
|
|
240
|
+
return 5;
|
|
241
|
+
}
|
|
230
242
|
if (process.stdout.isTTY) {
|
|
231
243
|
const tui = await import('../src/cli/tui/upgrade.js');
|
|
232
244
|
return tui.run({
|
|
@@ -241,17 +253,32 @@ async function runPlainUpgrade(target, values, templateDir, manifestPath) {
|
|
|
241
253
|
const oldManifest = await loadManifest(manifestPath);
|
|
242
254
|
const tplFiles = await listShippedFiles(templateDir);
|
|
243
255
|
const newManifest = await buildManifestFromDir(templateDir, tplFiles);
|
|
256
|
+
await overlayShippedTiers(templateDir, newManifest);
|
|
244
257
|
if (values['dry-run']) {
|
|
245
258
|
io.log(`Would upgrade ${tplFiles.length} files into ${target}`);
|
|
246
259
|
return 0;
|
|
247
260
|
}
|
|
248
261
|
const report = await threeWayMerge(templateDir, target, oldManifest, newManifest);
|
|
249
262
|
for (const action of report.actions) {
|
|
250
|
-
io.log(`${action.kind.padEnd(
|
|
263
|
+
io.log(`${action.kind.padEnd(28)} ${action.path}`);
|
|
251
264
|
}
|
|
252
265
|
return report.exitCode;
|
|
253
266
|
}
|
|
254
267
|
|
|
268
|
+
async function overlayShippedTiers(templateDir, newManifest) {
|
|
269
|
+
const shippedPath = join(templateDir, '.claude/manifest.json');
|
|
270
|
+
if (!existsSync(shippedPath)) return;
|
|
271
|
+
const { readFile: rf } = await import('node:fs/promises');
|
|
272
|
+
const shipped = JSON.parse(await rf(shippedPath, 'utf8'));
|
|
273
|
+
if (!shipped?.files) return;
|
|
274
|
+
for (const rel of Object.keys(newManifest.files)) {
|
|
275
|
+
const shippedEntry = shipped.files[rel];
|
|
276
|
+
if (shippedEntry && typeof shippedEntry === 'object' && typeof shippedEntry.tier === 'string') {
|
|
277
|
+
newManifest.files[rel] = { sha256: newManifest.files[rel], tier: shippedEntry.tier };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
255
282
|
async function dispatchDoctor(positionals, values) {
|
|
256
283
|
const target = resolve(positionals[1] ?? '.');
|
|
257
284
|
const report = await runDoctor(target, { strict: !!values.strict });
|