@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.js
CHANGED
|
@@ -290,14 +290,21 @@ function substrateConfigMdTemplate(pkgVersion) {
|
|
|
290
290
|
].join("\n");
|
|
291
291
|
}
|
|
292
292
|
const MANIFEST_SCHEMA_VERSION = "0.1.0";
|
|
293
|
-
const MANIFEST_SHAPE_VERSION =
|
|
293
|
+
const MANIFEST_SHAPE_VERSION = 3;
|
|
294
|
+
const GENERATED_BY = "@thebassclef/lite";
|
|
294
295
|
function manifestTemplate(input) {
|
|
295
296
|
const value = {
|
|
296
297
|
schema_version: MANIFEST_SHAPE_VERSION,
|
|
298
|
+
// Top-level `version` — read by the v0.45.0 npm-version-drift hook
|
|
299
|
+
// (fragment 91 under session-reflection) via lib/npm-version-check.sh
|
|
300
|
+
// `jq -r '.version // ""' "$manifest"`. Duplicates
|
|
301
|
+
// $bassclef.generated_by_version on purpose — the hook does not walk
|
|
302
|
+
// into $bassclef. Per bassclef-upstream#1749.
|
|
303
|
+
version: input.pkgVersion,
|
|
297
304
|
$bassclef: {
|
|
298
305
|
template: "init.manifest.json",
|
|
299
306
|
manifest_schema_version: MANIFEST_SCHEMA_VERSION,
|
|
300
|
-
generated_by:
|
|
307
|
+
generated_by: GENERATED_BY,
|
|
301
308
|
generated_by_version: input.pkgVersion
|
|
302
309
|
},
|
|
303
310
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -365,7 +372,7 @@ function readManifest(targetDir) {
|
|
|
365
372
|
if (compareSchemaVersion(schema, MANIFEST_SCHEMA_VERSION) > 0) {
|
|
366
373
|
throw new ManifestReadError(
|
|
367
374
|
"SchemaTooNew",
|
|
368
|
-
`manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/
|
|
375
|
+
`manifest schema version ${schema} is newer than this package understands (${MANIFEST_SCHEMA_VERSION}). Upgrade @thebassclef/lite.`
|
|
369
376
|
);
|
|
370
377
|
}
|
|
371
378
|
const obj = parsed;
|
|
@@ -518,7 +525,10 @@ function copySubstrate(targetDir, options = {}) {
|
|
|
518
525
|
hookCount: 0,
|
|
519
526
|
wiringVersion: manifest.version
|
|
520
527
|
};
|
|
521
|
-
if (options.dryRun)
|
|
528
|
+
if (options.dryRun) {
|
|
529
|
+
result.wouldCopy = [];
|
|
530
|
+
result.wouldCopyEntries = [];
|
|
531
|
+
}
|
|
522
532
|
const files = walkDistTree(bundleRoot);
|
|
523
533
|
const groups = groupByTopDirectory(files);
|
|
524
534
|
const bundleHookRelPaths = new Set(files.filter(isHookFile));
|
|
@@ -674,6 +684,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
|
|
|
674
684
|
const outputContent = options.transform ? options.transform(adopterRelPath, content) : content;
|
|
675
685
|
if (options.dryRun) {
|
|
676
686
|
result.wouldCopy?.push(adopterRelPath);
|
|
687
|
+
result.wouldCopyEntries?.push({
|
|
688
|
+
path: adopterRelPath,
|
|
689
|
+
scope,
|
|
690
|
+
content_hash_sha256: hashContent(outputContent)
|
|
691
|
+
});
|
|
677
692
|
return "wouldCopy";
|
|
678
693
|
}
|
|
679
694
|
try {
|
|
@@ -683,7 +698,11 @@ function copyOne(relPath, bundleRoot, options, result, scopeDecision) {
|
|
|
683
698
|
setExecutable(targetPath);
|
|
684
699
|
}
|
|
685
700
|
result.copied.push(adopterRelPath);
|
|
686
|
-
result.copiedEntries.push({
|
|
701
|
+
result.copiedEntries.push({
|
|
702
|
+
path: adopterRelPath,
|
|
703
|
+
scope,
|
|
704
|
+
content_hash_sha256: hashContent(outputContent)
|
|
705
|
+
});
|
|
687
706
|
return "copied";
|
|
688
707
|
} catch (e) {
|
|
689
708
|
if (e instanceof WriteError) {
|
|
@@ -760,7 +779,92 @@ function dirExists(p) {
|
|
|
760
779
|
return false;
|
|
761
780
|
}
|
|
762
781
|
}
|
|
782
|
+
const FAMILIES = [
|
|
783
|
+
"skills",
|
|
784
|
+
"rules",
|
|
785
|
+
"agents",
|
|
786
|
+
"luminaries",
|
|
787
|
+
"hooks",
|
|
788
|
+
"libs",
|
|
789
|
+
"adrs",
|
|
790
|
+
"standards",
|
|
791
|
+
"templates",
|
|
792
|
+
"presence-templates",
|
|
793
|
+
"scripts",
|
|
794
|
+
"root-docs",
|
|
795
|
+
"other"
|
|
796
|
+
];
|
|
797
|
+
const UNDER_CLAUDE = `${CLAUDE_TARGET_ROOT}/`;
|
|
798
|
+
const PREFIX_RULES = [
|
|
799
|
+
[`${UNDER_CLAUDE}skills/`, "skills"],
|
|
800
|
+
[`${UNDER_CLAUDE}rules/`, "rules"],
|
|
801
|
+
[`${UNDER_CLAUDE}agents/`, "agents"],
|
|
802
|
+
[`${UNDER_CLAUDE}luminaries/`, "luminaries"],
|
|
803
|
+
[`${UNDER_CLAUDE}hooks/`, "hooks"],
|
|
804
|
+
["lib/", "libs"],
|
|
805
|
+
["architecture/decisions/", "adrs"],
|
|
806
|
+
["standards/", "standards"],
|
|
807
|
+
["templates/", "templates"],
|
|
808
|
+
["presence/install/", "presence-templates"],
|
|
809
|
+
["scripts/", "scripts"]
|
|
810
|
+
];
|
|
811
|
+
function classifyEntry(path) {
|
|
812
|
+
for (const [prefix, family] of PREFIX_RULES) {
|
|
813
|
+
if (path.startsWith(prefix)) return family;
|
|
814
|
+
}
|
|
815
|
+
if (!path.includes("/") && /^[A-Z]/.test(path)) return "root-docs";
|
|
816
|
+
return "other";
|
|
817
|
+
}
|
|
818
|
+
function emptyCatalogCounts() {
|
|
819
|
+
const out = {};
|
|
820
|
+
for (const f of FAMILIES) out[f] = 0;
|
|
821
|
+
return out;
|
|
822
|
+
}
|
|
823
|
+
function buildInitReport(input) {
|
|
824
|
+
const catalog = emptyCatalogCounts();
|
|
825
|
+
let project = 0;
|
|
826
|
+
let user = 0;
|
|
827
|
+
for (const e of input.entries) {
|
|
828
|
+
catalog[classifyEntry(e.path)] += 1;
|
|
829
|
+
if (e.scope === "user") user += 1;
|
|
830
|
+
else project += 1;
|
|
831
|
+
}
|
|
832
|
+
const refused = input.refused.length;
|
|
833
|
+
const errored = input.errored.length;
|
|
834
|
+
const written = input.configs.length + input.entries.length;
|
|
835
|
+
return {
|
|
836
|
+
schema_version: 3,
|
|
837
|
+
tier: input.tier,
|
|
838
|
+
totals: {
|
|
839
|
+
files: written + refused + errored,
|
|
840
|
+
written,
|
|
841
|
+
project,
|
|
842
|
+
user,
|
|
843
|
+
configs: input.configs.length
|
|
844
|
+
},
|
|
845
|
+
catalog,
|
|
846
|
+
hooks: {
|
|
847
|
+
declared: input.hookCount,
|
|
848
|
+
copied: input.declaredHooksCopied,
|
|
849
|
+
files: catalog.hooks
|
|
850
|
+
},
|
|
851
|
+
refused,
|
|
852
|
+
errored,
|
|
853
|
+
failed: refused + errored
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function renderJsonReport(report, write) {
|
|
857
|
+
write(JSON.stringify(report) + "\n");
|
|
858
|
+
}
|
|
763
859
|
const RESOLVED_TIER = "lite";
|
|
860
|
+
const SAY_HUMAN = (s) => {
|
|
861
|
+
process.stdout.write(s);
|
|
862
|
+
};
|
|
863
|
+
const SAY_QUIET = () => {
|
|
864
|
+
};
|
|
865
|
+
function makeSay(json) {
|
|
866
|
+
return json ? SAY_QUIET : SAY_HUMAN;
|
|
867
|
+
}
|
|
764
868
|
const PLACEHOLDER_FILES = /* @__PURE__ */ new Set([
|
|
765
869
|
"CLAUDE.md",
|
|
766
870
|
"docs/whereami.md",
|
|
@@ -802,7 +906,7 @@ function runInit(argv) {
|
|
|
802
906
|
}
|
|
803
907
|
throw e;
|
|
804
908
|
}
|
|
805
|
-
const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes);
|
|
909
|
+
const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes, args.json);
|
|
806
910
|
if (advisoryOutcome === "refused") return 1;
|
|
807
911
|
const upgradeApproved = advisoryOutcome === "upgrade-approved";
|
|
808
912
|
if (!args.force && !args.dryRun && !upgradeApproved) {
|
|
@@ -824,26 +928,55 @@ function runInit(argv) {
|
|
|
824
928
|
templateVersion: SUBSTRATE_CONFIG_TEMPLATE_VERSION
|
|
825
929
|
}
|
|
826
930
|
];
|
|
931
|
+
const say = makeSay(args.json);
|
|
827
932
|
if (args.dryRun) {
|
|
828
|
-
runDryRun$1(plans);
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
933
|
+
runDryRun$1(plans, say);
|
|
934
|
+
const outcome = dispatchSubstrateCopy(
|
|
935
|
+
targetDir,
|
|
936
|
+
args.force || upgradeApproved,
|
|
937
|
+
args.verbose,
|
|
938
|
+
true,
|
|
939
|
+
args.allowRoot,
|
|
940
|
+
say
|
|
941
|
+
);
|
|
942
|
+
if (args.json) {
|
|
943
|
+
const report = buildInitReport({
|
|
944
|
+
entries: outcome.result?.wouldCopyEntries ?? [],
|
|
945
|
+
configs: plans.map((pl) => ({ path: pl.relativePath })),
|
|
946
|
+
refused: outcome.result?.refused ?? [],
|
|
947
|
+
// A dry run still reads every source file, so it can still fail
|
|
948
|
+
// to read one. Hard-coding this empty hid those failures from
|
|
949
|
+
// the JSON (RFC-0005 A-3).
|
|
950
|
+
errored: outcome.result?.errored ?? [],
|
|
951
|
+
hookCount: outcome.result?.hookCount ?? 0,
|
|
952
|
+
declaredHooksCopied: 0,
|
|
953
|
+
tier: RESOLVED_TIER
|
|
954
|
+
});
|
|
955
|
+
renderJsonReport(report, (t) => {
|
|
956
|
+
process.stdout.write(t);
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
return outcome.code;
|
|
960
|
+
}
|
|
961
|
+
return runReal$1(plans, args.force || upgradeApproved, args.verbose, targetDir, args.allowRoot, args.json, say);
|
|
962
|
+
}
|
|
963
|
+
function maybeEmitUpgradeAdvisory(targetDir, yes, json) {
|
|
964
|
+
const prompt = json ? (t) => {
|
|
965
|
+
process.stderr.write(t);
|
|
966
|
+
} : SAY_HUMAN;
|
|
834
967
|
const manifestPath = join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
835
968
|
if (!existsSync(manifestPath)) return "ok";
|
|
836
969
|
const version2 = readManifestShapeVersion(targetDir);
|
|
837
970
|
if (version2 !== null && version2 >= 2) return "ok";
|
|
838
|
-
|
|
971
|
+
prompt(
|
|
839
972
|
`bassclef init: cli 1.0.1 introduces user-scope hook installation at ~/${HOOKS_SUBPATH}. cli 1.0.0 did not write there.
|
|
840
973
|
`
|
|
841
974
|
);
|
|
842
975
|
if (yes) return "upgrade-approved";
|
|
843
|
-
|
|
976
|
+
prompt("bassclef init: continue? (y/N) ");
|
|
844
977
|
const answer = readOneLineFromStdin();
|
|
845
978
|
if (answer === "" || answer === "y" || answer === "Y") return "upgrade-approved";
|
|
846
|
-
|
|
979
|
+
prompt("bassclef init: aborted by adopter.\n");
|
|
847
980
|
return "refused";
|
|
848
981
|
}
|
|
849
982
|
function readOneLineFromStdin() {
|
|
@@ -855,7 +988,7 @@ function readOneLineFromStdin() {
|
|
|
855
988
|
return "";
|
|
856
989
|
}
|
|
857
990
|
}
|
|
858
|
-
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot,
|
|
991
|
+
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, say) {
|
|
859
992
|
const substitute = makePlaceholderTransform(targetDir);
|
|
860
993
|
let result;
|
|
861
994
|
try {
|
|
@@ -869,9 +1002,9 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
869
1002
|
if (e instanceof CopyFailure) {
|
|
870
1003
|
process.stderr.write(`bassclef init: ${e.message}
|
|
871
1004
|
`);
|
|
872
|
-
if (e.kind === "ManifestMissing") return 4;
|
|
873
|
-
if (e.kind === "SchemaIncompatible") return 5;
|
|
874
|
-
return 2;
|
|
1005
|
+
if (e.kind === "ManifestMissing") return { code: 4 };
|
|
1006
|
+
if (e.kind === "SchemaIncompatible") return { code: 5 };
|
|
1007
|
+
return { code: 2 };
|
|
875
1008
|
}
|
|
876
1009
|
throw e;
|
|
877
1010
|
}
|
|
@@ -879,23 +1012,23 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
879
1012
|
const wouldCopy = result.wouldCopy ?? [];
|
|
880
1013
|
for (const relativePath of wouldCopy) {
|
|
881
1014
|
const targetPath = join(targetDir, relativePath);
|
|
882
|
-
|
|
1015
|
+
say(` ${"would create".padEnd(14)} ${targetPath}
|
|
883
1016
|
`);
|
|
884
1017
|
}
|
|
885
1018
|
if (wouldCopy.length > 0) {
|
|
886
|
-
|
|
1019
|
+
say(
|
|
887
1020
|
`bassclef init: ${wouldCopy.length} substrate files would be copied.
|
|
888
1021
|
`
|
|
889
1022
|
);
|
|
890
1023
|
}
|
|
891
|
-
|
|
1024
|
+
say(
|
|
892
1025
|
`bassclef init: ${result.hookCount} hooks armed (${RESOLVED_TIER} tier).
|
|
893
1026
|
`
|
|
894
1027
|
);
|
|
895
|
-
return 0;
|
|
1028
|
+
return { code: 0, result };
|
|
896
1029
|
}
|
|
897
1030
|
if (result.copied.length === 0 && result.refused.length === 0 && result.errored.length === 0) {
|
|
898
|
-
return 0;
|
|
1031
|
+
return { code: 0, result };
|
|
899
1032
|
}
|
|
900
1033
|
const groupCounts = /* @__PURE__ */ new Map();
|
|
901
1034
|
for (const path of result.copied) {
|
|
@@ -904,17 +1037,17 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
904
1037
|
groupCounts.set(top, (groupCounts.get(top) ?? 0) + 1);
|
|
905
1038
|
}
|
|
906
1039
|
for (const [directory, count] of groupCounts) {
|
|
907
|
-
|
|
1040
|
+
say(` ${directory}: ${count} files copied
|
|
908
1041
|
`);
|
|
909
1042
|
}
|
|
910
1043
|
const parts = [];
|
|
911
1044
|
if (result.copied.length > 0) parts.push(`${result.copied.length} substrate files copied`);
|
|
912
1045
|
if (result.refused.length > 0) parts.push(`${result.refused.length} refused`);
|
|
913
1046
|
if (result.errored.length > 0) parts.push(`${result.errored.length} error(s)`);
|
|
914
|
-
|
|
1047
|
+
say(`bassclef init: ${parts.join(", ")}.
|
|
915
1048
|
`);
|
|
916
1049
|
const grandTotal = 1 + result.copied.length;
|
|
917
|
-
|
|
1050
|
+
say(
|
|
918
1051
|
`bassclef init: ${grandTotal} files total (1 config + ${result.copied.length} substrate).
|
|
919
1052
|
`
|
|
920
1053
|
);
|
|
@@ -926,17 +1059,17 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
926
1059
|
);
|
|
927
1060
|
const copiedCount = copiedHookEntries.length;
|
|
928
1061
|
const declaredCount = result.hookCount;
|
|
929
|
-
const
|
|
1062
|
+
const erroredCount = result.errored.length;
|
|
930
1063
|
const userScope = copiedHookEntries.filter((e) => e.scope === "user").length;
|
|
931
1064
|
const projectScope = copiedHookEntries.filter((e) => e.scope === "project").length;
|
|
932
1065
|
const scopeSuffix = userScope + projectScope > 0 ? ` ${projectScope} in <repo>/${HOOKS_SUBPATH.replace(/\/$/, "")}, ${userScope} in ~/${HOOKS_SUBPATH.replace(/\/$/, "")}.` : "";
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${
|
|
1066
|
+
if (erroredCount > 0) {
|
|
1067
|
+
say(
|
|
1068
|
+
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${erroredCount} failed — see errors above. Rerun bassclef init to retry.
|
|
936
1069
|
`
|
|
937
1070
|
);
|
|
938
1071
|
} else {
|
|
939
|
-
|
|
1072
|
+
say(
|
|
940
1073
|
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix}
|
|
941
1074
|
`
|
|
942
1075
|
);
|
|
@@ -981,7 +1114,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
981
1114
|
catalogCounts.luminaries > 0 ? `${catalogCounts.luminaries} luminaries` : null
|
|
982
1115
|
].filter((s) => s !== null);
|
|
983
1116
|
if (claudeCounts.length > 0) {
|
|
984
|
-
|
|
1117
|
+
say(
|
|
985
1118
|
`bassclef init: Installed ${claudeCounts.join(", ")} under <repo>/.claude/.
|
|
986
1119
|
`
|
|
987
1120
|
);
|
|
@@ -996,24 +1129,16 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
996
1129
|
catalogCounts.scripts > 0 ? `${catalogCounts.scripts} scripts` : null
|
|
997
1130
|
].filter((s) => s !== null);
|
|
998
1131
|
if (otherCounts.length > 0) {
|
|
999
|
-
|
|
1132
|
+
say(
|
|
1000
1133
|
`bassclef init: Installed ${otherCounts.join(", ")} under <repo>/.
|
|
1001
1134
|
`
|
|
1002
1135
|
);
|
|
1003
1136
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1137
|
+
if (result.refused.length > 0) {
|
|
1138
|
+
say(
|
|
1139
|
+
`bassclef init: ${result.refused.length} files refused (path collision). Use --force to overwrite existing files.
|
|
1006
1140
|
`
|
|
1007
|
-
|
|
1008
|
-
if (json) {
|
|
1009
|
-
const report = {
|
|
1010
|
-
copied: copiedCount,
|
|
1011
|
-
declared: declaredCount,
|
|
1012
|
-
failed: failedCount,
|
|
1013
|
-
scope_counts: { user: userScope, project: projectScope },
|
|
1014
|
-
tier: RESOLVED_TIER
|
|
1015
|
-
};
|
|
1016
|
-
process.stderr.write(JSON.stringify(report) + "\n");
|
|
1141
|
+
);
|
|
1017
1142
|
}
|
|
1018
1143
|
if (verbose && result.erroredMessages) {
|
|
1019
1144
|
for (const msg of result.erroredMessages) {
|
|
@@ -1021,7 +1146,7 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, jso
|
|
|
1021
1146
|
`);
|
|
1022
1147
|
}
|
|
1023
1148
|
}
|
|
1024
|
-
return result.errored.length > 0 ? 2 : 0;
|
|
1149
|
+
return { code: result.errored.length > 0 ? 2 : 0, result };
|
|
1025
1150
|
}
|
|
1026
1151
|
function makePlaceholderTransform(targetDir) {
|
|
1027
1152
|
const repoName = basename$1(targetDir);
|
|
@@ -1031,7 +1156,7 @@ function makePlaceholderTransform(targetDir) {
|
|
|
1031
1156
|
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);
|
|
1032
1157
|
};
|
|
1033
1158
|
}
|
|
1034
|
-
function runDryRun$1(plans) {
|
|
1159
|
+
function runDryRun$1(plans, say) {
|
|
1035
1160
|
for (const p of plans) {
|
|
1036
1161
|
let planned;
|
|
1037
1162
|
let extra = "";
|
|
@@ -1051,12 +1176,12 @@ function runDryRun$1(plans) {
|
|
|
1051
1176
|
planned = "would skip";
|
|
1052
1177
|
}
|
|
1053
1178
|
}
|
|
1054
|
-
|
|
1179
|
+
say(` ${planned.padEnd(14)} ${p.fullPath}${extra}
|
|
1055
1180
|
`);
|
|
1056
1181
|
}
|
|
1057
1182
|
return 0;
|
|
1058
1183
|
}
|
|
1059
|
-
function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
1184
|
+
function runReal$1(plans, force, verbose, targetDir, allowRoot, json, say) {
|
|
1060
1185
|
const results = [];
|
|
1061
1186
|
let anyRefused = false;
|
|
1062
1187
|
let anyError = false;
|
|
@@ -1104,7 +1229,7 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
|
1104
1229
|
if (r.outcome === "error") label = `error (${r.message ?? "unknown"})`;
|
|
1105
1230
|
else if (r.outcome === "refused") label = "refused";
|
|
1106
1231
|
else label = r.outcome;
|
|
1107
|
-
|
|
1232
|
+
say(` ${label.padEnd(10)} ${r.plan.fullPath}
|
|
1108
1233
|
`);
|
|
1109
1234
|
}
|
|
1110
1235
|
}
|
|
@@ -1126,38 +1251,59 @@ function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
|
1126
1251
|
return 2;
|
|
1127
1252
|
}
|
|
1128
1253
|
if (anyRefused && created > 0) {
|
|
1129
|
-
|
|
1254
|
+
say(
|
|
1130
1255
|
`bassclef init: ${created} config files created, ${unchanged} unchanged. Pass --force to overwrite.
|
|
1131
1256
|
`
|
|
1132
1257
|
);
|
|
1133
1258
|
} else if (created === 0 && unchanged === plans.length) {
|
|
1134
|
-
|
|
1259
|
+
say("bassclef init: already initialized. No changes.\n");
|
|
1135
1260
|
} else {
|
|
1136
|
-
|
|
1261
|
+
say(`bassclef init: ${created} config files created, ${unchanged} unchanged.
|
|
1137
1262
|
`);
|
|
1138
1263
|
}
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1264
|
+
const walker = dispatchSubstrateCopy(targetDir, force, verbose, false, allowRoot, say);
|
|
1265
|
+
const declaredLeaves = readDeclaredCommandLeaves(
|
|
1266
|
+
join(targetDir, ".claude", "settings.json")
|
|
1267
|
+
);
|
|
1268
|
+
const declaredHooksCopied = (walker.result?.copiedEntries ?? []).filter(
|
|
1269
|
+
(e) => e.path.startsWith(HOOKS_SUBPATH) && e.path.endsWith(".sh") && declaredLeaves.has(basename$1(e.path))
|
|
1270
|
+
).length;
|
|
1271
|
+
const report = buildInitReport({
|
|
1272
|
+
entries: walker.result?.copiedEntries ?? [],
|
|
1273
|
+
configs: results.map((r) => ({ path: r.plan.relativePath })),
|
|
1274
|
+
refused: walker.result?.refused ?? [],
|
|
1275
|
+
errored: walker.result?.errored ?? [],
|
|
1276
|
+
hookCount: walker.result?.hookCount ?? 0,
|
|
1277
|
+
declaredHooksCopied,
|
|
1278
|
+
tier: RESOLVED_TIER
|
|
1279
|
+
});
|
|
1280
|
+
writeManifest(targetDir, results, walker.result);
|
|
1281
|
+
if (walker.code === 0) {
|
|
1282
|
+
say(
|
|
1143
1283
|
`bassclef init: your substrate lives under .claude/. Add .claude/ to .gitignore if you have not.
|
|
1144
1284
|
`
|
|
1145
1285
|
);
|
|
1146
1286
|
}
|
|
1147
|
-
|
|
1287
|
+
if (json) {
|
|
1288
|
+
renderJsonReport(report, (t) => {
|
|
1289
|
+
process.stdout.write(t);
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
return walker.code;
|
|
1148
1293
|
}
|
|
1149
1294
|
function shouldRefuseRoot(currentUid, allowRoot) {
|
|
1150
1295
|
if (currentUid === void 0) return false;
|
|
1151
1296
|
if (currentUid !== 0) return false;
|
|
1152
1297
|
return !allowRoot;
|
|
1153
1298
|
}
|
|
1154
|
-
function writeManifest(targetDir, results) {
|
|
1299
|
+
function writeManifest(targetDir, results, walkerResult) {
|
|
1155
1300
|
const entries = results.map((r) => {
|
|
1156
1301
|
const entry = {
|
|
1157
1302
|
path: r.plan.relativePath,
|
|
1158
1303
|
template: r.plan.templateName,
|
|
1159
1304
|
template_version: r.plan.templateVersion,
|
|
1160
|
-
outcome: r.outcome
|
|
1305
|
+
outcome: r.outcome,
|
|
1306
|
+
source: "config-composer"
|
|
1161
1307
|
};
|
|
1162
1308
|
if (r.outcome === "created") {
|
|
1163
1309
|
entry.content_hash_sha256 = hashContent(r.plan.content);
|
|
@@ -1165,6 +1311,38 @@ function writeManifest(targetDir, results) {
|
|
|
1165
1311
|
}
|
|
1166
1312
|
return entry;
|
|
1167
1313
|
});
|
|
1314
|
+
if (walkerResult) {
|
|
1315
|
+
for (const copied of walkerResult.copiedEntries) {
|
|
1316
|
+
entries.push({
|
|
1317
|
+
path: copied.path,
|
|
1318
|
+
template: `bundle:${copied.path}`,
|
|
1319
|
+
template_version: version,
|
|
1320
|
+
outcome: "created",
|
|
1321
|
+
source: "bundle",
|
|
1322
|
+
scope: copied.scope,
|
|
1323
|
+
...copied.content_hash_sha256 ? { content_hash_sha256: copied.content_hash_sha256 } : {},
|
|
1324
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
for (const path of walkerResult.refused) {
|
|
1328
|
+
entries.push({
|
|
1329
|
+
path,
|
|
1330
|
+
template: `bundle:${path}`,
|
|
1331
|
+
template_version: version,
|
|
1332
|
+
outcome: "refused",
|
|
1333
|
+
source: "bundle"
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
for (const path of walkerResult.errored) {
|
|
1337
|
+
entries.push({
|
|
1338
|
+
path,
|
|
1339
|
+
template: `bundle:${path}`,
|
|
1340
|
+
template_version: version,
|
|
1341
|
+
outcome: "error",
|
|
1342
|
+
source: "bundle"
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1168
1346
|
const manifestDir = join(targetDir, ".bassclef");
|
|
1169
1347
|
const manifestPath = join(manifestDir, "init.manifest.json");
|
|
1170
1348
|
const content = manifestTemplate({
|
|
@@ -1175,7 +1353,12 @@ function writeManifest(targetDir, results) {
|
|
|
1175
1353
|
try {
|
|
1176
1354
|
mkdirSafely(manifestDir);
|
|
1177
1355
|
writeSafely(manifestPath, content, { force: true });
|
|
1178
|
-
} catch {
|
|
1356
|
+
} catch (e) {
|
|
1357
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
1358
|
+
process.stderr.write(
|
|
1359
|
+
`bassclef init: could not write ${manifestPath} (${reason}). The files were written. Run \`bassclef init --force\` to rebuild the record.
|
|
1360
|
+
`
|
|
1361
|
+
);
|
|
1179
1362
|
}
|
|
1180
1363
|
}
|
|
1181
1364
|
function usage$2() {
|
|
@@ -1626,7 +1809,10 @@ function emitL2Output(decisions, verbose) {
|
|
|
1626
1809
|
}
|
|
1627
1810
|
}
|
|
1628
1811
|
function updateManifestEntry(manifest, path, patch) {
|
|
1629
|
-
const
|
|
1812
|
+
const wantScope = patch.scope;
|
|
1813
|
+
const idx = manifest.files.findIndex(
|
|
1814
|
+
(f) => f.path === path && (wantScope === void 0 || f.scope === wantScope)
|
|
1815
|
+
);
|
|
1630
1816
|
if (idx < 0) {
|
|
1631
1817
|
manifest.files.push({
|
|
1632
1818
|
path,
|
|
@@ -1810,6 +1996,12 @@ Proceed?`,
|
|
|
1810
1996
|
const errored = copyResult.errored;
|
|
1811
1997
|
const newManifest = {
|
|
1812
1998
|
schema_version: MANIFEST_SHAPE_VERSION,
|
|
1999
|
+
// Top-level `.version` — read by the v0.45.0 npm-version-drift hook
|
|
2000
|
+
// (fragment 91 under session-reflection) via lib/npm-version-check.sh
|
|
2001
|
+
// `jq -r '.version // ""' "$manifest"`. Duplicates
|
|
2002
|
+
// $bassclef.generated_by_version on purpose — the hook does not walk
|
|
2003
|
+
// into $bassclef. Per bassclef-upstream#1749; bassclef-cli#129.
|
|
2004
|
+
version,
|
|
1813
2005
|
$bassclef: {
|
|
1814
2006
|
template: "init.manifest.json",
|
|
1815
2007
|
manifest_schema_version: "0.1.0",
|
|
@@ -1977,6 +2169,9 @@ Run \`bassclef migrate --help\` for migrate options + the upgrade paths.
|
|
|
1977
2169
|
Version: ${version}
|
|
1978
2170
|
Docs: https://github.com/sunj-labs/bassclef-cli
|
|
1979
2171
|
`;
|
|
2172
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
2173
|
+
stream._handle?.setBlocking?.(true);
|
|
2174
|
+
}
|
|
1980
2175
|
async function main(argv) {
|
|
1981
2176
|
const first = argv[0];
|
|
1982
2177
|
if (first === void 0 || first === "--help" || first === "-h" || first === "help") {
|
package/dist/index.cjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version: "1.
|
|
1
|
+
export declare const version: "1.2.0-alpha.1";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// tier: lite
|
|
2
|
+
// bassclef adopter config — tune substrate rules from one file.
|
|
3
|
+
// Reference schema: standards/bassclef-configs-schema.md
|
|
4
|
+
// Every block below matches what a substrate rule reads. Values shown
|
|
5
|
+
// are the defaults; delete a block and the default kicks in.
|
|
6
|
+
{
|
|
7
|
+
// What your project runs. Frontend framework the /shadcn-first and
|
|
8
|
+
// /new-dependency-check rules key off. Common values: "vite-react",
|
|
9
|
+
// "nextjs", "remix", "vue", "svelte", "none".
|
|
10
|
+
"tech_stack": {
|
|
11
|
+
"frontend": "none"
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// Prose discipline. The kiss-check Stop hook watches your writing
|
|
15
|
+
// for jargon. Value "true" = advisory (findings to stderr, no block).
|
|
16
|
+
// Value "strict" = BLOCK the stop, forcing a rewrite turn.
|
|
17
|
+
// Value false = disabled.
|
|
18
|
+
"prose_discipline": {
|
|
19
|
+
"kiss_words_turn_prose": true
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Where /promote sends proposals. Two shapes:
|
|
23
|
+
// "<owner>/<repo>" — gh issue create --repo <value>
|
|
24
|
+
// "email:<address>" — mailto: URL printed for adopter to send
|
|
25
|
+
// Absent → /promote files in the current repo (backward-compat).
|
|
26
|
+
// "promote_target": "your-org/your-repo",
|
|
27
|
+
|
|
28
|
+
// Testing tiers per .claude/rules/testing-tier-config.md. Global
|
|
29
|
+
// floor sets the baseline for every path that no matcher covers.
|
|
30
|
+
// Tier 0 = strict TDD (test-first, BLOCK on missing). Tier 1 =
|
|
31
|
+
// test-with (WARN on missing). Tier 2 = smoke test only. Tier 3 =
|
|
32
|
+
// manual verify (no test discipline).
|
|
33
|
+
"testing": {
|
|
34
|
+
"global_floor": 1,
|
|
35
|
+
"path_matchers": [
|
|
36
|
+
{ "match": ".claude/rules/*.md", "tier": 3 },
|
|
37
|
+
{ "match": "state/markers/*/*", "tier": 3 },
|
|
38
|
+
{ "match": "chronicle/*.md", "tier": 3 },
|
|
39
|
+
{ "match": "docs/iteration-bets/*.md", "tier": 3 },
|
|
40
|
+
{ "match": "docs/decompositions/*.md", "tier": 3 }
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// /longrun preset picker per .claude/skills/longrun/SKILL.md. Values:
|
|
45
|
+
// "converged" — plan doc drives; lean prep output
|
|
46
|
+
// "exploratory" — scan-table with no cards until asked
|
|
47
|
+
// "reversible-small" — three-chunk compact for small scope
|
|
48
|
+
// "auto" — picker decides per session signals (default)
|
|
49
|
+
"longrun": {
|
|
50
|
+
"preset": "auto"
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Adopter defaults surface. Reserved for future rules. Absent =
|
|
54
|
+
// downstream rules keep their own defaults.
|
|
55
|
+
"adopter_defaults": {}
|
|
56
|
+
}
|