@thebassclef/lite 1.9.11 → 1.9.13

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 CHANGED
@@ -67,7 +67,7 @@ your browser.
67
67
 
68
68
  ## Current release
69
69
 
70
- <!-- version-start -->1.9.11<!-- version-end -->
70
+ <!-- version-start -->1.9.13<!-- version-end -->
71
71
 
72
72
  See [CHANGELOG.md](CHANGELOG.md) for the full changelog. Adopter-facing
73
73
  release notes live at [docs.bassclef.dev/docs/whats-new/releases](https://docs.bassclef.dev/docs/whats-new/releases).
package/dist/cli.cjs CHANGED
@@ -272,6 +272,47 @@ function hashContent(content) {
272
272
  normalized = normalized.replace(/\r\n/g, "\n");
273
273
  return node_crypto.createHash("sha256").update(normalized, "utf8").digest("hex");
274
274
  }
275
+ const SCHEMA_VERSION = "1.0.0";
276
+ const WRITER_SLUG = "bassclef-init-npm";
277
+ function registerInstallWrittenPaths(targetDir, paths, now = /* @__PURE__ */ new Date()) {
278
+ if (paths.length === 0) return;
279
+ const manifestPath = node_path.join(targetDir, "state", "install-written-paths.json");
280
+ let manifest = {
281
+ schema_version: SCHEMA_VERSION,
282
+ entries: []
283
+ };
284
+ if (node_fs.existsSync(manifestPath)) {
285
+ try {
286
+ const parsed = JSON.parse(node_fs.readFileSync(manifestPath, "utf8"));
287
+ if (parsed && typeof parsed === "object" && "entries" in parsed && Array.isArray(parsed.entries)) {
288
+ manifest = parsed;
289
+ manifest.schema_version = SCHEMA_VERSION;
290
+ }
291
+ } catch {
292
+ }
293
+ }
294
+ const writtenAt = now.toISOString().replace(/\.\d{3}Z$/, "Z");
295
+ const uniqueInputs = /* @__PURE__ */ new Map();
296
+ for (const p of paths) {
297
+ uniqueInputs.set(p.relativePath, p);
298
+ }
299
+ const incomingPaths = new Set(uniqueInputs.keys());
300
+ manifest.entries = manifest.entries.filter((e) => !incomingPaths.has(e.path));
301
+ for (const p of uniqueInputs.values()) {
302
+ manifest.entries.push({
303
+ path: p.relativePath,
304
+ writer: WRITER_SLUG,
305
+ written_at: writtenAt,
306
+ content_hash: `sha256:${p.contentHashSha256}`
307
+ });
308
+ }
309
+ node_fs.mkdirSync(node_path.dirname(manifestPath), { recursive: true });
310
+ node_fs.writeFileSync(
311
+ manifestPath,
312
+ JSON.stringify(manifest, null, 2) + "\n",
313
+ "utf8"
314
+ );
315
+ }
275
316
  const SUBSTRATE_CONFIG_TEMPLATE_VERSION = "0.0.1";
276
317
  function substrateConfigMdTemplate(pkgVersion) {
277
318
  return [
@@ -549,6 +590,7 @@ function copySubstrate(targetDir, options = {}) {
549
590
  copied: [],
550
591
  copiedEntries: [],
551
592
  refused: [],
593
+ unchanged: [],
552
594
  errored: [],
553
595
  erroredMessages: [],
554
596
  hookCount: 0,
@@ -736,6 +778,16 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
736
778
  } catch (e) {
737
779
  if (e instanceof WriteError) {
738
780
  if (e.kind === "AlreadyExists") {
781
+ if (scope === "user") {
782
+ try {
783
+ const existing = node_fs.readFileSync(targetPath, "utf8");
784
+ if (hashContent(existing) === hashContent(outputContent)) {
785
+ result.unchanged.push(adopterRelPath);
786
+ return "unchanged";
787
+ }
788
+ } catch {
789
+ }
790
+ }
739
791
  result.refused.push(adopterRelPath);
740
792
  return "refused";
741
793
  }
@@ -1028,6 +1080,24 @@ function matchesDesired(current) {
1028
1080
  const c = current;
1029
1081
  return c.type === STATUSLINE_FIELD.type && c.command === STATUSLINE_FIELD.command;
1030
1082
  }
1083
+ const VALUE_TEST_BLOCK = `Bassclef is ready.
1084
+
1085
+ It added engineering workflows and automatic checks to Claude Code.
1086
+ Nothing else needs configuring.
1087
+
1088
+ Try it on something small:
1089
+
1090
+ claude
1091
+ /riff
1092
+
1093
+ Describe a feature you're considering. You'll get three approaches before any code is written.
1094
+
1095
+ Then choose one:
1096
+ /launch
1097
+
1098
+ When the plan looks right:
1099
+ /build
1100
+ `;
1031
1101
  const FAMILIES = [
1032
1102
  "skills",
1033
1103
  "rules",
@@ -1079,13 +1149,14 @@ function buildInitReport(input) {
1079
1149
  else project += 1;
1080
1150
  }
1081
1151
  const refused = input.refused.length;
1152
+ const unchanged = input.unchanged?.length ?? 0;
1082
1153
  const errored = input.errored.length;
1083
1154
  const written = input.configs.length + input.entries.length;
1084
1155
  return {
1085
1156
  schema_version: 3,
1086
1157
  tier: input.tier,
1087
1158
  totals: {
1088
- files: written + refused + errored,
1159
+ files: written + refused + unchanged + errored,
1089
1160
  written,
1090
1161
  project,
1091
1162
  user,
@@ -1098,6 +1169,7 @@ function buildInitReport(input) {
1098
1169
  files: catalog.hooks
1099
1170
  },
1100
1171
  refused,
1172
+ unchanged,
1101
1173
  errored,
1102
1174
  failed: refused + errored
1103
1175
  };
@@ -1203,6 +1275,7 @@ function runInit(argv) {
1203
1275
  entries: outcome.result?.wouldCopyEntries ?? [],
1204
1276
  configs: plans.map((pl) => ({ path: pl.relativePath })),
1205
1277
  refused: outcome.result?.refused ?? [],
1278
+ unchanged: outcome.result?.unchanged ?? [],
1206
1279
  // A dry run still reads every source file, so it can still fail
1207
1280
  // to read one. Hard-coding this empty hid those failures from
1208
1281
  // the JSON (RFC-0005 A-3).
@@ -1301,6 +1374,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say
1301
1374
  }
1302
1375
  const parts = [];
1303
1376
  if (result.copied.length > 0) parts.push(`${result.copied.length} substrate files copied`);
1377
+ if (result.unchanged.length > 0) parts.push(`${result.unchanged.length} already-present`);
1304
1378
  if (result.refused.length > 0) parts.push(`${result.refused.length} refused`);
1305
1379
  if (result.errored.length > 0) parts.push(`${result.errored.length} error(s)`);
1306
1380
  say(`bassclef init: ${parts.join(", ")}.
@@ -1366,32 +1440,34 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say
1366
1440
  catalogCounts["root-docs"] += 1;
1367
1441
  }
1368
1442
  }
1369
- const claudeCounts = [
1370
- catalogCounts.skills > 0 ? `${catalogCounts.skills} skills` : null,
1371
- catalogCounts.rules > 0 ? `${catalogCounts.rules} rules` : null,
1372
- catalogCounts.agents > 0 ? `${catalogCounts.agents} agents` : null,
1373
- catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
1374
- ].filter((s) => s !== null);
1375
- if (claudeCounts.length > 0) {
1376
- say(
1377
- `bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
1443
+ if (verbose) {
1444
+ const claudeCounts = [
1445
+ catalogCounts.skills > 0 ? `${catalogCounts.skills} skills` : null,
1446
+ catalogCounts.rules > 0 ? `${catalogCounts.rules} rules` : null,
1447
+ catalogCounts.agents > 0 ? `${catalogCounts.agents} agents` : null,
1448
+ catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
1449
+ ].filter((s) => s !== null);
1450
+ if (claudeCounts.length > 0) {
1451
+ say(
1452
+ `bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
1378
1453
  `
1379
- );
1380
- }
1381
- const otherCounts = [
1382
- catalogCounts.libs > 0 ? `${catalogCounts.libs} libs` : null,
1383
- catalogCounts.adrs > 0 ? `${catalogCounts.adrs} ADRs` : null,
1384
- catalogCounts.templates > 0 ? `${catalogCounts.templates} templates` : null,
1385
- catalogCounts["presence-templates"] > 0 ? `${catalogCounts["presence-templates"]} presence-templates` : null,
1386
- catalogCounts.standards > 0 ? `${catalogCounts.standards} standards` : null,
1387
- catalogCounts["root-docs"] > 0 ? `${catalogCounts["root-docs"]} root-docs` : null,
1388
- catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
1389
- ].filter((s) => s !== null);
1390
- if (otherCounts.length > 0) {
1391
- say(
1392
- `bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
1454
+ );
1455
+ }
1456
+ const otherCounts = [
1457
+ catalogCounts.libs > 0 ? `${catalogCounts.libs} libs` : null,
1458
+ catalogCounts.adrs > 0 ? `${catalogCounts.adrs} ADRs` : null,
1459
+ catalogCounts.templates > 0 ? `${catalogCounts.templates} templates` : null,
1460
+ catalogCounts["presence-templates"] > 0 ? `${catalogCounts["presence-templates"]} presence-templates` : null,
1461
+ catalogCounts.standards > 0 ? `${catalogCounts.standards} standards` : null,
1462
+ catalogCounts["root-docs"] > 0 ? `${catalogCounts["root-docs"]} root-docs` : null,
1463
+ catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
1464
+ ].filter((s) => s !== null);
1465
+ if (otherCounts.length > 0) {
1466
+ say(
1467
+ `bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
1393
1468
  `
1394
- );
1469
+ );
1470
+ }
1395
1471
  }
1396
1472
  if (result.refused.length > 0) {
1397
1473
  say(
@@ -1531,12 +1607,25 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json, skipStatus
1531
1607
  entries: walker.result?.copiedEntries ?? [],
1532
1608
  configs: results.map((r) => ({ path: r.plan.relativePath })),
1533
1609
  refused: walker.result?.refused ?? [],
1610
+ unchanged: walker.result?.unchanged ?? [],
1534
1611
  errored: walker.result?.errored ?? [],
1535
1612
  hookCount: walker.result?.hookCount ?? 0,
1536
1613
  declaredHooksCopied,
1537
1614
  tier: RESOLVED_TIER
1538
1615
  });
1539
1616
  writeManifest(targetDir, results, walker.result);
1617
+ const createdConfigPaths = results.filter((r) => r.outcome === "created").map((r) => ({
1618
+ relativePath: r.plan.relativePath,
1619
+ contentHashSha256: hashContent(r.plan.content)
1620
+ }));
1621
+ const createdBundlePaths = (walker.result?.copiedEntries ?? []).filter((e) => typeof e.content_hash_sha256 === "string").map((e) => ({
1622
+ relativePath: e.path,
1623
+ contentHashSha256: e.content_hash_sha256
1624
+ }));
1625
+ registerInstallWrittenPaths(targetDir, [
1626
+ ...createdConfigPaths,
1627
+ ...createdBundlePaths
1628
+ ]);
1540
1629
  if (walker.code === 0) {
1541
1630
  say(
1542
1631
  `bassclef init: your substrate lives under .claude/. Add .claude/ to .gitignore if you have not.
@@ -1562,6 +1651,9 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json, skipStatus
1562
1651
  process.stdout.write(t);
1563
1652
  });
1564
1653
  }
1654
+ if (!json && walker.code === 0) {
1655
+ say(VALUE_TEST_BLOCK);
1656
+ }
1565
1657
  return walker.code;
1566
1658
  }
1567
1659
  function shouldRefuseRoot(currentUid, allowRoot) {
@@ -1606,6 +1698,15 @@ function writeManifest(targetDir, results, walkerResult) {
1606
1698
  source: "bundle"
1607
1699
  });
1608
1700
  }
1701
+ for (const path of walkerResult.unchanged) {
1702
+ entries.push({
1703
+ path,
1704
+ template: `bundle:${path}`,
1705
+ template_version: index.version,
1706
+ outcome: "unchanged",
1707
+ source: "bundle"
1708
+ });
1709
+ }
1609
1710
  for (const path of walkerResult.errored) {
1610
1711
  entries.push({
1611
1712
  path,
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { version } from "./index.js";
3
- import { realpathSync, statSync, constants, lstatSync, mkdirSync, accessSync, unlinkSync, openSync, writeSync, closeSync, readlinkSync, readFileSync, chmodSync, readdirSync, existsSync, writeFileSync, renameSync } from "node:fs";
3
+ import { realpathSync, statSync, constants, lstatSync, mkdirSync, accessSync, unlinkSync, openSync, writeSync, closeSync, readlinkSync, existsSync, readFileSync, writeFileSync, chmodSync, readdirSync, renameSync } from "node:fs";
4
4
  import { isAbsolute, resolve, dirname, join, normalize, relative, basename as basename$1 } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { createHash } from "node:crypto";
@@ -249,6 +249,47 @@ function hashContent(content) {
249
249
  normalized = normalized.replace(/\r\n/g, "\n");
250
250
  return createHash("sha256").update(normalized, "utf8").digest("hex");
251
251
  }
252
+ const SCHEMA_VERSION = "1.0.0";
253
+ const WRITER_SLUG = "bassclef-init-npm";
254
+ function registerInstallWrittenPaths(targetDir, paths, now = /* @__PURE__ */ new Date()) {
255
+ if (paths.length === 0) return;
256
+ const manifestPath = join(targetDir, "state", "install-written-paths.json");
257
+ let manifest = {
258
+ schema_version: SCHEMA_VERSION,
259
+ entries: []
260
+ };
261
+ if (existsSync(manifestPath)) {
262
+ try {
263
+ const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
264
+ if (parsed && typeof parsed === "object" && "entries" in parsed && Array.isArray(parsed.entries)) {
265
+ manifest = parsed;
266
+ manifest.schema_version = SCHEMA_VERSION;
267
+ }
268
+ } catch {
269
+ }
270
+ }
271
+ const writtenAt = now.toISOString().replace(/\.\d{3}Z$/, "Z");
272
+ const uniqueInputs = /* @__PURE__ */ new Map();
273
+ for (const p of paths) {
274
+ uniqueInputs.set(p.relativePath, p);
275
+ }
276
+ const incomingPaths = new Set(uniqueInputs.keys());
277
+ manifest.entries = manifest.entries.filter((e) => !incomingPaths.has(e.path));
278
+ for (const p of uniqueInputs.values()) {
279
+ manifest.entries.push({
280
+ path: p.relativePath,
281
+ writer: WRITER_SLUG,
282
+ written_at: writtenAt,
283
+ content_hash: `sha256:${p.contentHashSha256}`
284
+ });
285
+ }
286
+ mkdirSync(dirname(manifestPath), { recursive: true });
287
+ writeFileSync(
288
+ manifestPath,
289
+ JSON.stringify(manifest, null, 2) + "\n",
290
+ "utf8"
291
+ );
292
+ }
252
293
  const SUBSTRATE_CONFIG_TEMPLATE_VERSION = "0.0.1";
253
294
  function substrateConfigMdTemplate(pkgVersion) {
254
295
  return [
@@ -526,6 +567,7 @@ function copySubstrate(targetDir, options = {}) {
526
567
  copied: [],
527
568
  copiedEntries: [],
528
569
  refused: [],
570
+ unchanged: [],
529
571
  errored: [],
530
572
  erroredMessages: [],
531
573
  hookCount: 0,
@@ -713,6 +755,16 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
713
755
  } catch (e) {
714
756
  if (e instanceof WriteError) {
715
757
  if (e.kind === "AlreadyExists") {
758
+ if (scope === "user") {
759
+ try {
760
+ const existing = readFileSync(targetPath, "utf8");
761
+ if (hashContent(existing) === hashContent(outputContent)) {
762
+ result.unchanged.push(adopterRelPath);
763
+ return "unchanged";
764
+ }
765
+ } catch {
766
+ }
767
+ }
716
768
  result.refused.push(adopterRelPath);
717
769
  return "refused";
718
770
  }
@@ -1005,6 +1057,24 @@ function matchesDesired(current) {
1005
1057
  const c = current;
1006
1058
  return c.type === STATUSLINE_FIELD.type && c.command === STATUSLINE_FIELD.command;
1007
1059
  }
1060
+ const VALUE_TEST_BLOCK = `Bassclef is ready.
1061
+
1062
+ It added engineering workflows and automatic checks to Claude Code.
1063
+ Nothing else needs configuring.
1064
+
1065
+ Try it on something small:
1066
+
1067
+ claude
1068
+ /riff
1069
+
1070
+ Describe a feature you're considering. You'll get three approaches before any code is written.
1071
+
1072
+ Then choose one:
1073
+ /launch
1074
+
1075
+ When the plan looks right:
1076
+ /build
1077
+ `;
1008
1078
  const FAMILIES = [
1009
1079
  "skills",
1010
1080
  "rules",
@@ -1056,13 +1126,14 @@ function buildInitReport(input) {
1056
1126
  else project += 1;
1057
1127
  }
1058
1128
  const refused = input.refused.length;
1129
+ const unchanged = input.unchanged?.length ?? 0;
1059
1130
  const errored = input.errored.length;
1060
1131
  const written = input.configs.length + input.entries.length;
1061
1132
  return {
1062
1133
  schema_version: 3,
1063
1134
  tier: input.tier,
1064
1135
  totals: {
1065
- files: written + refused + errored,
1136
+ files: written + refused + unchanged + errored,
1066
1137
  written,
1067
1138
  project,
1068
1139
  user,
@@ -1075,6 +1146,7 @@ function buildInitReport(input) {
1075
1146
  files: catalog.hooks
1076
1147
  },
1077
1148
  refused,
1149
+ unchanged,
1078
1150
  errored,
1079
1151
  failed: refused + errored
1080
1152
  };
@@ -1180,6 +1252,7 @@ function runInit(argv) {
1180
1252
  entries: outcome.result?.wouldCopyEntries ?? [],
1181
1253
  configs: plans.map((pl) => ({ path: pl.relativePath })),
1182
1254
  refused: outcome.result?.refused ?? [],
1255
+ unchanged: outcome.result?.unchanged ?? [],
1183
1256
  // A dry run still reads every source file, so it can still fail
1184
1257
  // to read one. Hard-coding this empty hid those failures from
1185
1258
  // the JSON (RFC-0005 A-3).
@@ -1278,6 +1351,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say
1278
1351
  }
1279
1352
  const parts = [];
1280
1353
  if (result.copied.length > 0) parts.push(`${result.copied.length} substrate files copied`);
1354
+ if (result.unchanged.length > 0) parts.push(`${result.unchanged.length} already-present`);
1281
1355
  if (result.refused.length > 0) parts.push(`${result.refused.length} refused`);
1282
1356
  if (result.errored.length > 0) parts.push(`${result.errored.length} error(s)`);
1283
1357
  say(`bassclef init: ${parts.join(", ")}.
@@ -1343,32 +1417,34 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say
1343
1417
  catalogCounts["root-docs"] += 1;
1344
1418
  }
1345
1419
  }
1346
- const claudeCounts = [
1347
- catalogCounts.skills > 0 ? `${catalogCounts.skills} skills` : null,
1348
- catalogCounts.rules > 0 ? `${catalogCounts.rules} rules` : null,
1349
- catalogCounts.agents > 0 ? `${catalogCounts.agents} agents` : null,
1350
- catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
1351
- ].filter((s) => s !== null);
1352
- if (claudeCounts.length > 0) {
1353
- say(
1354
- `bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
1420
+ if (verbose) {
1421
+ const claudeCounts = [
1422
+ catalogCounts.skills > 0 ? `${catalogCounts.skills} skills` : null,
1423
+ catalogCounts.rules > 0 ? `${catalogCounts.rules} rules` : null,
1424
+ catalogCounts.agents > 0 ? `${catalogCounts.agents} agents` : null,
1425
+ catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
1426
+ ].filter((s) => s !== null);
1427
+ if (claudeCounts.length > 0) {
1428
+ say(
1429
+ `bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
1355
1430
  `
1356
- );
1357
- }
1358
- const otherCounts = [
1359
- catalogCounts.libs > 0 ? `${catalogCounts.libs} libs` : null,
1360
- catalogCounts.adrs > 0 ? `${catalogCounts.adrs} ADRs` : null,
1361
- catalogCounts.templates > 0 ? `${catalogCounts.templates} templates` : null,
1362
- catalogCounts["presence-templates"] > 0 ? `${catalogCounts["presence-templates"]} presence-templates` : null,
1363
- catalogCounts.standards > 0 ? `${catalogCounts.standards} standards` : null,
1364
- catalogCounts["root-docs"] > 0 ? `${catalogCounts["root-docs"]} root-docs` : null,
1365
- catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
1366
- ].filter((s) => s !== null);
1367
- if (otherCounts.length > 0) {
1368
- say(
1369
- `bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
1431
+ );
1432
+ }
1433
+ const otherCounts = [
1434
+ catalogCounts.libs > 0 ? `${catalogCounts.libs} libs` : null,
1435
+ catalogCounts.adrs > 0 ? `${catalogCounts.adrs} ADRs` : null,
1436
+ catalogCounts.templates > 0 ? `${catalogCounts.templates} templates` : null,
1437
+ catalogCounts["presence-templates"] > 0 ? `${catalogCounts["presence-templates"]} presence-templates` : null,
1438
+ catalogCounts.standards > 0 ? `${catalogCounts.standards} standards` : null,
1439
+ catalogCounts["root-docs"] > 0 ? `${catalogCounts["root-docs"]} root-docs` : null,
1440
+ catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
1441
+ ].filter((s) => s !== null);
1442
+ if (otherCounts.length > 0) {
1443
+ say(
1444
+ `bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
1370
1445
  `
1371
- );
1446
+ );
1447
+ }
1372
1448
  }
1373
1449
  if (result.refused.length > 0) {
1374
1450
  say(
@@ -1508,12 +1584,25 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json, skipStatus
1508
1584
  entries: walker.result?.copiedEntries ?? [],
1509
1585
  configs: results.map((r) => ({ path: r.plan.relativePath })),
1510
1586
  refused: walker.result?.refused ?? [],
1587
+ unchanged: walker.result?.unchanged ?? [],
1511
1588
  errored: walker.result?.errored ?? [],
1512
1589
  hookCount: walker.result?.hookCount ?? 0,
1513
1590
  declaredHooksCopied,
1514
1591
  tier: RESOLVED_TIER
1515
1592
  });
1516
1593
  writeManifest(targetDir, results, walker.result);
1594
+ const createdConfigPaths = results.filter((r) => r.outcome === "created").map((r) => ({
1595
+ relativePath: r.plan.relativePath,
1596
+ contentHashSha256: hashContent(r.plan.content)
1597
+ }));
1598
+ const createdBundlePaths = (walker.result?.copiedEntries ?? []).filter((e) => typeof e.content_hash_sha256 === "string").map((e) => ({
1599
+ relativePath: e.path,
1600
+ contentHashSha256: e.content_hash_sha256
1601
+ }));
1602
+ registerInstallWrittenPaths(targetDir, [
1603
+ ...createdConfigPaths,
1604
+ ...createdBundlePaths
1605
+ ]);
1517
1606
  if (walker.code === 0) {
1518
1607
  say(
1519
1608
  `bassclef init: your substrate lives under .claude/. Add .claude/ to .gitignore if you have not.
@@ -1539,6 +1628,9 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json, skipStatus
1539
1628
  process.stdout.write(t);
1540
1629
  });
1541
1630
  }
1631
+ if (!json && walker.code === 0) {
1632
+ say(VALUE_TEST_BLOCK);
1633
+ }
1542
1634
  return walker.code;
1543
1635
  }
1544
1636
  function shouldRefuseRoot(currentUid, allowRoot) {
@@ -1583,6 +1675,15 @@ function writeManifest(targetDir, results, walkerResult) {
1583
1675
  source: "bundle"
1584
1676
  });
1585
1677
  }
1678
+ for (const path of walkerResult.unchanged) {
1679
+ entries.push({
1680
+ path,
1681
+ template: `bundle:${path}`,
1682
+ template_version: version,
1683
+ outcome: "unchanged",
1684
+ source: "bundle"
1685
+ });
1686
+ }
1586
1687
  for (const path of walkerResult.errored) {
1587
1688
  entries.push({
1588
1689
  path,
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const version = "1.9.11";
3
+ const version = "1.9.13";
4
4
  exports.version = version;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version: "1.9.11";
1
+ export declare const version: "1.9.13";
2
2
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = "1.9.11";
1
+ const version = "1.9.13";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thebassclef/lite",
3
- "version": "1.9.11",
3
+ "version": "1.9.13",
4
4
  "description": "Bassclef CLI — install and upgrade bassclef in your project with two commands.",
5
5
  "keywords": [
6
6
  "bassclef",