@tech-leads-club/harness-toolkit 0.3.2 → 0.3.4
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/bin/tlc-build.mjs +15 -29
- package/bin/tlc-cli.ts +99 -3
- package/dist/compact-before.mjs +7961 -13
- package/dist/doctor.mjs +8480 -33
- package/dist/help-topic.mjs +14 -6
- package/dist/init-project.mjs +793 -36
- package/dist/install-runtime.mjs +7268 -17
- package/dist/lessons-cli.mjs +7044 -26
- package/dist/obs-cli.mjs +7037 -27
- package/dist/price-lookup.mjs +201 -11
- package/dist/prompt-submit.mjs +7975 -15
- package/dist/refresh-model-prices.mjs +7060 -25
- package/dist/response-after.mjs +7962 -12
- package/dist/run.mjs +7960 -10
- package/dist/session-end.mjs +8030 -15
- package/dist/session-start.mjs +8076 -21
- package/dist/shim.mjs +7024 -18
- package/dist/stop.mjs +8042 -29
- package/dist/subagent-start.mjs +7983 -13
- package/dist/subagent-stop.mjs +7962 -12
- package/dist/support.mjs +7168 -21
- package/dist/tlc-cli.mjs +8250 -65
- package/dist/tool-after.mjs +8177 -21
- package/dist/tool-before.mjs +7991 -16
- package/dist/tool-failure.mjs +7962 -15
- package/dist/uninstall-runtime.mjs +1008 -61
- package/docs/log.md +1 -1
- package/package.json +1 -1
- package/dist/chunks/compact-before-1e4qg1qt.mjs +0 -1185
- package/dist/chunks/compact-before-2hpbfxm5.mjs +0 -5782
- package/dist/chunks/compact-before-49j320yp.mjs +0 -1283
- package/dist/chunks/compact-before-4jrq0sqs.mjs +0 -61
- package/dist/chunks/compact-before-6w8n1vh1.mjs +0 -186
- package/dist/chunks/compact-before-7sdmwswh.mjs +0 -52
- package/dist/chunks/compact-before-beqpmqrm.mjs +0 -187
- package/dist/chunks/compact-before-j9y4jgn4.mjs +0 -845
- package/dist/chunks/compact-before-pk86tqx2.mjs +0 -118
- package/dist/chunks/compact-before-pkqk5v29.mjs +0 -137
- package/dist/chunks/compact-before-w1293m4n.mjs +0 -315
- package/dist/chunks/compact-before-wnnds45y.mjs +0 -26
- package/dist/chunks/compact-before-wt2c3nh4.mjs +0 -551
package/bin/tlc-build.mjs
CHANGED
|
@@ -38,32 +38,21 @@ function sourcesIn(dir) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* hazard: this built every entry in ONE invocation with `--splitting`, which cut `dist/` from 5.0 MB to 548 KB —
|
|
42
|
+
* and shipped a broken CLI. A shared chunk carries the module body of anything two entries both import, including
|
|
43
|
+
* `bin/tlc-cli.ts`, and its `if (import.meta.main)` guard evaluates **true** inside that chunk. So running
|
|
44
|
+
* `install-runtime`, which imports the CLI for `NPM_MARKER` and `wireRuntime`, ran the CLI's `main` instead:
|
|
45
|
+
* `tlc harness install` printed `unknown:` and installed nothing. Published as 0.3.2 and caught by installing it
|
|
44
46
|
* ([/decisions/ad-098.md](/decisions/ad-098.md)).
|
|
45
47
|
*
|
|
46
|
-
* invariant:
|
|
47
|
-
*
|
|
48
|
-
*
|
|
48
|
+
* invariant: one bundle per entry, so a module that self-executes behind `import.meta.main` is inlined into the
|
|
49
|
+
* one program that is allowed to run it. Splitting can come back when no library module carries that guard, and
|
|
50
|
+
* not before — the size win is real and it is not worth a CLI that cannot install.
|
|
49
51
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
function build(targets) {
|
|
52
|
+
function buildOne(source, out) {
|
|
53
53
|
const result = spawnSync(
|
|
54
54
|
"bun",
|
|
55
|
-
[
|
|
56
|
-
"build",
|
|
57
|
-
"--target=node",
|
|
58
|
-
"--format=esm",
|
|
59
|
-
"--splitting",
|
|
60
|
-
`--outdir=${dist}`,
|
|
61
|
-
"--entry-naming",
|
|
62
|
-
"[name].mjs",
|
|
63
|
-
"--chunk-naming",
|
|
64
|
-
`${CHUNK_DIR}/[name]-[hash].mjs`,
|
|
65
|
-
...targets.map((target) => target.source),
|
|
66
|
-
],
|
|
55
|
+
["build", "--target=node", "--format=esm", `--outfile=${out}`, source],
|
|
67
56
|
{ stdio: "inherit" },
|
|
68
57
|
);
|
|
69
58
|
if (result.error?.code === "ENOENT") {
|
|
@@ -85,14 +74,11 @@ const targets = [
|
|
|
85
74
|
mkdirSync(dist, { recursive: true });
|
|
86
75
|
console.log(`tlc-build → ${dist}`);
|
|
87
76
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*/
|
|
94
|
-
rmSync(join(dist, CHUNK_DIR), { recursive: true, force: true });
|
|
95
|
-
build(targets);
|
|
77
|
+
// invariant: a leftover chunk directory from the split build would ship for ever, referenced by nothing.
|
|
78
|
+
rmSync(join(dist, "chunks"), { recursive: true, force: true });
|
|
79
|
+
for (const target of targets) {
|
|
80
|
+
buildOne(target.source, join(dist, `${target.name}.mjs`));
|
|
81
|
+
}
|
|
96
82
|
|
|
97
83
|
// invariant: the launcher stays executable on a filesystem that tracks the bit. A no-op where it does not.
|
|
98
84
|
try {
|
package/bin/tlc-cli.ts
CHANGED
|
@@ -904,6 +904,67 @@ export function resolveHarnessRoot(): string {
|
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
906
|
|
|
907
|
+
/**
|
|
908
|
+
* Where npm put the package this command was installed from.
|
|
909
|
+
*
|
|
910
|
+
* hazard: `update` spawned `install-runtime` through the **runtime home's** launcher, so the tool resolved its
|
|
911
|
+
* source and its destination to the same directory and reported "already at … — nothing to copy". Measured on a
|
|
912
|
+
* scratch machine: `npm i -g` moved the package from 0.3.0 to 0.3.2 and the runtime the hooks execute stayed on
|
|
913
|
+
* 0.3.0. Every npm install that ever ran `update` bumped a package and kept its old code, while `doctor` said
|
|
914
|
+
* update "re-materialises this directory" ([/decisions/ad-098.md](/decisions/ad-098.md)).
|
|
915
|
+
*
|
|
916
|
+
* invariant: asked of npm rather than derived from this process. The CLI can be running from the runtime home,
|
|
917
|
+
* from the package, or from a linked clone, and only npm knows where it installs globally.
|
|
918
|
+
*/
|
|
919
|
+
export function globalPackageRoot(
|
|
920
|
+
probe = {
|
|
921
|
+
npmRoot: () => spawnSync("npm", ["root", "-g"], { encoding: "utf8", shell: true }).stdout ?? "",
|
|
922
|
+
exists: existsSync,
|
|
923
|
+
},
|
|
924
|
+
): string | null {
|
|
925
|
+
// invariant: trimmed here rather than in the probe. `npm root -g` ends in a newline, and a path with a newline
|
|
926
|
+
// in it fails as a directory while reading as a plausible string in an error message.
|
|
927
|
+
const root = probe.npmRoot().trim();
|
|
928
|
+
if (root.length === 0) {
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
const candidate = join(root, ...NPM_PACKAGE.split("/"));
|
|
932
|
+
return probe.exists(candidate) ? candidate : null;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* invariant: the *package's* launcher runs the materialisation, not the runtime home's. A release that fixes
|
|
937
|
+
* `install` has to be able to deliver that fix, and the old code cannot do it.
|
|
938
|
+
*
|
|
939
|
+
* invariant: only the **source** is named. `TLC_ORIGIN` is the one end this can know; the destination belongs to
|
|
940
|
+
* `installDest`, which exists because on a first npm run the *resolved* home is the package itself
|
|
941
|
+
* ([/decisions/ad-056.md](/decisions/ad-056.md)).
|
|
942
|
+
*
|
|
943
|
+
* hazard: the first version of this named the destination too, as `runtimeHome()`, and walked straight into that.
|
|
944
|
+
* On a clean machine it wrote `config.json` and the price catalogue into
|
|
945
|
+
* `node_modules/@tech-leads-club/harness-toolkit`, copied nothing, and then crashed writing hooks — Node refuses
|
|
946
|
+
* to strip types under `node_modules` ([/decisions/ad-098.md](/decisions/ad-098.md)).
|
|
947
|
+
*/
|
|
948
|
+
export function npmSyncPlan(packageRoot: string): {
|
|
949
|
+
command: string;
|
|
950
|
+
args: string[];
|
|
951
|
+
env: Record<string, string>;
|
|
952
|
+
} {
|
|
953
|
+
return {
|
|
954
|
+
command: process.execPath,
|
|
955
|
+
args: [join(packageRoot, "bin", "tlc-exec.mjs"), "install-runtime"],
|
|
956
|
+
env: { TLC_ORIGIN: packageRoot },
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
export function npmRootFailureMessage(home: string): string {
|
|
961
|
+
return [
|
|
962
|
+
`update: npm reported no global root, so the package it just installed cannot be found.`,
|
|
963
|
+
` The runtime at ${home} is unchanged — nothing was half-written.`,
|
|
964
|
+
` Run \`npm root -g\` yourself; then \`npm i -g ${NPM_PACKAGE}@latest\` and \`tlc harness install\`.`,
|
|
965
|
+
].join("\n");
|
|
966
|
+
}
|
|
967
|
+
|
|
907
968
|
/**
|
|
908
969
|
* Everything an install has to put in place outside the runtime directory itself: the init skill where each
|
|
909
970
|
* provider reads it, the user-level hooks, and a seeded config.
|
|
@@ -989,6 +1050,7 @@ export type Action =
|
|
|
989
1050
|
| { kind: "prices-refresh"; scope: string }
|
|
990
1051
|
| { kind: "prices-lookup"; modelId: string }
|
|
991
1052
|
| { kind: "entry"; entry: string; args: string[] }
|
|
1053
|
+
| { kind: "install"; args: string[] }
|
|
992
1054
|
| { kind: "unknown"; cmd: string };
|
|
993
1055
|
|
|
994
1056
|
export function route(args: string[]): Action {
|
|
@@ -1107,7 +1169,7 @@ export function route(args: string[]): Action {
|
|
|
1107
1169
|
case "init":
|
|
1108
1170
|
return { kind: "entry", entry: "init-project", args: args.slice(1) };
|
|
1109
1171
|
case "install":
|
|
1110
|
-
return { kind: "
|
|
1172
|
+
return { kind: "install", args: args.slice(1) };
|
|
1111
1173
|
// why: the exit has to be as easy to find as the entrance. An operator who cannot get the harness off their
|
|
1112
1174
|
// machine without hand-editing settings.json will not try it on a second one
|
|
1113
1175
|
// ([/decisions/ad-066.md](/decisions/ad-066.md)).
|
|
@@ -1293,9 +1355,15 @@ function runUpdate(root: string): never {
|
|
|
1293
1355
|
console.error(npmUpdateFailureMessage());
|
|
1294
1356
|
process.exit(bump.status ?? 1);
|
|
1295
1357
|
}
|
|
1296
|
-
const
|
|
1358
|
+
const packageRoot = globalPackageRoot();
|
|
1359
|
+
if (packageRoot === null) {
|
|
1360
|
+
console.error(npmRootFailureMessage(home));
|
|
1361
|
+
process.exit(1);
|
|
1362
|
+
}
|
|
1363
|
+
const plan = npmSyncPlan(packageRoot);
|
|
1364
|
+
const sync = spawnSync(plan.command, plan.args, {
|
|
1297
1365
|
stdio: "inherit",
|
|
1298
|
-
env: process.env,
|
|
1366
|
+
env: { ...process.env, ...plan.env },
|
|
1299
1367
|
});
|
|
1300
1368
|
if ((sync.status ?? 1) !== 0) {
|
|
1301
1369
|
process.exit(sync.status ?? 1);
|
|
@@ -1390,6 +1458,31 @@ function runEntry(entry: string, toolArgs: string[], root: string): never {
|
|
|
1390
1458
|
process.exit(r.status ?? 1);
|
|
1391
1459
|
}
|
|
1392
1460
|
|
|
1461
|
+
/**
|
|
1462
|
+
* `install`, which is the only entry that must run from the *package* rather than from the runtime it is about to
|
|
1463
|
+
* replace.
|
|
1464
|
+
*
|
|
1465
|
+
* hazard: it went through `runEntry`, so the runtime home's own launcher ran the runtime home's own
|
|
1466
|
+
* `install-runtime`, whose source and destination then resolved to the same directory. Measured: package at 0.3.3,
|
|
1467
|
+
* runtime left on 0.3.1, with the command reporting success — and this is the recovery route the README and every
|
|
1468
|
+
* failure message name ([/decisions/ad-098.md](/decisions/ad-098.md)).
|
|
1469
|
+
*
|
|
1470
|
+
* invariant: `--link` stays local. It points the runtime at the checkout the operator is standing in, so its
|
|
1471
|
+
* source is the working directory and never the package.
|
|
1472
|
+
*/
|
|
1473
|
+
function runInstall(toolArgs: string[], root: string): never {
|
|
1474
|
+
const packageRoot = toolArgs.includes("--link") ? null : globalPackageRoot();
|
|
1475
|
+
if (packageRoot === null) {
|
|
1476
|
+
runEntry("install-runtime", toolArgs, root);
|
|
1477
|
+
}
|
|
1478
|
+
const plan = npmSyncPlan(packageRoot);
|
|
1479
|
+
const r = spawnSync(plan.command, [...plan.args, ...toolArgs], {
|
|
1480
|
+
stdio: "inherit",
|
|
1481
|
+
env: { ...process.env, ...plan.env, TLC_PROJECT_DIR: root },
|
|
1482
|
+
});
|
|
1483
|
+
process.exit(r.status ?? 1);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1393
1486
|
function main(argv: string[]): void {
|
|
1394
1487
|
const root = resolveProjectRoot();
|
|
1395
1488
|
const group = (argv[0] ?? "").toLowerCase();
|
|
@@ -1553,6 +1646,9 @@ function main(argv: string[]): void {
|
|
|
1553
1646
|
case "prices-lookup":
|
|
1554
1647
|
runEntry("price-lookup", json ? [action.modelId, JSON_FLAG] : [action.modelId], root);
|
|
1555
1648
|
break;
|
|
1649
|
+
case "install":
|
|
1650
|
+
runInstall(action.args, root);
|
|
1651
|
+
break;
|
|
1556
1652
|
case "entry":
|
|
1557
1653
|
runEntry(action.entry, json ? [...action.args, JSON_FLAG] : action.args, root);
|
|
1558
1654
|
break;
|