@tech-leads-club/harness-toolkit 0.5.1 → 0.6.0
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/generate-schema.ts +67 -0
- package/bin/tlc-build.mjs +26 -1
- package/config.example.json +1 -0
- package/dist/compact-before.mjs +83 -83
- package/dist/doctor.mjs +79 -79
- package/dist/init-project.mjs +87 -87
- package/dist/install-runtime.mjs +82 -82
- package/dist/lessons-cli.mjs +86 -86
- package/dist/obs-cli.mjs +79 -79
- package/dist/prompt-submit.mjs +83 -83
- package/dist/refresh-model-prices.mjs +82 -82
- package/dist/response-after.mjs +83 -83
- package/dist/run.mjs +83 -83
- package/dist/session-end.mjs +89 -89
- package/dist/session-start.mjs +91 -91
- package/dist/shim.mjs +81 -81
- package/dist/stop.mjs +89 -89
- package/dist/subagent-start.mjs +83 -83
- package/dist/subagent-stop.mjs +84 -84
- package/dist/support.mjs +87 -87
- package/dist/tlc-cli.mjs +100 -100
- package/dist/tool-after.mjs +83 -83
- package/dist/tool-before.mjs +83 -83
- package/dist/tool-failure.mjs +83 -83
- package/dist/uninstall-runtime.mjs +3 -3
- package/package.json +4 -2
- package/schema.json +545 -0
- package/src/core/capability/capability.store.ts +27 -1
- package/src/core/comment-policy/comment-policy.service.ts +3 -8
- package/src/core/core.facade.ts +5 -1
- package/src/core/policy/policy.loader.ts +5 -1
- package/src/core/policy/policy.shadow.ts +68 -2
- package/src/entrypoints/stop.ts +5 -7
- package/tools/doctor.ts +48 -0
- package/tools/init-project.ts +19 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import * as TJS from "typescript-json-schema";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* why: `PartialPolicy`, not `Policy` — a human-written config is always partial, and `Policy`'s every
|
|
7
|
+
* field being non-optional would mark a minimal config invalid in every editor.
|
|
8
|
+
*/
|
|
9
|
+
const ROOT_TYPE = "PartialPolicy";
|
|
10
|
+
const SOURCE_FILE = "src/core/policy/policy.types.ts";
|
|
11
|
+
|
|
12
|
+
function compilerOptionsFrom(root: string): Record<string, unknown> {
|
|
13
|
+
const raw = JSON.parse(readFileSync(join(root, "tsconfig.json"), "utf8")) as {
|
|
14
|
+
compilerOptions: Record<string, unknown>;
|
|
15
|
+
};
|
|
16
|
+
return raw.compilerOptions;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* invariant: `noExtraProps` is what makes an unknown key (the `format` class of bug) a schema
|
|
21
|
+
* violation instead of something the schema silently accepts.
|
|
22
|
+
*/
|
|
23
|
+
export function generateConfigSchema(rawRoot: string): Record<string, unknown> {
|
|
24
|
+
// why: TypeScript prints an import() type query against its own canonicalised path — a `.` segment
|
|
25
|
+
// or backslashes in the caller's spelling would silently defeat redactBuildPath's string match below.
|
|
26
|
+
const root = resolve(rawRoot);
|
|
27
|
+
const program = TJS.getProgramFromFiles([join(root, SOURCE_FILE)], compilerOptionsFrom(root), root);
|
|
28
|
+
const schema = TJS.generateSchema(program, ROOT_TYPE, {
|
|
29
|
+
required: true,
|
|
30
|
+
noExtraProps: true,
|
|
31
|
+
strictNullChecks: true,
|
|
32
|
+
});
|
|
33
|
+
if (schema === null) {
|
|
34
|
+
throw new Error(`typescript-json-schema produced no schema for ${ROOT_TYPE} in ${SOURCE_FILE}`);
|
|
35
|
+
}
|
|
36
|
+
const properties = (schema as { properties?: Record<string, unknown> }).properties ?? {};
|
|
37
|
+
const withSchemaProp = {
|
|
38
|
+
...schema,
|
|
39
|
+
properties: {
|
|
40
|
+
// why: a JSON Schema meta-key, not a PartialPolicy field — typescript-json-schema never emits
|
|
41
|
+
// it, and `noExtraProps` would otherwise make every real config with a `$schema` line invalid.
|
|
42
|
+
$schema: { type: "string" },
|
|
43
|
+
...properties,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
return redactBuildPath(withSchemaProp, root);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* hazard: a field typed `Partial<Policy["grind"]>` (an indexed-access type, not its own named alias)
|
|
51
|
+
* has no clean name to give its `$ref`, so the generator falls back to printing the full structural
|
|
52
|
+
* type — including an `import("<absolute path>")` type query for every cross-file reference inside
|
|
53
|
+
* it. That absolute path is the machine that ran the build, encoded twice: once raw in `$ref` targets
|
|
54
|
+
* that are also definition keys, once URI-percent-encoded in the `$ref` string itself. Published
|
|
55
|
+
* as-is, it would leak the CI runner's (or a contributor's) filesystem layout into a public schema.
|
|
56
|
+
*/
|
|
57
|
+
// why: TypeScript always prints module specifiers with forward slashes, regardless of host OS — on
|
|
58
|
+
// Windows `root` itself is backslash-separated, so the raw string alone never matches what got printed.
|
|
59
|
+
function redactBuildPath(schema: Record<string, unknown>, root: string): Record<string, unknown> {
|
|
60
|
+
const posixRoot = root.replace(/\\/g, "/");
|
|
61
|
+
const candidates = [root, posixRoot, encodeURIComponent(root), encodeURIComponent(posixRoot)];
|
|
62
|
+
let serialized = JSON.stringify(schema);
|
|
63
|
+
for (const candidate of candidates) {
|
|
64
|
+
serialized = serialized.split(candidate).join(".");
|
|
65
|
+
}
|
|
66
|
+
return JSON.parse(serialized) as Record<string, unknown>;
|
|
67
|
+
}
|
package/bin/tlc-build.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* entrypoint, and the missing bundle only surfaces when a hook fires on somebody's machine.
|
|
17
17
|
*/
|
|
18
18
|
import { spawnSync } from "node:child_process";
|
|
19
|
-
import { chmodSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
19
|
+
import { chmodSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
20
20
|
import { basename, dirname, join } from "node:path";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
|
|
@@ -115,3 +115,28 @@ for (const entry of readdirSync(dist, { withFileTypes: true })) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
console.log(`tlc-build: ok (${bundles} bundles)`);
|
|
118
|
+
|
|
119
|
+
// why: dynamic, and a missing module degrades rather than fails — typescript-json-schema is a
|
|
120
|
+
// devDependency, absent from a plain install, and the dist rebuild above is this script's real
|
|
121
|
+
// recovery contract (bin/tlc-exec.mjs's "dist missing, run tlc-build.mjs" message). Never committed
|
|
122
|
+
// (.gitignore) either way, so nothing here needs the freshness gate `dist/` no longer has.
|
|
123
|
+
let generateConfigSchema;
|
|
124
|
+
try {
|
|
125
|
+
({ generateConfigSchema } = await import("./generate-schema.ts"));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error?.code !== "ERR_MODULE_NOT_FOUND") {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
console.log("tlc-build: schema.json skipped — typescript-json-schema is not installed here");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (generateConfigSchema) {
|
|
134
|
+
try {
|
|
135
|
+
const schema = generateConfigSchema(root);
|
|
136
|
+
writeFileSync(join(root, "schema.json"), `${JSON.stringify(schema, null, 2)}\n`);
|
|
137
|
+
console.log("tlc-build: schema.json ok");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(`tlc-build: schema.json generation failed — ${error instanceof Error ? error.message : error}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|