@thebassclef/lite 1.0.0 → 1.0.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 +240 -23
- package/dist/cli.js +242 -25
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/hooks/artifact-ingestion-gate.sh +357 -0
- package/dist/lite/.claude/hooks/assert-verify-steering.sh +77 -0
- package/dist/lite/.claude/hooks/bassclef-source-config-validate.sh +215 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +634 -0
- package/dist/lite/.claude/hooks/compound-noun-scrub.sh +292 -0
- package/dist/lite/.claude/hooks/kiss-expansion-inject.sh +69 -0
- package/dist/lite/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +156 -0
- package/dist/lite/.claude/hooks/post-skill-friction-check.sh +177 -0
- package/dist/lite/.claude/hooks/post-skill-telemetry.sh +62 -0
- package/dist/lite/.claude/hooks/pre-build-gate.sh +511 -0
- package/dist/lite/.claude/hooks/pre-commit-gate.sh +451 -0
- package/dist/lite/.claude/hooks/session-end.sh +433 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +303 -0
- package/dist/lite/.claude/hooks/skill-body-grade-gate.sh +219 -0
- package/dist/lite/.claude/hooks/skill-body-intent-drift.sh +107 -0
- package/dist/lite/.claude/hooks/state-validate.sh +271 -0
- package/dist/lite/.claude/hooks/substrate-clarity-gate.sh +1110 -0
- package/dist/lite/.claude/hooks/temperance-gate.sh +147 -0
- package/dist/lite/.claude/hooks/testing-tier-enforce.sh +233 -0
- package/dist/lite/.claude/hooks/turn-prose-grade-measure.sh +219 -0
- package/dist/lite/.claude/hooks/turn-prose-kiss-check.sh +463 -0
- package/dist/lite/.claude/hooks/vocabulary-migration-check.sh +171 -0
- package/dist/lite/.claude/hooks/whereami-utc-gate.sh +142 -0
- package/dist/lite/CLAUDE.md +2 -2
- package/dist/lite/whereami.md +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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, readFileSync, readdirSync, existsSync } from "node:fs";
|
|
4
|
-
import { isAbsolute, resolve, dirname, join, relative, basename as basename$1 } from "node:path";
|
|
3
|
+
import { realpathSync, statSync, constants, lstatSync, mkdirSync, accessSync, unlinkSync, openSync, writeSync, closeSync, readlinkSync, readFileSync, chmodSync, readdirSync, existsSync } from "node:fs";
|
|
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";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -13,7 +13,9 @@ const DEFAULTS$2 = {
|
|
|
13
13
|
verbose: false,
|
|
14
14
|
allowRoot: false,
|
|
15
15
|
allowAnyDir: false,
|
|
16
|
-
dir: void 0
|
|
16
|
+
dir: void 0,
|
|
17
|
+
yes: false,
|
|
18
|
+
json: false
|
|
17
19
|
};
|
|
18
20
|
let ArgvError$1 = class ArgvError extends Error {
|
|
19
21
|
name = "ArgvError";
|
|
@@ -48,6 +50,16 @@ function parseInitArgs(argv) {
|
|
|
48
50
|
i += 1;
|
|
49
51
|
continue;
|
|
50
52
|
}
|
|
53
|
+
if (token === "--yes") {
|
|
54
|
+
out.yes = true;
|
|
55
|
+
i += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (token === "--json") {
|
|
59
|
+
out.json = true;
|
|
60
|
+
i += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
51
63
|
if (token === "--dir") {
|
|
52
64
|
const value = argv[i + 1];
|
|
53
65
|
if (value === void 0 || value.startsWith("--")) {
|
|
@@ -116,6 +128,13 @@ function isUnder(child, parent) {
|
|
|
116
128
|
if (rel === par) return true;
|
|
117
129
|
return rel.startsWith(par + "/");
|
|
118
130
|
}
|
|
131
|
+
function readlinkOrNull(path) {
|
|
132
|
+
try {
|
|
133
|
+
return readlinkSync(path);
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
119
138
|
class WriteError extends Error {
|
|
120
139
|
name = "WriteError";
|
|
121
140
|
kind;
|
|
@@ -149,7 +168,12 @@ function writeSafely(path, content, opts = {}) {
|
|
|
149
168
|
else throw new WriteError("Unknown", `cannot stat ${path}: ${err.code ?? "unknown"}`);
|
|
150
169
|
}
|
|
151
170
|
if (existing === "symlink") {
|
|
152
|
-
|
|
171
|
+
const target = readlinkOrNull(path);
|
|
172
|
+
const targetClause = target ? ` (points to: ${target})` : "";
|
|
173
|
+
throw new WriteError(
|
|
174
|
+
"SymlinkRefused",
|
|
175
|
+
`refusing to follow symlink at target path: ${path}${targetClause}. Delete or move the symlink and rerun bassclef init.`
|
|
176
|
+
);
|
|
153
177
|
}
|
|
154
178
|
if (existing !== "none" && !opts.force) {
|
|
155
179
|
throw new WriteError("AlreadyExists", `file already exists (pass --force to overwrite): ${path}`);
|
|
@@ -266,8 +290,10 @@ function substrateConfigMdTemplate(pkgVersion) {
|
|
|
266
290
|
].join("\n");
|
|
267
291
|
}
|
|
268
292
|
const MANIFEST_SCHEMA_VERSION = "0.1.0";
|
|
293
|
+
const MANIFEST_SHAPE_VERSION = 2;
|
|
269
294
|
function manifestTemplate(input) {
|
|
270
295
|
const value = {
|
|
296
|
+
schema_version: MANIFEST_SHAPE_VERSION,
|
|
271
297
|
$bassclef: {
|
|
272
298
|
template: "init.manifest.json",
|
|
273
299
|
manifest_schema_version: MANIFEST_SCHEMA_VERSION,
|
|
@@ -289,6 +315,15 @@ class ManifestReadError extends Error {
|
|
|
289
315
|
this.kind = kind;
|
|
290
316
|
}
|
|
291
317
|
}
|
|
318
|
+
function readManifestShapeVersion(targetDir) {
|
|
319
|
+
try {
|
|
320
|
+
const parsed = readManifest(targetDir);
|
|
321
|
+
if (typeof parsed.schema_version === "number") return parsed.schema_version;
|
|
322
|
+
return null;
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
292
327
|
function readManifest(targetDir) {
|
|
293
328
|
const path = join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
294
329
|
let raw;
|
|
@@ -369,8 +404,6 @@ async function computeConfigHashes(targetDir, paths) {
|
|
|
369
404
|
}
|
|
370
405
|
return out;
|
|
371
406
|
}
|
|
372
|
-
const EXPECTED_WIRING_SCHEMA_MAJOR = 2;
|
|
373
|
-
const WIRING_MANIFEST_RELATIVE = "standards/bassclef-wiring-manifest.json";
|
|
374
407
|
class CopyFailure extends Error {
|
|
375
408
|
constructor(kind, message) {
|
|
376
409
|
super(message);
|
|
@@ -378,11 +411,106 @@ class CopyFailure extends Error {
|
|
|
378
411
|
this.name = "CopyFailure";
|
|
379
412
|
}
|
|
380
413
|
}
|
|
414
|
+
function resolveHome(opts) {
|
|
415
|
+
const raw = process.env.HOME;
|
|
416
|
+
if (raw === void 0 || raw === "") {
|
|
417
|
+
throw new CopyFailure(
|
|
418
|
+
"EnvironmentIncomplete",
|
|
419
|
+
"HOME environment variable is unset. bassclef init needs HOME set to resolve user-scope hook targets. Set HOME to your home directory and rerun."
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
if (raw === "/root" && !opts.allowRoot) {
|
|
423
|
+
throw new CopyFailure(
|
|
424
|
+
"SudoBypassRefused",
|
|
425
|
+
"HOME resolves to /root — sudo bypass detected. Routing user-scope hooks to /root breaks the adopter maintenance model. Run bassclef init without sudo, or pass --allow-root if this is intentional."
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
const canonical = raw === homedir() ? homedir() : raw;
|
|
429
|
+
try {
|
|
430
|
+
return realpathSync(canonical);
|
|
431
|
+
} catch {
|
|
432
|
+
return canonical.endsWith("/") ? canonical.slice(0, -1) : canonical;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const PREFIX_HOME = "$HOME/";
|
|
436
|
+
const PREFIX_PROJECT = "$CLAUDE_PROJECT_DIR/";
|
|
437
|
+
function classify$1(hook, opts) {
|
|
438
|
+
const cmd = hook.command;
|
|
439
|
+
if (typeof cmd !== "string" || cmd.length === 0) {
|
|
440
|
+
throw new CopyFailure(
|
|
441
|
+
"UnknownScopePrefix",
|
|
442
|
+
"Hook command is empty. settings.json must supply a non-empty command string."
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
if (cmd.startsWith(PREFIX_HOME)) {
|
|
446
|
+
return classifyUser(cmd.slice(PREFIX_HOME.length), opts);
|
|
447
|
+
}
|
|
448
|
+
if (cmd.startsWith(PREFIX_PROJECT)) {
|
|
449
|
+
return classifyProject(cmd.slice(PREFIX_PROJECT.length), opts);
|
|
450
|
+
}
|
|
451
|
+
throw new CopyFailure(
|
|
452
|
+
"UnknownScopePrefix",
|
|
453
|
+
`Hook command "${cmd}" uses an unknown scope prefix. cli 1.0.1 handles "$HOME/..." and "$CLAUDE_PROJECT_DIR/..." only. Upgrade cli to a version that supports this prefix.`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
function classifyUser(relPath, opts) {
|
|
457
|
+
const home = resolveHome({ allowRoot: opts.allowRoot });
|
|
458
|
+
const cleaned = normalizeRel(relPath);
|
|
459
|
+
const targetPath = normalize(join(home, cleaned));
|
|
460
|
+
assertContained(targetPath, home, opts.allowRoot ? "/root" : null);
|
|
461
|
+
return { scope: "user", targetPath };
|
|
462
|
+
}
|
|
463
|
+
function classifyProject(relPath, opts) {
|
|
464
|
+
const cleaned = normalizeRel(relPath);
|
|
465
|
+
const targetPath = normalize(join(opts.targetDir, cleaned));
|
|
466
|
+
assertContained(targetPath, opts.targetDir);
|
|
467
|
+
return { scope: "project", targetPath };
|
|
468
|
+
}
|
|
469
|
+
function normalizeRel(rel) {
|
|
470
|
+
return rel.replace(/\/{2,}/g, "/");
|
|
471
|
+
}
|
|
472
|
+
function assertContained(target, root, altRoot) {
|
|
473
|
+
const resolvedTarget = resolve(target);
|
|
474
|
+
const resolvedRoot = resolve(root);
|
|
475
|
+
if (resolvedTarget === resolvedRoot) return;
|
|
476
|
+
if (resolvedTarget.startsWith(resolvedRoot + "/")) return;
|
|
477
|
+
if (altRoot) {
|
|
478
|
+
const resolvedAlt = resolve(altRoot);
|
|
479
|
+
if (resolvedTarget === resolvedAlt) return;
|
|
480
|
+
if (resolvedTarget.startsWith(resolvedAlt + "/")) return;
|
|
481
|
+
}
|
|
482
|
+
throw new CopyFailure(
|
|
483
|
+
"PathTraversalRefused",
|
|
484
|
+
`Hook target "${target}" escapes ${root}. settings.json commands must resolve inside the declared scope root. If this is a bassclef-upstream bundle defect, file at sunj-labs/bassclef-upstream.`
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
const HOOK_EXECUTABLE_MODE = 493;
|
|
488
|
+
function setExecutable(targetPath) {
|
|
489
|
+
if (process.platform === "win32") {
|
|
490
|
+
process.stderr.write(
|
|
491
|
+
`bassclef init: executable bit not applicable on this OS (${targetPath}). Claude Code will still invoke the hook if a POSIX shell layer is present.
|
|
492
|
+
`
|
|
493
|
+
);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
chmodSync(targetPath, HOOK_EXECUTABLE_MODE);
|
|
497
|
+
}
|
|
498
|
+
const HOOKS_SUBPATH = ".claude/hooks/";
|
|
499
|
+
const SETTINGS_SUBPATH = ".claude/settings.json";
|
|
500
|
+
const CONFIG_FILES = [
|
|
501
|
+
".claude/settings.json",
|
|
502
|
+
"substrate.config.md",
|
|
503
|
+
"substrate.secrets.md"
|
|
504
|
+
];
|
|
505
|
+
const CURRENT_ENTRY_COUNT = 149;
|
|
506
|
+
const EXPECTED_WIRING_SCHEMA_MAJOR = 2;
|
|
507
|
+
const WIRING_MANIFEST_RELATIVE = "standards/bassclef-wiring-manifest.json";
|
|
381
508
|
function copySubstrate(targetDir, options = {}) {
|
|
382
509
|
const bundleRoot = resolveBundleRoot(options.bundleRoot);
|
|
383
510
|
const manifest = readWiringManifest(bundleRoot);
|
|
384
511
|
const result = {
|
|
385
512
|
copied: [],
|
|
513
|
+
copiedEntries: [],
|
|
386
514
|
refused: [],
|
|
387
515
|
errored: [],
|
|
388
516
|
erroredMessages: [],
|
|
@@ -392,10 +520,12 @@ function copySubstrate(targetDir, options = {}) {
|
|
|
392
520
|
if (options.dryRun) result.wouldCopy = [];
|
|
393
521
|
const files = walkDistTree(bundleRoot);
|
|
394
522
|
const groups = groupByTopDirectory(files);
|
|
523
|
+
const bundleHookRelPaths = new Set(files.filter(isHookFile));
|
|
524
|
+
const scopeMap = buildScopeMap(bundleRoot, targetDir, options, bundleHookRelPaths);
|
|
395
525
|
for (const [directory, groupFiles] of groups) {
|
|
396
526
|
let completedInGroup = 0;
|
|
397
527
|
for (const relPath of groupFiles) {
|
|
398
|
-
const outcome = copyOne(relPath, bundleRoot, targetDir, options, result);
|
|
528
|
+
const outcome = copyOne(relPath, bundleRoot, targetDir, options, result, scopeMap);
|
|
399
529
|
if (outcome !== "skipped") completedInGroup += 1;
|
|
400
530
|
}
|
|
401
531
|
if (options.onProgress) options.onProgress(directory, completedInGroup);
|
|
@@ -474,10 +604,41 @@ function mapAdopterPath(relPath) {
|
|
|
474
604
|
if (relPath === GITIGNORE_BUNDLE_NAME) return GITIGNORE_ADOPTER_NAME;
|
|
475
605
|
return relPath;
|
|
476
606
|
}
|
|
477
|
-
function
|
|
607
|
+
function buildScopeMap(bundleRoot, targetDir, options, bundleHookRelPaths) {
|
|
608
|
+
const map = /* @__PURE__ */ new Map();
|
|
609
|
+
const settingsPath = join(bundleRoot, SETTINGS_SUBPATH);
|
|
610
|
+
if (!fileExists(settingsPath)) return map;
|
|
611
|
+
let parsed;
|
|
612
|
+
try {
|
|
613
|
+
parsed = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
614
|
+
} catch {
|
|
615
|
+
return map;
|
|
616
|
+
}
|
|
617
|
+
const allowRoot = options.allowRoot ?? false;
|
|
618
|
+
for (const eventBlocks of Object.values(parsed.hooks ?? {})) {
|
|
619
|
+
for (const block of eventBlocks) {
|
|
620
|
+
for (const entry of block.hooks ?? []) {
|
|
621
|
+
const cmd = entry.command;
|
|
622
|
+
if (typeof cmd !== "string" || cmd.length === 0) continue;
|
|
623
|
+
if (!cmd.startsWith("$")) continue;
|
|
624
|
+
const decision = classify$1({ command: cmd }, { targetDir, allowRoot });
|
|
625
|
+
const relSource = cmd.replace(/^\$HOME\//, "").replace(/^\$CLAUDE_PROJECT_DIR\//, "").replace(/\/{2,}/g, "/");
|
|
626
|
+
if (!bundleHookRelPaths.has(relSource)) continue;
|
|
627
|
+
map.set(relSource, decision);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return map;
|
|
632
|
+
}
|
|
633
|
+
function isHookFile(relPath) {
|
|
634
|
+
return relPath.startsWith(HOOKS_SUBPATH) && relPath.endsWith(".sh");
|
|
635
|
+
}
|
|
636
|
+
function copyOne(relPath, bundleRoot, targetDir, options, result, scopeMap) {
|
|
478
637
|
const sourcePath = join(bundleRoot, relPath);
|
|
479
638
|
const adopterRelPath = mapAdopterPath(relPath);
|
|
480
|
-
const
|
|
639
|
+
const scopeDecision = isHookFile(relPath) ? scopeMap.get(relPath) : void 0;
|
|
640
|
+
const targetPath = scopeDecision ? scopeDecision.targetPath : join(targetDir, adopterRelPath);
|
|
641
|
+
const scope = scopeDecision ? scopeDecision.scope : "project";
|
|
481
642
|
let content;
|
|
482
643
|
try {
|
|
483
644
|
content = readFileSync(sourcePath, "utf8");
|
|
@@ -496,7 +657,11 @@ function copyOne(relPath, bundleRoot, targetDir, options, result) {
|
|
|
496
657
|
try {
|
|
497
658
|
mkdirSafely(dirname(targetPath));
|
|
498
659
|
writeSafely(targetPath, outputContent, { force: options.force ?? false });
|
|
660
|
+
if (isHookFile(relPath)) {
|
|
661
|
+
setExecutable(targetPath);
|
|
662
|
+
}
|
|
499
663
|
result.copied.push(adopterRelPath);
|
|
664
|
+
result.copiedEntries.push({ path: adopterRelPath, scope });
|
|
500
665
|
return "copied";
|
|
501
666
|
} catch (e) {
|
|
502
667
|
if (e instanceof WriteError) {
|
|
@@ -506,6 +671,8 @@ function copyOne(relPath, bundleRoot, targetDir, options, result) {
|
|
|
506
671
|
}
|
|
507
672
|
if (e.kind === "SymlinkRefused") {
|
|
508
673
|
result.refused.push(adopterRelPath);
|
|
674
|
+
process.stderr.write(`bassclef init: ${e.message}
|
|
675
|
+
`);
|
|
509
676
|
return "refused";
|
|
510
677
|
}
|
|
511
678
|
const message = `${adopterRelPath} — write failed (${e.kind}): ${e.message}. Check the target directory exists and is writable, then rerun.`;
|
|
@@ -613,7 +780,10 @@ function runInit(argv) {
|
|
|
613
780
|
}
|
|
614
781
|
throw e;
|
|
615
782
|
}
|
|
616
|
-
|
|
783
|
+
const advisoryOutcome = maybeEmitUpgradeAdvisory(targetDir, args.yes);
|
|
784
|
+
if (advisoryOutcome === "refused") return 1;
|
|
785
|
+
const upgradeApproved = advisoryOutcome === "upgrade-approved";
|
|
786
|
+
if (!args.force && !args.dryRun && !upgradeApproved) {
|
|
617
787
|
const manifestPath = join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
618
788
|
if (existsSync(manifestPath)) {
|
|
619
789
|
process.stderr.write(
|
|
@@ -634,18 +804,44 @@ function runInit(argv) {
|
|
|
634
804
|
];
|
|
635
805
|
if (args.dryRun) {
|
|
636
806
|
runDryRun$1(plans);
|
|
637
|
-
return dispatchSubstrateCopy(targetDir, args.force, args.verbose, true);
|
|
807
|
+
return dispatchSubstrateCopy(targetDir, args.force || upgradeApproved, args.verbose, true, args.allowRoot, args.json);
|
|
808
|
+
}
|
|
809
|
+
return runReal$1(plans, args.force || upgradeApproved, args.verbose, targetDir, args.allowRoot, args.json);
|
|
810
|
+
}
|
|
811
|
+
function maybeEmitUpgradeAdvisory(targetDir, yes) {
|
|
812
|
+
const manifestPath = join(targetDir, MANIFEST_RELATIVE_PATH);
|
|
813
|
+
if (!existsSync(manifestPath)) return "ok";
|
|
814
|
+
const version2 = readManifestShapeVersion(targetDir);
|
|
815
|
+
if (version2 !== null && version2 >= 2) return "ok";
|
|
816
|
+
process.stdout.write(
|
|
817
|
+
`bassclef init: cli 1.0.1 introduces user-scope hook installation at ~/${HOOKS_SUBPATH}. cli 1.0.0 did not write there.
|
|
818
|
+
`
|
|
819
|
+
);
|
|
820
|
+
if (yes) return "upgrade-approved";
|
|
821
|
+
process.stdout.write("bassclef init: continue? (y/N) ");
|
|
822
|
+
const answer = readOneLineFromStdin();
|
|
823
|
+
if (answer === "" || answer === "y" || answer === "Y") return "upgrade-approved";
|
|
824
|
+
process.stdout.write("bassclef init: aborted by adopter.\n");
|
|
825
|
+
return "refused";
|
|
826
|
+
}
|
|
827
|
+
function readOneLineFromStdin() {
|
|
828
|
+
try {
|
|
829
|
+
const buf = Buffer.alloc(256);
|
|
830
|
+
const n = require("node:fs").readSync(0, buf, 0, 256, null);
|
|
831
|
+
return n > 0 ? buf.slice(0, n).toString("utf8").trim() : "";
|
|
832
|
+
} catch {
|
|
833
|
+
return "";
|
|
638
834
|
}
|
|
639
|
-
return runReal$1(plans, args.force, args.verbose, targetDir);
|
|
640
835
|
}
|
|
641
|
-
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun) {
|
|
836
|
+
function dispatchSubstrateCopy(targetDir, force, verbose, dryRun, allowRoot, json) {
|
|
642
837
|
const substitute = makePlaceholderTransform(targetDir);
|
|
643
838
|
let result;
|
|
644
839
|
try {
|
|
645
840
|
result = copySubstrate(targetDir, {
|
|
646
841
|
force,
|
|
647
842
|
dryRun,
|
|
648
|
-
transform: substitute
|
|
843
|
+
transform: substitute,
|
|
844
|
+
allowRoot
|
|
649
845
|
});
|
|
650
846
|
} catch (e) {
|
|
651
847
|
if (e instanceof CopyFailure) {
|
|
@@ -700,10 +896,36 @@ function dispatchSubstrateCopy(targetDir, force, verbose, dryRun) {
|
|
|
700
896
|
`bassclef init: ${grandTotal} files total (1 config + ${result.copied.length} substrate).
|
|
701
897
|
`
|
|
702
898
|
);
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
`
|
|
899
|
+
const copiedHookEntries = result.copiedEntries.filter(
|
|
900
|
+
(e) => e.path.startsWith(HOOKS_SUBPATH) && e.path.endsWith(".sh")
|
|
706
901
|
);
|
|
902
|
+
const copiedCount = copiedHookEntries.length;
|
|
903
|
+
const declaredCount = result.hookCount;
|
|
904
|
+
const failedCount = result.refused.length + result.errored.length;
|
|
905
|
+
const userScope = copiedHookEntries.filter((e) => e.scope === "user").length;
|
|
906
|
+
const projectScope = copiedHookEntries.filter((e) => e.scope === "project").length;
|
|
907
|
+
const scopeSuffix = userScope + projectScope > 0 ? ` ${projectScope} in <repo>/${HOOKS_SUBPATH.replace(/\/$/, "")}, ${userScope} in ~/${HOOKS_SUBPATH.replace(/\/$/, "")}.` : "";
|
|
908
|
+
if (failedCount > 0) {
|
|
909
|
+
process.stdout.write(
|
|
910
|
+
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix} ${failedCount} failed — see errors above. Rerun bassclef init to retry.
|
|
911
|
+
`
|
|
912
|
+
);
|
|
913
|
+
} else {
|
|
914
|
+
process.stdout.write(
|
|
915
|
+
`bassclef init: Installed ${copiedCount} of ${declaredCount} hooks (${RESOLVED_TIER} tier).${scopeSuffix}
|
|
916
|
+
`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
if (json) {
|
|
920
|
+
const report = {
|
|
921
|
+
copied: copiedCount,
|
|
922
|
+
declared: declaredCount,
|
|
923
|
+
failed: failedCount,
|
|
924
|
+
scope_counts: { user: userScope, project: projectScope },
|
|
925
|
+
tier: RESOLVED_TIER
|
|
926
|
+
};
|
|
927
|
+
process.stderr.write(JSON.stringify(report) + "\n");
|
|
928
|
+
}
|
|
707
929
|
if (verbose && result.erroredMessages) {
|
|
708
930
|
for (const msg of result.erroredMessages) {
|
|
709
931
|
process.stderr.write(` substrate: ${msg}
|
|
@@ -745,7 +967,7 @@ function runDryRun$1(plans) {
|
|
|
745
967
|
}
|
|
746
968
|
return 0;
|
|
747
969
|
}
|
|
748
|
-
function runReal$1(plans, force, verbose, targetDir) {
|
|
970
|
+
function runReal$1(plans, force, verbose, targetDir, allowRoot, json) {
|
|
749
971
|
const results = [];
|
|
750
972
|
let anyRefused = false;
|
|
751
973
|
let anyError = false;
|
|
@@ -825,7 +1047,7 @@ function runReal$1(plans, force, verbose, targetDir) {
|
|
|
825
1047
|
process.stdout.write(`bassclef init: ${created} config files created, ${unchanged} unchanged.
|
|
826
1048
|
`);
|
|
827
1049
|
}
|
|
828
|
-
const walkerExit = dispatchSubstrateCopy(targetDir, force, verbose, false);
|
|
1050
|
+
const walkerExit = dispatchSubstrateCopy(targetDir, force, verbose, false, allowRoot, json);
|
|
829
1051
|
writeManifest(targetDir, results);
|
|
830
1052
|
if (walkerExit === 0) {
|
|
831
1053
|
process.stdout.write(
|
|
@@ -1429,12 +1651,6 @@ async function confirm(question, opts = {}) {
|
|
|
1429
1651
|
rl.close();
|
|
1430
1652
|
}
|
|
1431
1653
|
}
|
|
1432
|
-
const CONFIG_FILES = [
|
|
1433
|
-
".claude/settings.json",
|
|
1434
|
-
"substrate.config.md",
|
|
1435
|
-
"substrate.secrets.md"
|
|
1436
|
-
];
|
|
1437
|
-
const CURRENT_ENTRY_COUNT = 149;
|
|
1438
1654
|
async function detectAdopterState(targetDir) {
|
|
1439
1655
|
let manifest;
|
|
1440
1656
|
try {
|
|
@@ -1484,6 +1700,7 @@ Proceed?`,
|
|
|
1484
1700
|
);
|
|
1485
1701
|
const errored = copyResult.errored;
|
|
1486
1702
|
const newManifest = {
|
|
1703
|
+
schema_version: MANIFEST_SHAPE_VERSION,
|
|
1487
1704
|
$bassclef: {
|
|
1488
1705
|
template: "init.manifest.json",
|
|
1489
1706
|
manifest_schema_version: "0.1.0",
|
package/dist/index.cjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version: "1.0.
|
|
1
|
+
export declare const version: "1.0.1";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED