@thebassclef/lite 1.1.0 → 1.2.0-alpha.1
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/dist/cli.cjs +258 -63
- package/dist/cli.js +258 -63
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
- package/dist/lite/.claude/bassclef-orientation.md +120 -0
- package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
- package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
- package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
- package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
- package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
- package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
- package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
- package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
- package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
- package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
- package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
- package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
- package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
- package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
- package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
- package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
- package/dist/lite/.claude/hooks/save-state.sh +268 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
- package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
- package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
- package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
- package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
- package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
- package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
- package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
- package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
- package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
- package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
- package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
- package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
- package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
- package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
- package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
- package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
- package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
- package/dist/lite/.claude/rules/cost-governance.md +154 -0
- package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
- package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
- package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
- package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
- package/dist/lite/.claude/rules/session-artifacts.md +30 -0
- package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
- package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
- package/dist/lite/.claude/rules/usability.md +22 -0
- package/dist/lite/.claude/settings.json +20 -0
- package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
- package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
- package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
- package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
- package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
- package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
- package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
- package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
- package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
- package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
- package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
- package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
- package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
- package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
- package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
- package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
- package/dist/lite/.claude/skills/release/SKILL.md +311 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
- package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
- package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
- package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
- package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
- package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
- package/dist/lite/.claude/skills/state/SKILL.md +204 -0
- package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
- package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
- package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
- package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
- package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
- package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
- package/dist/lite/README.md +5 -1
- package/dist/lite/gitignore +6 -0
- package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
- package/dist/lite/lib/clone-failure-classifier.sh +59 -0
- package/dist/lite/lib/fixture-builder.sh +190 -0
- package/dist/lite/lib/fresh-install-check.sh +49 -0
- package/dist/lite/lib/hook-heartbeat.sh +0 -1
- package/dist/lite/lib/mechanism-fidelity.sh +569 -0
- package/dist/lite/lib/npm-version-check.sh +112 -0
- package/dist/lite/lib/preview-state.sh +229 -0
- package/dist/lite/lib/session-rescue.sh +0 -1
- package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
- package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
- package/dist/lite/standards/architect-review-discipline.md +222 -0
- package/dist/lite/standards/bassclef-platform.md +249 -0
- package/dist/lite/standards/bassclef-test-repos.md +97 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
- package/dist/lite/standards/cold-adopter-harness.md +205 -0
- package/dist/lite/standards/data-management.md +135 -0
- package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
- package/dist/lite/standards/lite-manifest.json +1456 -14
- package/dist/lite/standards/mechanism-fidelity.md +205 -0
- package/dist/lite/standards/operator-private-discipline.md +200 -0
- package/dist/lite/standards/operator-to-public-flow.md +195 -0
- package/dist/lite/standards/prototype-gallery.md +251 -0
- package/dist/lite/standards/pseudonym-key-schema.md +109 -0
- package/dist/lite/standards/sibling-validation-checklist.md +181 -0
- package/dist/lite/standards/slack-notifications.md +161 -0
- package/dist/lite/standards/synthetic-user-testing.md +173 -0
- package/dist/lite/standards/vendor-bindings.md +165 -0
- package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
- package/dist/lite/standards/vpc-fit-validation.md +167 -0
- package/dist/lite/templates/gallery-yml.md +113 -0
- package/dist/lite/templates/product-canvas.md +169 -0
- package/dist/lite/templates/value-prop-canvas.md +180 -0
- package/package.json +12 -5
- /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -313,14 +313,21 @@ function substrateConfigMdTemplate(pkgVersion) {
|
|
|
313
313
|
].join("\n");
|
|
314
314
|
}
|
|
315
315
|
const MANIFEST_SCHEMA_VERSION = "0.1.0";
|
|
316
|
-
const MANIFEST_SHAPE_VERSION =
|
|
316
|
+
const MANIFEST_SHAPE_VERSION = 3;
|
|
317
|
+
const GENERATED_BY = "@thebassclef/lite";
|
|
317
318
|
function manifestTemplate(input) {
|
|
318
319
|
const value = {
|
|
319
320
|
schema_version: MANIFEST_SHAPE_VERSION,
|
|
321
|
+
// Top-level `version` — read by the v0.45.0 npm-version-drift hook
|
|
322
|
+
// (fragment 91 under session-reflection) via lib/npm-version-check.sh
|
|
323
|
+
// `jq -r '.version // ""' "$manifest"`. Duplicates
|
|
324
|
+
// $bassclef.generated_by_version on purpose — the hook does not walk
|
|
325
|
+
// into $bassclef. Per bassclef-upstream#1749.
|
|
326
|
+
version: input.pkgVersion,
|
|
320
327
|
$bassclef: {
|
|
321
328
|
template: "init.manifest.json",
|
|
322
329
|
manifest_schema_version: MANIFEST_SCHEMA_VERSION,
|
|
323
|
-
generated_by:
|
|
330
|
+
generated_by: GENERATED_BY,
|
|
324
331
|
generated_by_version: input.pkgVersion
|
|
325
332
|
},
|
|
326
333
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -388,7 +395,7 @@ function readManifest(targetDir) {
|
|
|
388
395
|
if (compareSchemaVersion(schema, MANIFEST_SCHEMA_VERSION) > 0) {
|
|
389
396
|
throw new ManifestReadError(
|
|
390
397
|
"SchemaTooNew",
|
|
391
|
-
`manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/
|
|
398
|
+
`manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/lite.`
|
|
392
399
|
);
|
|
393
400
|
}
|
|
394
401
|
const obj = parsed;
|
|
@@ -541,7 +548,10 @@ function copySubstrate(targetDir, options = {}) {
|
|
|
541
548
|
hookCount: 0,
|
|
542
549
|
wiringVersion: manifest.version
|
|
543
550
|
};
|
|
544
|
-
if (options.dryRun)
|
|
551
|
+
if (options.dryRun) {
|
|
552
|
+
result.wouldCopy = [];
|
|
553
|
+
result.wouldCopyEntries = [];
|
|
554
|
+
}
|
|
545
555
|
const files = walkDistTree(bundleRoot);
|
|
546
556
|
const groups = groupByTopDirectory(files);
|
|
547
557
|
const bundleHookRelPaths = new Set(files.filter(isHookFile));
|
|
@@ -697,6 +707,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
|
|
|
697
707
|
const outputContent = options.transform ? options.transform(adopterRelPath, content) : content;
|
|
698
708
|
if (options.dryRun) {
|
|
699
709
|
result.wouldCopy?.push(adopterRelPath);
|
|
710
|
+
result.wouldCopyEntries?.push({
|
|
711
|
+
path: adopterRelPath,
|
|
712
|
+
scope,
|
|
713
|
+
content_hash_sha256: hashContent(outputContent)
|
|
714
|
+
});
|
|
700
715
|
return "wouldCopy";
|
|
701
716
|
}
|
|
702
717
|
try {
|
|
@@ -706,7 +721,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
|
|
|
706
721
|
setExecutable(targetPath);
|
|
707
722
|
}
|
|
708
723
|
result.copied.push(adopterRelPath);
|
|
709
|
-
result.copiedEntries.push({
|
|
724
|
+
result.copiedEntries.push({
|
|
725
|
+
path: adopterRelPath,
|
|
726
|
+
scope,
|
|
727
|
+
content_hash_sha256: hashContent(outputContent)
|
|
728
|
+
});
|
|
710
729
|
return "copied";
|
|
711
730
|
} catch (e) {
|
|
712
731
|
if (e instanceof WriteError) {
|
|
@@ -783,7 +802,92 @@ function dirExists(p) {
|
|
|
783
802
|
return false;
|
|
784
803
|
}
|
|
785
804
|
}
|
|
805
|
+
const FAMILIES = [
|
|
806
|
+
"skills",
|
|
807
|
+
"rules",
|
|
808
|
+
"agents",
|
|
809
|
+
"luminaries",
|
|
810
|
+
"hooks",
|
|
811
|
+
"libs",
|
|
812
|
+
"adrs",
|
|
813
|
+
"standards",
|
|
814
|
+
"templates",
|
|
815
|
+
"presence-templates",
|
|
816
|
+
"scripts",
|
|
817
|
+
"root-docs",
|
|
818
|
+
"other"
|
|
819
|
+
];
|
|
820
|
+
const UNDER_CLAUDE = `${CLAUDE_TARGET_ROOT}/`;
|
|
821
|
+
const PREFIX_RULES = [
|
|
822
|
+
[`${UNDER_CLAUDE}skills/`, "skills"],
|
|
823
|
+
[`${UNDER_CLAUDE}rules/`, "rules"],
|
|
824
|
+
[`${UNDER_CLAUDE}agents/`, "agents"],
|
|
825
|
+
[`${UNDER_CLAUDE}luminaries/`, "luminaries"],
|
|
826
|
+
[`${UNDER_CLAUDE}hooks/`, "hooks"],
|
|
827
|
+
["lib/", "libs"],
|
|
828
|
+
["architecture/decisions/", "adrs"],
|
|
829
|
+
["standards/", "standards"],
|
|
830
|
+
["templates/", "templates"],
|
|
831
|
+
["presence/install/", "presence-templates"],
|
|
832
|
+
["scripts/", "scripts"]
|
|
833
|
+
];
|
|
834
|
+
function classifyEntry(path) {
|
|
835
|
+
for (const [prefix, family] of PREFIX_RULES) {
|
|
836
|
+
if (path.startsWith(prefix)) return family;
|
|
837
|
+
}
|
|
838
|
+
if (!path.includes("/") && /^[A-Z]/.test(path)) return "root-docs";
|
|
839
|
+
return "other";
|
|
840
|
+
}
|
|
841
|
+
function emptyCatalogCounts() {
|
|
842
|
+
const out = {};
|
|
843
|
+
for (const f of FAMILIES) out[f] = 0;
|
|
844
|
+
return out;
|
|
845
|
+
}
|
|
846
|
+
function buildInitReport(input) {
|
|
847
|
+
const catalog = emptyCatalogCounts();
|
|
848
|
+
let project = 0;
|
|
849
|
+
let user = 0;
|
|
850
|
+
for (const e of input.entries) {
|
|
851
|
+
catalog[classifyEntry(e.path)] += 1;
|
|
852
|
+
if (e.scope === "user") user += 1;
|
|
853
|
+
else project += 1;
|
|
854
|
+
}
|
|
855
|
+
const refused = input.refused.length;
|
|
856
|
+
const errored = input.errored.length;
|
|
857
|
+
const written = input.configs.length + input.entries.length;
|
|
858
|
+
return {
|
|
859
|
+
schema_version: 3,
|
|
860
|
+
tier: input.tier,
|
|
861
|
+
totals: {
|
|
862
|
+
files: written + refused + errored,
|
|
863
|
+
written,
|
|
864
|
+
project,
|
|
865
|
+
user,
|
|
866
|
+
configs: input.configs.length
|
|
867
|
+
},
|
|
868
|
+
catalog,
|
|
869
|
+
hooks: {
|
|
870
|
+
declared: input.hookCount,
|
|
871
|
+
copied: input.declaredHooksCopied,
|
|
872
|
+
files: catalog.hooks
|
|
873
|
+
},
|
|
874
|
+
refused,
|
|
875
|
+
errored,
|
|
876
|
+
failed: refused + errored
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function renderJsonReport(report, write) {
|
|
880
|
+
write(JSON.stringify(report) + "\n");
|
|
881
|
+
}
|
|
786
882
|
const RESOLVED_TIER = "lite";
|
|
883
|
+
const SAY_HUMAN = (s) => {
|
|
884
|
+
process.stdout.write(s);
|
|
885
|
+
};
|
|
886
|
+
const SAY_QUIET = () => {
|
|
887
|
+
};
|
|
888
|
+
function makeSay(json) {
|
|
889
|
+
return json ? SAY_QUIET : SAY_HUMAN;
|
|
890
|
+
}
|
|
787
891
|
const PLACEHOLDER_FILES = /* @__PURE__ */ new Set([
|
|
788
892
|
"CLAUDE.md",
|
|
789
893
|
"docs/whereami.md",
|
|
@@ -825,7 +929,7 @@ function runInit(argv) {
|
|
|
825
929
|
}
|
|
826
930
|
throw e;
|
|
827
931
|
}
|
|
828
|
-
const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes);
|
|
932
|
+
const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes, args.json);
|
|
829
933
|
if (advisoryOutcome === "refused") return 1;
|
|
830
934
|
const upgradeApproved = advisoryOutcome === "upgrade-approved";
|
|
831
935
|
if (!args.force && !args.dryRun && !upgradeApproved) {
|
|
@@ -847,26 +951,55 @@ function runInit(argv) {
|
|
|
847
951
|
templateVersion: SUBSTRATE_CONFIG_TEMPLATE_VERSION
|
|
848
952
|
}
|
|
849
953
|
];
|
|
954
|
+
const say = makeSay(args.json);
|
|
850
955
|
if (args.dryRun) {
|
|
851
|
-
runDryRun$1(plans);
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
956
|
+
runDryRun$1(plans, say);
|
|
957
|
+
const outcome = dispatchSubstrateCopy(
|
|
958
|
+
targetDir,
|
|
959
|
+
args.force || upgradeApproved,
|
|
960
|
+
args.verbose,
|
|
961
|
+
true,
|
|
962
|
+
args.allowRoot,
|
|
963
|
+
say
|
|
964
|
+
);
|
|
965
|
+
if (args.json) {
|
|
966
|
+
const report = buildInitReport({
|
|
967
|
+
entries: outcome.result?.wouldCopyEntries ?? [],
|
|
968
|
+
configs: plans.map((pl) => ({ path: pl.relativePath })),
|
|
969
|
+
refused: outcome.result?.refused ?? [],
|
|
970
|
+
// A dry run still reads every source file, so it can still fail
|
|
971
|
+
// to read one. Hard-coding this empty hid those failures from
|
|
972
|
+
// the JSON (RFC-0005 A-3).
|
|
973
|
+
errored: outcome.result?.errored ?? [],
|
|
974
|
+
hookCount: outcome.result?.hookCount ?? 0,
|
|
975
|
+
declaredHooksCopied: 0,
|
|
976
|
+
tier: RESOLVED_TIER
|
|
977
|
+
});
|
|
978
|
+
renderJsonReport(report, (t) => {
|
|
979
|
+
process.stdout.write(t);
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
return outcome.code;
|
|
983
|
+
}
|
|
984
|
+
return runReal$1(plans, args.force || upgradeApproved, args.verbose, targetDir, args.allowRoot, args.json, say);
|
|
985
|
+
}
|
|
986
|
+
function maybeEmitUpgradeAdvisory(targetDir, yes, json) {
|
|
987
|
+
const prompt = json ? (t) => {
|
|
988
|
+
process.stderr.write(t);
|
|
989
|
+
} : SAY_HUMAN;
|
|
857
990
|
const manifestPath = node_path.join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
858
991
|
if (!node_fs.existsSync(manifestPath)) return "ok";
|
|
859
992
|
const version = readManifestShapeVersion(targetDir);
|
|
860
993
|
if (version !== null && version >= 2) return "ok";
|
|
861
|
-
|
|
994
|
+
prompt(
|
|
862
995
|
`bassclef init: cli 1.0.1 introduces user-scope hook installation at ~/${HOOKS_SUBPATH}. cli 1.0.0 did not write there.
|
|
863
996
|
`
|
|
864
997
|
);
|
|
865
998
|
if (yes) return "upgrade-approved";
|
|
866
|
-
|
|
999
|
+
prompt("bassclef init: continue? (y/N) ");
|
|
867
1000
|
const answer = readOneLineFromStdin();
|
|
868
1001
|
if (answer === "" || answer === "y" || answer === "Y") return "upgrade-approved";
|
|
869
|
-
|
|
1002
|
+
prompt("bassclef init: aborted by adopter.\n");
|
|
870
1003
|
return "refused";
|
|
871
1004
|
}
|
|
872
1005
|
function readOneLineFromStdin() {
|
|
@@ -878,7 +1011,7 @@ function readOneLineFromStdin() {
|
|
|
878
1011
|
return "";
|
|
879
1012
|
}
|
|
880
1013
|
}
|
|
881
|
-
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot,
|
|
1014
|
+
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say) {
|
|
882
1015
|
const substitute = makePlaceholderTransform(targetDir);
|
|
883
1016
|
let result;
|
|
884
1017
|
try {
|
|
@@ -892,9 +1025,9 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
892
1025
|
if (e instanceof CopyFailure) {
|
|
893
1026
|
process.stderr.write(`bassclef init: ${e.message}
|
|
894
1027
|
`);
|
|
895
|
-
if (e.kind === "ManifestMissing") return 4;
|
|
896
|
-
if (e.kind === "SchemaIncompatible") return 5;
|
|
897
|
-
return 2;
|
|
1028
|
+
if (e.kind === "ManifestMissing") return { code: 4 };
|
|
1029
|
+
if (e.kind === "SchemaIncompatible") return { code: 5 };
|
|
1030
|
+
return { code: 2 };
|
|
898
1031
|
}
|
|
899
1032
|
throw e;
|
|
900
1033
|
}
|
|
@@ -902,23 +1035,23 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
902
1035
|
const wouldCopy = result.wouldCopy ?? [];
|
|
903
1036
|
for (const relativePath of wouldCopy) {
|
|
904
1037
|
const targetPath = node_path.join(targetDir, relativePath);
|
|
905
|
-
|
|
1038
|
+
say(` ${"would create".padEnd(14)} ${targetPath}
|
|
906
1039
|
`);
|
|
907
1040
|
}
|
|
908
1041
|
if (wouldCopy.length > 0) {
|
|
909
|
-
|
|
1042
|
+
say(
|
|
910
1043
|
`bassclef init: ${wouldCopy.length} substrate files would be copied.
|
|
911
1044
|
`
|
|
912
1045
|
);
|
|
913
1046
|
}
|
|
914
|
-
|
|
1047
|
+
say(
|
|
915
1048
|
`bassclef init: ${result.hookCount} hooks armed (${RESOLVED_TIER} tier).
|
|
916
1049
|
`
|
|
917
1050
|
);
|
|
918
|
-
return 0;
|
|
1051
|
+
return { code: 0, result };
|
|
919
1052
|
}
|
|
920
1053
|
if (result.copied.length === 0 && result.refused.length === 0 && result.errored.length === 0) {
|
|
921
|
-
return 0;
|
|
1054
|
+
return { code: 0, result };
|
|
922
1055
|
}
|
|
923
1056
|
const groupCounts = /* @__PURE__ */ new Map();
|
|
924
1057
|
for (const path of result.copied) {
|
|
@@ -927,17 +1060,17 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
927
1060
|
groupCounts.set(top, (groupCounts.get(top) ?? 0) + 1);
|
|
928
1061
|
}
|
|
929
1062
|
for (const [directory, count] of groupCounts) {
|
|
930
|
-
|
|
1063
|
+
say(` ${directory}: ${count} files copied
|
|
931
1064
|
`);
|
|
932
1065
|
}
|
|
933
1066
|
const parts = [];
|
|
934
1067
|
if (result.copied.length > 0) parts.push(`${result.copied.length} substrate files copied`);
|
|
935
1068
|
if (result.refused.length > 0) parts.push(`${result.refused.length} refused`);
|
|
936
1069
|
if (result.errored.length > 0) parts.push(`${result.errored.length} error(s)`);
|
|
937
|
-
|
|
1070
|
+
say(`bassclef init: ${parts.join(", ")}.
|
|
938
1071
|
`);
|
|
939
1072
|
const grandTotal = 1 + result.copied.length;
|
|
940
|
-
|
|
1073
|
+
say(
|
|
941
1074
|
`bassclef init: ${grandTotal} files total (1 config + ${result.copied.length} substrate).
|
|
942
1075
|
`
|
|
943
1076
|
);
|
|
@@ -949,17 +1082,17 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
949
1082
|
);
|
|
950
1083
|
const copiedCount = copiedHookEntries.length;
|
|
951
1084
|
const declaredCount = result.hookCount;
|
|
952
|
-
const
|
|
1085
|
+
const erroredCount = result.errored.length;
|
|
953
1086
|
const userScope = copiedHookEntries.filter((e) => e.scope === "user").length;
|
|
954
1087
|
const projectScope = copiedHookEntries.filter((e) => e.scope === "project").length;
|
|
955
1088
|
const scopeSuffix = userScope + projectScope > 0 ? ` ${projectScope} in <repo>/${HOOKS_SUBPATH.replace(/\/$/, "")}, ${userScope} in ~/${HOOKS_SUBPATH.replace(/\/$/, "")}.` : "";
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${
|
|
1089
|
+
if (erroredCount > 0) {
|
|
1090
|
+
say(
|
|
1091
|
+
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${erroredCount} failed — see errors above. Rerun bassclef init to retry.
|
|
959
1092
|
`
|
|
960
1093
|
);
|
|
961
1094
|
} else {
|
|
962
|
-
|
|
1095
|
+
say(
|
|
963
1096
|
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix}
|
|
964
1097
|
`
|
|
965
1098
|
);
|
|
@@ -1004,7 +1137,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
1004
1137
|
catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
|
|
1005
1138
|
].filter((s) => s !== null);
|
|
1006
1139
|
if (claudeCounts.length > 0) {
|
|
1007
|
-
|
|
1140
|
+
say(
|
|
1008
1141
|
`bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
|
|
1009
1142
|
`
|
|
1010
1143
|
);
|
|
@@ -1019,24 +1152,16 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
1019
1152
|
catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
|
|
1020
1153
|
].filter((s) => s !== null);
|
|
1021
1154
|
if (otherCounts.length > 0) {
|
|
1022
|
-
|
|
1155
|
+
say(
|
|
1023
1156
|
`bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
|
|
1024
1157
|
`
|
|
1025
1158
|
);
|
|
1026
1159
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1160
|
+
if (result.refused.length > 0) {
|
|
1161
|
+
say(
|
|
1162
|
+
`bassclef init: ${result.refused.length} files refused (path collision). Use --force to overwrite existing files.
|
|
1029
1163
|
`
|
|
1030
|
-
|
|
1031
|
-
if (json) {
|
|
1032
|
-
const report = {
|
|
1033
|
-
copied: copiedCount,
|
|
1034
|
-
declared: declaredCount,
|
|
1035
|
-
failed: failedCount,
|
|
1036
|
-
scope_counts: { user: userScope, project: projectScope },
|
|
1037
|
-
tier: RESOLVED_TIER
|
|
1038
|
-
};
|
|
1039
|
-
process.stderr.write(JSON.stringify(report) + "\n");
|
|
1164
|
+
);
|
|
1040
1165
|
}
|
|
1041
1166
|
if (verbose && result.erroredMessages) {
|
|
1042
1167
|
for (const msg of result.erroredMessages) {
|
|
@@ -1044,7 +1169,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
1044
1169
|
`);
|
|
1045
1170
|
}
|
|
1046
1171
|
}
|
|
1047
|
-
return result.errored.length > 0 ? 2 : 0;
|
|
1172
|
+
return { code: result.errored.length > 0 ? 2 : 0, result };
|
|
1048
1173
|
}
|
|
1049
1174
|
function makePlaceholderTransform(targetDir) {
|
|
1050
1175
|
const repoName = node_path.basename(targetDir);
|
|
@@ -1054,7 +1179,7 @@ function makePlaceholderTransform(targetDir) {
|
|
|
1054
1179
|
return content.replace(/\[REPO_NAME\]/g, repoName).replace(/\[ISO_TIMESTAMP\]/g, timestamp).replace(/\[TIER\]/g, RESOLVED_TIER).replace(/\[Repo name\]/g, repoName).replace(/<tier>/g, RESOLVED_TIER);
|
|
1055
1180
|
};
|
|
1056
1181
|
}
|
|
1057
|
-
function runDryRun$1(plans) {
|
|
1182
|
+
function runDryRun$1(plans, say) {
|
|
1058
1183
|
for (const p of plans) {
|
|
1059
1184
|
let planned;
|
|
1060
1185
|
let extra = "";
|
|
@@ -1074,12 +1199,12 @@ function runDryRun$1(plans) {
|
|
|
1074
1199
|
planned = "would skip";
|
|
1075
1200
|
}
|
|
1076
1201
|
}
|
|
1077
|
-
|
|
1202
|
+
say(` ${planned.padEnd(14)} ${p.fullPath}${extra}
|
|
1078
1203
|
`);
|
|
1079
1204
|
}
|
|
1080
1205
|
return 0;
|
|
1081
1206
|
}
|
|
1082
|
-
function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
1207
|
+
function runReal$1(plans, force, verbose, targetDir, allowRoot, json, say) {
|
|
1083
1208
|
const results = [];
|
|
1084
1209
|
let anyRefused = false;
|
|
1085
1210
|
let anyError = false;
|
|
@@ -1127,7 +1252,7 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
|
1127
1252
|
if (r.outcome === "error") label = `error (${r.message ?? "unknown"})`;
|
|
1128
1253
|
else if (r.outcome === "refused") label = "refused";
|
|
1129
1254
|
else label = r.outcome;
|
|
1130
|
-
|
|
1255
|
+
say(` ${label.padEnd(10)} ${r.plan.fullPath}
|
|
1131
1256
|
`);
|
|
1132
1257
|
}
|
|
1133
1258
|
}
|
|
@@ -1149,38 +1274,59 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
|
1149
1274
|
return 2;
|
|
1150
1275
|
}
|
|
1151
1276
|
if (anyRefused && created > 0) {
|
|
1152
|
-
|
|
1277
|
+
say(
|
|
1153
1278
|
`bassclef init: ${created} config files created, ${unchanged} unchanged. Pass --force to overwrite.
|
|
1154
1279
|
`
|
|
1155
1280
|
);
|
|
1156
1281
|
} else if (created === 0 && unchanged === plans.length) {
|
|
1157
|
-
|
|
1282
|
+
say("bassclef init: already initialized. No changes.\n");
|
|
1158
1283
|
} else {
|
|
1159
|
-
|
|
1284
|
+
say(`bassclef init: ${created} config files created, ${unchanged} unchanged.
|
|
1160
1285
|
`);
|
|
1161
1286
|
}
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1287
|
+
const walker = dispatchSubstrateCopy(targetDir, force, verbose, false, allowRoot, say);
|
|
1288
|
+
const declaredLeaves = readDeclaredCommandLeaves(
|
|
1289
|
+
node_path.join(targetDir, ".claude", "settings.json")
|
|
1290
|
+
);
|
|
1291
|
+
const declaredHooksCopied = (walker.result?.copiedEntries ?? []).filter(
|
|
1292
|
+
(e) => e.path.startsWith(HOOKS_SUBPATH) && e.path.endsWith(".sh") && declaredLeaves.has(node_path.basename(e.path))
|
|
1293
|
+
).length;
|
|
1294
|
+
const report = buildInitReport({
|
|
1295
|
+
entries: walker.result?.copiedEntries ?? [],
|
|
1296
|
+
configs: results.map((r) => ({ path: r.plan.relativePath })),
|
|
1297
|
+
refused: walker.result?.refused ?? [],
|
|
1298
|
+
errored: walker.result?.errored ?? [],
|
|
1299
|
+
hookCount: walker.result?.hookCount ?? 0,
|
|
1300
|
+
declaredHooksCopied,
|
|
1301
|
+
tier: RESOLVED_TIER
|
|
1302
|
+
});
|
|
1303
|
+
writeManifest(targetDir, results, walker.result);
|
|
1304
|
+
if (walker.code === 0) {
|
|
1305
|
+
say(
|
|
1166
1306
|
`bassclef init: your substrate lives under .claude/. Add .claude/ to .gitignore if you have not.
|
|
1167
1307
|
`
|
|
1168
1308
|
);
|
|
1169
1309
|
}
|
|
1170
|
-
|
|
1310
|
+
if (json) {
|
|
1311
|
+
renderJsonReport(report, (t) => {
|
|
1312
|
+
process.stdout.write(t);
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
return walker.code;
|
|
1171
1316
|
}
|
|
1172
1317
|
function shouldRefuseRoot(currentUid, allowRoot) {
|
|
1173
1318
|
if (currentUid === void 0) return false;
|
|
1174
1319
|
if (currentUid !== 0) return false;
|
|
1175
1320
|
return !allowRoot;
|
|
1176
1321
|
}
|
|
1177
|
-
function writeManifest(targetDir, results) {
|
|
1322
|
+
function writeManifest(targetDir, results, walkerResult) {
|
|
1178
1323
|
const entries = results.map((r) => {
|
|
1179
1324
|
const entry = {
|
|
1180
1325
|
path: r.plan.relativePath,
|
|
1181
1326
|
template: r.plan.templateName,
|
|
1182
1327
|
template_version: r.plan.templateVersion,
|
|
1183
|
-
outcome: r.outcome
|
|
1328
|
+
outcome: r.outcome,
|
|
1329
|
+
source: "config-composer"
|
|
1184
1330
|
};
|
|
1185
1331
|
if (r.outcome === "created") {
|
|
1186
1332
|
entry.content_hash_sha256 = hashContent(r.plan.content);
|
|
@@ -1188,6 +1334,38 @@ function writeManifest(targetDir, results) {
|
|
|
1188
1334
|
}
|
|
1189
1335
|
return entry;
|
|
1190
1336
|
});
|
|
1337
|
+
if (walkerResult) {
|
|
1338
|
+
for (const copied of walkerResult.copiedEntries) {
|
|
1339
|
+
entries.push({
|
|
1340
|
+
path: copied.path,
|
|
1341
|
+
template: `bundle:${copied.path}`,
|
|
1342
|
+
template_version: index.version,
|
|
1343
|
+
outcome: "created",
|
|
1344
|
+
source: "bundle",
|
|
1345
|
+
scope: copied.scope,
|
|
1346
|
+
...copied.content_hash_sha256 ? { content_hash_sha256: copied.content_hash_sha256 } : {},
|
|
1347
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
for (const path of walkerResult.refused) {
|
|
1351
|
+
entries.push({
|
|
1352
|
+
path,
|
|
1353
|
+
template: `bundle:${path}`,
|
|
1354
|
+
template_version: index.version,
|
|
1355
|
+
outcome: "refused",
|
|
1356
|
+
source: "bundle"
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
for (const path of walkerResult.errored) {
|
|
1360
|
+
entries.push({
|
|
1361
|
+
path,
|
|
1362
|
+
template: `bundle:${path}`,
|
|
1363
|
+
template_version: index.version,
|
|
1364
|
+
outcome: "error",
|
|
1365
|
+
source: "bundle"
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1191
1369
|
const manifestDir = node_path.join(targetDir, ".bassclef");
|
|
1192
1370
|
const manifestPath = node_path.join(manifestDir, "init.manifest.json");
|
|
1193
1371
|
const content = manifestTemplate({
|
|
@@ -1198,7 +1376,12 @@ function writeManifest(targetDir, results) {
|
|
|
1198
1376
|
try {
|
|
1199
1377
|
mkdirSafely(manifestDir);
|
|
1200
1378
|
writeSafely(manifestPath, content, { force: true });
|
|
1201
|
-
} catch {
|
|
1379
|
+
} catch (e) {
|
|
1380
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
1381
|
+
process.stderr.write(
|
|
1382
|
+
`bassclef init: could not write ${manifestPath} (${reason}). The files were written. Run \`bassclef init --force\` to rebuild the record.
|
|
1383
|
+
`
|
|
1384
|
+
);
|
|
1202
1385
|
}
|
|
1203
1386
|
}
|
|
1204
1387
|
function usage$2() {
|
|
@@ -1649,7 +1832,10 @@ function emitL2Output(decisions, verbose) {
|
|
|
1649
1832
|
}
|
|
1650
1833
|
}
|
|
1651
1834
|
function updateManifestEntry(manifest, path, patch) {
|
|
1652
|
-
const
|
|
1835
|
+
const wantScope = patch.scope;
|
|
1836
|
+
const idx = manifest.files.findIndex(
|
|
1837
|
+
(f) => f.path === path && (wantScope === void 0 || f.scope === wantScope)
|
|
1838
|
+
);
|
|
1653
1839
|
if (idx < 0) {
|
|
1654
1840
|
manifest.files.push({
|
|
1655
1841
|
path,
|
|
@@ -1833,6 +2019,12 @@ Proceed?`,
|
|
|
1833
2019
|
const errored = copyResult.errored;
|
|
1834
2020
|
const newManifest = {
|
|
1835
2021
|
schema_version: MANIFEST_SHAPE_VERSION,
|
|
2022
|
+
// Top-level `.version` — read by the v0.45.0 npm-version-drift hook
|
|
2023
|
+
// (fragment 91 under session-reflection) via lib/npm-version-check.sh
|
|
2024
|
+
// `jq -r '.version // ""' "$manifest"`. Duplicates
|
|
2025
|
+
// $bassclef.generated_by_version on purpose — the hook does not walk
|
|
2026
|
+
// into $bassclef. Per bassclef-upstream#1749; bassclef-cli#129.
|
|
2027
|
+
version: index.version,
|
|
1836
2028
|
$bassclef: {
|
|
1837
2029
|
template: "init.manifest.json",
|
|
1838
2030
|
manifest_schema_version: "0.1.0",
|
|
@@ -2000,6 +2192,9 @@ Run \`bassclef migrate --help\` for migrate options + the upgrade paths.
|
|
|
2000
2192
|
Version: ${index.version}
|
|
2001
2193
|
Docs: https://github.com/sunj-labs/bassclef-cli
|
|
2002
2194
|
`;
|
|
2195
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
2196
|
+
stream._handle?.setBlocking?.(true);
|
|
2197
|
+
}
|
|
2003
2198
|
async function main(argv) {
|
|
2004
2199
|
const first = argv[0];
|
|
2005
2200
|
if (first === void 0 || first === "--help" || first === "-h" || first === "help") {
|