@tech-leads-club/harness-toolkit 0.3.2 → 0.3.3
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 +63 -2
- 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 +8235 -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/dist/init-project.mjs
CHANGED
|
@@ -1,27 +1,784 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import"
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// tools/init-project.ts
|
|
5
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6
|
+
import { dirname as dirname3, join as join4, sep } from "node:path";
|
|
7
|
+
|
|
8
|
+
// bin/write-user-hooks.mjs
|
|
9
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
11
|
+
|
|
12
|
+
// src/providers/claude/claude.wiring.ts
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { dirname, join as join2 } from "node:path";
|
|
15
|
+
|
|
16
|
+
// src/platform/paths.ts
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
function harnessDir(root) {
|
|
20
|
+
return join(root, ".tlc", "harness");
|
|
21
|
+
}
|
|
22
|
+
function conventionalRuntimeHome() {
|
|
23
|
+
return join(homedir(), ".tlc", "harness");
|
|
24
|
+
}
|
|
25
|
+
function runtimeHome(env = process.env) {
|
|
26
|
+
return env.TLC_HOME ?? conventionalRuntimeHome();
|
|
27
|
+
}
|
|
28
|
+
function projectConfigPath(root) {
|
|
29
|
+
return join(harnessDir(root), "config.json");
|
|
30
|
+
}
|
|
31
|
+
function claudeConfigDir() {
|
|
32
|
+
const custom = process.env.CLAUDE_CONFIG_DIR?.trim();
|
|
33
|
+
return custom && custom.length > 0 ? custom : join(homedir(), ".claude");
|
|
34
|
+
}
|
|
35
|
+
function cursorConfigDir() {
|
|
36
|
+
const custom = process.env.CURSOR_CONFIG_DIR?.trim();
|
|
37
|
+
return custom && custom.length > 0 ? custom : join(homedir(), ".cursor");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/providers/claude/claude.wiring.ts
|
|
41
|
+
function isPlainRecord(value) {
|
|
42
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function isHooksRecord(value) {
|
|
45
|
+
return isPlainRecord(value);
|
|
46
|
+
}
|
|
47
|
+
function deepEqual(a, b) {
|
|
48
|
+
if (a === b) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
52
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
56
|
+
}
|
|
57
|
+
if (isPlainRecord(a) && isPlainRecord(b)) {
|
|
58
|
+
const aKeys = Object.keys(a);
|
|
59
|
+
const bKeys = Object.keys(b);
|
|
60
|
+
if (aKeys.length !== bKeys.length) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return aKeys.every((key) => bKeys.includes(key) && deepEqual(a[key], b[key]));
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function desiredHooksFor(entries) {
|
|
68
|
+
const hooks = {};
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const group = {
|
|
71
|
+
hooks: [{ type: "command", command: entry.command, args: entry.args }]
|
|
72
|
+
};
|
|
73
|
+
hooks[entry.hookEvent] = [...hooks[entry.hookEvent] ?? [], group];
|
|
74
|
+
}
|
|
75
|
+
return hooks;
|
|
76
|
+
}
|
|
77
|
+
var LAUNCHER_MARKER = "tlc-exec.mjs";
|
|
78
|
+
function isHarnessGroup(group) {
|
|
79
|
+
return JSON.stringify(group ?? null).includes(LAUNCHER_MARKER);
|
|
80
|
+
}
|
|
81
|
+
function canonicalLauncherPath(path, resolve = realpathSync) {
|
|
82
|
+
try {
|
|
83
|
+
return resolve(path);
|
|
84
|
+
} catch {
|
|
85
|
+
return path;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function canonicalizeGroups(groups, resolve) {
|
|
89
|
+
return JSON.parse(JSON.stringify(groups ?? null, (_key, value) => typeof value === "string" && value.includes(LAUNCHER_MARKER) ? canonicalLauncherPath(value, resolve) : value));
|
|
90
|
+
}
|
|
91
|
+
function mergeClaudeSettings(existingText, entries) {
|
|
92
|
+
const desired = desiredHooksFor(entries);
|
|
93
|
+
let settings = {};
|
|
94
|
+
if (existingText !== null && existingText.trim() !== "") {
|
|
95
|
+
let parsed;
|
|
96
|
+
try {
|
|
97
|
+
parsed = JSON.parse(existingText);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
return { ok: false, error: message, block: JSON.stringify({ hooks: desired }, null, 2) };
|
|
101
|
+
}
|
|
102
|
+
if (!isPlainRecord(parsed)) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
error: "settings.json root is not a JSON object",
|
|
106
|
+
block: JSON.stringify({ hooks: desired }, null, 2)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
settings = parsed;
|
|
110
|
+
}
|
|
111
|
+
const currentHooks = isHooksRecord(settings.hooks) ? settings.hooks : {};
|
|
112
|
+
const mergedHooks = { ...currentHooks };
|
|
113
|
+
let changed = false;
|
|
114
|
+
for (const [hookEvent, groups] of Object.entries(desired)) {
|
|
115
|
+
const existingGroups = mergedHooks[hookEvent] ?? [];
|
|
116
|
+
const foreign = existingGroups.filter((group) => !isHarnessGroup(group));
|
|
117
|
+
const nextGroups = [...foreign, ...groups];
|
|
118
|
+
if (!deepEqual(canonicalizeGroups(existingGroups), canonicalizeGroups(nextGroups))) {
|
|
119
|
+
changed = true;
|
|
120
|
+
}
|
|
121
|
+
mergedHooks[hookEvent] = nextGroups;
|
|
122
|
+
}
|
|
123
|
+
const mergedSettings = { ...settings, hooks: mergedHooks };
|
|
124
|
+
return { ok: true, settingsText: JSON.stringify(mergedSettings, null, 2), changed };
|
|
125
|
+
}
|
|
126
|
+
function applyClaudeWiring(settingsPath, entries) {
|
|
127
|
+
const existingText = existsSync(settingsPath) ? readFileSync(settingsPath, "utf8") : null;
|
|
128
|
+
const result = mergeClaudeSettings(existingText, entries);
|
|
129
|
+
if (result.ok && result.changed) {
|
|
130
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
131
|
+
writeFileSync(settingsPath, result.settingsText, "utf8");
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
// src/providers/provider.degrade.ts
|
|
136
|
+
var NO_HUMAN_MODES = new Set(["bypassPermissions", "dontAsk"]);
|
|
137
|
+
// bin/write-user-hooks.mjs
|
|
138
|
+
var CURSOR_MARKER = "tlc-exec.mjs";
|
|
139
|
+
function quoteIfNeeded(token) {
|
|
140
|
+
return token.includes(" ") ? `"${token}"` : token;
|
|
141
|
+
}
|
|
142
|
+
function commandStringFor(entry) {
|
|
143
|
+
return [entry.command, ...entry.args].map(quoteIfNeeded).join(" ");
|
|
144
|
+
}
|
|
145
|
+
function renderCursorHooksDocument(entries) {
|
|
146
|
+
const hooks = {};
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const rendered = { command: commandStringFor(entry), timeout: entry.timeoutSeconds };
|
|
149
|
+
if (entry.failClosed) {
|
|
150
|
+
rendered.failClosed = true;
|
|
151
|
+
}
|
|
152
|
+
if (entry.matcher !== undefined) {
|
|
153
|
+
rendered.matcher = entry.matcher;
|
|
154
|
+
}
|
|
155
|
+
if (entry.loopLimit !== undefined) {
|
|
156
|
+
rendered.loop_limit = entry.loopLimit;
|
|
157
|
+
}
|
|
158
|
+
hooks[entry.hookEvent] = [...hooks[entry.hookEvent] ?? [], rendered];
|
|
159
|
+
}
|
|
160
|
+
return { version: 1, hooks };
|
|
161
|
+
}
|
|
162
|
+
function isCursorWired(targetPath) {
|
|
163
|
+
return existsSync2(targetPath) && readFileSync2(targetPath, "utf8").includes(CURSOR_MARKER);
|
|
164
|
+
}
|
|
165
|
+
function applyCursorWiring(wiring, { force = false } = {}) {
|
|
166
|
+
const targetPath = wiring.target;
|
|
167
|
+
const document = renderCursorHooksDocument(wiring.entries);
|
|
168
|
+
const rendered = `${JSON.stringify(document, null, 2)}
|
|
169
|
+
`;
|
|
170
|
+
if (existsSync2(targetPath) && !force) {
|
|
171
|
+
if (isCursorWired(targetPath)) {
|
|
172
|
+
return { status: "unchanged", target: targetPath };
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
status: "refused",
|
|
176
|
+
target: targetPath,
|
|
177
|
+
reason: `${targetPath} exists without harness entries — rerun with --force to overwrite, or merge manually.`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
mkdirSync2(dirname2(targetPath), { recursive: true });
|
|
181
|
+
writeFileSync2(targetPath, rendered);
|
|
182
|
+
return { status: "written", target: targetPath };
|
|
183
|
+
}
|
|
184
|
+
if (false) {}
|
|
185
|
+
|
|
186
|
+
// src/core/comment-policy/comment-syntax.catalog.ts
|
|
187
|
+
var COMMENT_SYNTAX = [
|
|
188
|
+
{
|
|
189
|
+
id: "typescript",
|
|
190
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
|
|
191
|
+
line: ["//"],
|
|
192
|
+
block: [["/*", "*/"]],
|
|
193
|
+
middle: ["*"]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: "python",
|
|
197
|
+
extensions: [".py", ".pyi", ".pyw"],
|
|
198
|
+
line: ["#"],
|
|
199
|
+
block: [
|
|
200
|
+
['"""', '"""'],
|
|
201
|
+
["'''", "'''"]
|
|
202
|
+
],
|
|
203
|
+
middle: []
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "ruby",
|
|
207
|
+
extensions: [".rb", ".rake", ".gemspec"],
|
|
208
|
+
line: ["#"],
|
|
209
|
+
block: [["=begin", "=end"]],
|
|
210
|
+
middle: []
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: "shell",
|
|
214
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh", ".fish"],
|
|
215
|
+
line: ["#"],
|
|
216
|
+
block: [],
|
|
217
|
+
middle: []
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: "go",
|
|
221
|
+
extensions: [".go"],
|
|
222
|
+
line: ["//"],
|
|
223
|
+
block: [["/*", "*/"]],
|
|
224
|
+
middle: ["*"]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "rust",
|
|
228
|
+
extensions: [".rs"],
|
|
229
|
+
line: ["//"],
|
|
230
|
+
block: [["/*", "*/"]],
|
|
231
|
+
middle: ["*"]
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "java",
|
|
235
|
+
extensions: [".java"],
|
|
236
|
+
line: ["//"],
|
|
237
|
+
block: [["/*", "*/"]],
|
|
238
|
+
middle: ["*"]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: "kotlin",
|
|
242
|
+
extensions: [".kt", ".kts"],
|
|
243
|
+
line: ["//"],
|
|
244
|
+
block: [["/*", "*/"]],
|
|
245
|
+
middle: ["*"]
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "swift",
|
|
249
|
+
extensions: [".swift"],
|
|
250
|
+
line: ["//"],
|
|
251
|
+
block: [["/*", "*/"]],
|
|
252
|
+
middle: ["*"]
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: "c",
|
|
256
|
+
extensions: [".c", ".h"],
|
|
257
|
+
line: ["//"],
|
|
258
|
+
block: [["/*", "*/"]],
|
|
259
|
+
middle: ["*"]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "cpp",
|
|
263
|
+
extensions: [".cc", ".cpp", ".cxx", ".hpp", ".hh", ".hxx"],
|
|
264
|
+
line: ["//"],
|
|
265
|
+
block: [["/*", "*/"]],
|
|
266
|
+
middle: ["*"]
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
id: "csharp",
|
|
270
|
+
extensions: [".cs"],
|
|
271
|
+
line: ["//"],
|
|
272
|
+
block: [["/*", "*/"]],
|
|
273
|
+
middle: ["*"]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: "php",
|
|
277
|
+
extensions: [".php"],
|
|
278
|
+
line: ["//", "#"],
|
|
279
|
+
block: [["/*", "*/"]],
|
|
280
|
+
middle: ["*"]
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
id: "scala",
|
|
284
|
+
extensions: [".scala", ".sc"],
|
|
285
|
+
line: ["//"],
|
|
286
|
+
block: [["/*", "*/"]],
|
|
287
|
+
middle: ["*"]
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: "dart",
|
|
291
|
+
extensions: [".dart"],
|
|
292
|
+
line: ["//"],
|
|
293
|
+
block: [["/*", "*/"]],
|
|
294
|
+
middle: ["*"]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
id: "elixir",
|
|
298
|
+
extensions: [".ex", ".exs"],
|
|
299
|
+
line: ["#"],
|
|
300
|
+
block: [],
|
|
301
|
+
middle: []
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "erlang",
|
|
305
|
+
extensions: [".erl", ".hrl"],
|
|
306
|
+
line: ["%"],
|
|
307
|
+
block: [],
|
|
308
|
+
middle: []
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "haskell",
|
|
312
|
+
extensions: [".hs"],
|
|
313
|
+
line: ["--"],
|
|
314
|
+
block: [["{-", "-}"]],
|
|
315
|
+
middle: []
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "lua",
|
|
319
|
+
extensions: [".lua"],
|
|
320
|
+
line: ["--"],
|
|
321
|
+
block: [["--[[", "]]"]],
|
|
322
|
+
middle: []
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "sql",
|
|
326
|
+
extensions: [".sql"],
|
|
327
|
+
line: ["--"],
|
|
328
|
+
block: [["/*", "*/"]],
|
|
329
|
+
middle: ["*"]
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: "css",
|
|
333
|
+
extensions: [".css", ".scss", ".sass", ".less"],
|
|
334
|
+
line: ["//"],
|
|
335
|
+
block: [["/*", "*/"]],
|
|
336
|
+
middle: ["*"]
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "yaml",
|
|
340
|
+
extensions: [".yaml", ".yml"],
|
|
341
|
+
line: ["#"],
|
|
342
|
+
block: [],
|
|
343
|
+
middle: []
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
id: "toml",
|
|
347
|
+
extensions: [".toml"],
|
|
348
|
+
line: ["#"],
|
|
349
|
+
block: [],
|
|
350
|
+
middle: []
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: "ini",
|
|
354
|
+
extensions: [".ini", ".cfg", ".conf", ".properties"],
|
|
355
|
+
line: [";", "#"],
|
|
356
|
+
block: [],
|
|
357
|
+
middle: []
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: "dockerfile",
|
|
361
|
+
extensions: [".dockerfile", "dockerfile"],
|
|
362
|
+
line: ["#"],
|
|
363
|
+
block: [],
|
|
364
|
+
middle: []
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: "makefile",
|
|
368
|
+
extensions: [".mk", "makefile"],
|
|
369
|
+
line: ["#"],
|
|
370
|
+
block: [],
|
|
371
|
+
middle: []
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "terraform",
|
|
375
|
+
extensions: [".tf", ".tfvars"],
|
|
376
|
+
line: ["#", "//"],
|
|
377
|
+
block: [["/*", "*/"]],
|
|
378
|
+
middle: ["*"]
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: "powershell",
|
|
382
|
+
extensions: [".ps1", ".psm1", ".psd1"],
|
|
383
|
+
line: ["#"],
|
|
384
|
+
block: [["<#", "#>"]],
|
|
385
|
+
middle: []
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: "perl",
|
|
389
|
+
extensions: [".pl", ".pm"],
|
|
390
|
+
line: ["#"],
|
|
391
|
+
block: [],
|
|
392
|
+
middle: []
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "r",
|
|
396
|
+
extensions: [".r"],
|
|
397
|
+
line: ["#"],
|
|
398
|
+
block: [],
|
|
399
|
+
middle: []
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: "julia",
|
|
403
|
+
extensions: [".jl"],
|
|
404
|
+
line: ["#"],
|
|
405
|
+
block: [["#=", "=#"]],
|
|
406
|
+
middle: []
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
id: "vue",
|
|
410
|
+
extensions: [".vue", ".svelte"],
|
|
411
|
+
line: ["//"],
|
|
412
|
+
block: [
|
|
413
|
+
["/*", "*/"],
|
|
414
|
+
["<!--", "-->"]
|
|
415
|
+
],
|
|
416
|
+
middle: ["*"]
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
id: "html",
|
|
420
|
+
extensions: [".html", ".htm", ".xml", ".xhtml"],
|
|
421
|
+
line: [],
|
|
422
|
+
block: [["<!--", "-->"]],
|
|
423
|
+
middle: []
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: "graphql",
|
|
427
|
+
extensions: [".graphql", ".gql"],
|
|
428
|
+
line: ["#"],
|
|
429
|
+
block: [],
|
|
430
|
+
middle: []
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
id: "protobuf",
|
|
434
|
+
extensions: [".proto"],
|
|
435
|
+
line: ["//"],
|
|
436
|
+
block: [["/*", "*/"]],
|
|
437
|
+
middle: ["*"]
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
id: "zig",
|
|
441
|
+
extensions: [".zig"],
|
|
442
|
+
line: ["//"],
|
|
443
|
+
block: [],
|
|
444
|
+
middle: []
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
id: "clojure",
|
|
448
|
+
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
|
449
|
+
line: [";"],
|
|
450
|
+
block: [],
|
|
451
|
+
middle: []
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
id: "ocaml",
|
|
455
|
+
extensions: [".ml", ".mli"],
|
|
456
|
+
line: [],
|
|
457
|
+
block: [["(*", "*)"]],
|
|
458
|
+
middle: ["*"]
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
id: "fsharp",
|
|
462
|
+
extensions: [".fs", ".fsi", ".fsx"],
|
|
463
|
+
line: ["//"],
|
|
464
|
+
block: [["(*", "*)"]],
|
|
465
|
+
middle: ["*"]
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: "vim",
|
|
469
|
+
extensions: [".vim"],
|
|
470
|
+
line: ['"'],
|
|
471
|
+
block: [],
|
|
472
|
+
middle: []
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
id: "tex",
|
|
476
|
+
extensions: [".tex", ".sty", ".cls"],
|
|
477
|
+
line: ["%"],
|
|
478
|
+
block: [],
|
|
479
|
+
middle: []
|
|
480
|
+
}
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
// src/core/comment-policy/comment-syntax.store.ts
|
|
484
|
+
function buildIndex(entries) {
|
|
485
|
+
const byKey = new Map;
|
|
486
|
+
for (const entry of entries) {
|
|
487
|
+
const syntax = { line: entry.line, block: entry.block, middle: entry.middle };
|
|
488
|
+
for (const extension of entry.extensions) {
|
|
489
|
+
byKey.set(extension.toLowerCase(), syntax);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return byKey;
|
|
493
|
+
}
|
|
494
|
+
var INDEX = buildIndex(COMMENT_SYNTAX);
|
|
495
|
+
var KNOWN_EXTENSION_COUNT = INDEX.size;
|
|
496
|
+
|
|
497
|
+
// src/core/comment-policy/comment-policy.service.ts
|
|
498
|
+
var STOPWORDS = new Set([
|
|
499
|
+
"a",
|
|
500
|
+
"an",
|
|
501
|
+
"and",
|
|
502
|
+
"are",
|
|
503
|
+
"as",
|
|
504
|
+
"at",
|
|
505
|
+
"be",
|
|
506
|
+
"by",
|
|
507
|
+
"for",
|
|
508
|
+
"from",
|
|
509
|
+
"get",
|
|
510
|
+
"gets",
|
|
511
|
+
"has",
|
|
512
|
+
"in",
|
|
513
|
+
"into",
|
|
514
|
+
"is",
|
|
515
|
+
"it",
|
|
516
|
+
"its",
|
|
517
|
+
"of",
|
|
518
|
+
"on",
|
|
519
|
+
"or",
|
|
520
|
+
"return",
|
|
521
|
+
"returns",
|
|
522
|
+
"set",
|
|
523
|
+
"sets",
|
|
524
|
+
"that",
|
|
525
|
+
"the",
|
|
526
|
+
"then",
|
|
527
|
+
"this",
|
|
528
|
+
"to",
|
|
529
|
+
"true",
|
|
530
|
+
"when",
|
|
531
|
+
"which",
|
|
532
|
+
"with"
|
|
533
|
+
]);
|
|
534
|
+
|
|
535
|
+
// src/core/duplication/duplication.service.ts
|
|
536
|
+
var MIN_RUN = 6;
|
|
537
|
+
|
|
538
|
+
// src/core/observability/observability.types.ts
|
|
539
|
+
var DEFAULT_OBS = {
|
|
540
|
+
enabled: true,
|
|
541
|
+
signalPath: "obs.jsonl",
|
|
542
|
+
debugPath: "debug.jsonl",
|
|
543
|
+
debugEnabled: false,
|
|
544
|
+
includePayloads: false,
|
|
545
|
+
maxAttrChars: 500,
|
|
546
|
+
sessionCostAlertUsd: 5,
|
|
547
|
+
retentionDays: 14,
|
|
548
|
+
maxSignalEvents: 50000,
|
|
549
|
+
globalSpool: false
|
|
550
|
+
};
|
|
551
|
+
var SIGNAL_KINDS = new Set([
|
|
552
|
+
"session.start",
|
|
553
|
+
"session.end",
|
|
554
|
+
"generation.end",
|
|
555
|
+
"tool.fail",
|
|
556
|
+
"subagent.start",
|
|
557
|
+
"subagent.end",
|
|
558
|
+
"prompt.submit",
|
|
559
|
+
"compact",
|
|
560
|
+
"gate.outcome",
|
|
561
|
+
"cost.turn",
|
|
562
|
+
"cost.session_alert",
|
|
563
|
+
"ship.claim",
|
|
564
|
+
"policy.deny",
|
|
565
|
+
"policy.observe"
|
|
566
|
+
]);
|
|
567
|
+
var LIVE_ALLOWLIST = new Set([
|
|
568
|
+
"session.start",
|
|
569
|
+
"session.end",
|
|
570
|
+
"generation.end",
|
|
571
|
+
"tool.fail",
|
|
572
|
+
"shell.end",
|
|
573
|
+
"subagent.start",
|
|
574
|
+
"subagent.end",
|
|
575
|
+
"gate.outcome",
|
|
576
|
+
"cost.turn",
|
|
577
|
+
"cost.session_alert",
|
|
578
|
+
"ship.claim",
|
|
579
|
+
"policy.deny",
|
|
580
|
+
"compact",
|
|
581
|
+
"prompt.submit"
|
|
582
|
+
]);
|
|
583
|
+
|
|
584
|
+
// src/core/policy/policy.defaults.ts
|
|
585
|
+
var DEFAULT_LESSONS_POLICY = {
|
|
586
|
+
enabled: false,
|
|
587
|
+
maxInjectSession: 5,
|
|
588
|
+
maxInjectRetry: 8,
|
|
589
|
+
maxCharsSession: 900,
|
|
590
|
+
maxCharsRetry: 1400,
|
|
591
|
+
promoteHitCount: 2,
|
|
592
|
+
decayLambda: 0.02,
|
|
593
|
+
projectBoost: 1.5,
|
|
594
|
+
syncRulesFile: "auto",
|
|
595
|
+
gardenOnSessionEnd: true
|
|
596
|
+
};
|
|
597
|
+
var DEFAULTS = {
|
|
598
|
+
version: 1,
|
|
599
|
+
mode: "solo",
|
|
600
|
+
codePaths: ["src", "apps", "libs", "packages"],
|
|
601
|
+
grind: {
|
|
602
|
+
enabled: false,
|
|
603
|
+
maxLoops: 5,
|
|
604
|
+
lintCommand: null,
|
|
605
|
+
testCommand: null,
|
|
606
|
+
appendFiles: "auto"
|
|
607
|
+
},
|
|
608
|
+
shipGate: {
|
|
609
|
+
enabled: false,
|
|
610
|
+
runtimePathPrefixes: ["src", "apps", "libs", "packages", "deploy", "scripts"],
|
|
611
|
+
runtimePathExcludes: [".tlc/", "**/node_modules/", "**/.git/"],
|
|
612
|
+
evidenceDir: null,
|
|
613
|
+
evidenceMaxAgeHours: 48,
|
|
614
|
+
emptyDiffAntiShip: false,
|
|
615
|
+
claimWindowMinutes: 10
|
|
616
|
+
},
|
|
617
|
+
subagents: {
|
|
618
|
+
enforceAllowlist: false,
|
|
619
|
+
requireModel: false,
|
|
620
|
+
allowedModels: [],
|
|
621
|
+
blockedPatterns: ["-fast(?:$|[^a-z0-9])", "/fast(?:$|[^a-z0-9])"],
|
|
622
|
+
minEffort: null,
|
|
623
|
+
blockParentFast: false,
|
|
624
|
+
blockMode: "deny",
|
|
625
|
+
readOnlyTypes: ["explore"]
|
|
626
|
+
},
|
|
627
|
+
docs: {
|
|
628
|
+
command: null,
|
|
629
|
+
severity: "warn"
|
|
630
|
+
},
|
|
631
|
+
observe: {
|
|
632
|
+
enabled: false,
|
|
633
|
+
rails: []
|
|
634
|
+
},
|
|
635
|
+
comments: {
|
|
636
|
+
enabled: false,
|
|
637
|
+
onViolation: "followup",
|
|
638
|
+
mode: "declared"
|
|
639
|
+
},
|
|
640
|
+
supplyChain: {
|
|
641
|
+
enabled: false
|
|
642
|
+
},
|
|
643
|
+
duplication: {
|
|
644
|
+
enabled: false,
|
|
645
|
+
minRun: MIN_RUN
|
|
646
|
+
},
|
|
647
|
+
obs: {
|
|
648
|
+
globalSpool: false,
|
|
649
|
+
includePayloads: DEFAULT_OBS.includePayloads,
|
|
650
|
+
maxAttrChars: DEFAULT_OBS.maxAttrChars,
|
|
651
|
+
sessionCostAlertUsd: DEFAULT_OBS.sessionCostAlertUsd,
|
|
652
|
+
retentionDays: DEFAULT_OBS.retentionDays
|
|
653
|
+
},
|
|
654
|
+
untrustedContent: {
|
|
655
|
+
mode: "frame",
|
|
656
|
+
enabled: false,
|
|
657
|
+
extraTools: [],
|
|
658
|
+
extraCommandPatterns: []
|
|
659
|
+
},
|
|
660
|
+
planGate: {
|
|
661
|
+
enabled: false,
|
|
662
|
+
windowMinutes: 120
|
|
663
|
+
},
|
|
664
|
+
shell: {
|
|
665
|
+
catastrophicAsk: true,
|
|
666
|
+
stallDetection: false,
|
|
667
|
+
stallRepeatThreshold: 3
|
|
668
|
+
},
|
|
669
|
+
intelligence: {
|
|
670
|
+
gapFeedback: true,
|
|
671
|
+
failureClassification: true,
|
|
672
|
+
progressiveHandoff: true,
|
|
673
|
+
progressiveContext: true,
|
|
674
|
+
autopilot: true,
|
|
675
|
+
idleTurnGate: false,
|
|
676
|
+
budgetContinue: false,
|
|
677
|
+
budgetContinueAfterLoops: 3,
|
|
678
|
+
lessons: { ...DEFAULT_LESSONS_POLICY }
|
|
679
|
+
},
|
|
680
|
+
mcpPrime: [],
|
|
681
|
+
bootstrapExtra: []
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// src/platform/style.ts
|
|
685
|
+
var COLORS = {
|
|
686
|
+
structure: "#3d3a4a",
|
|
687
|
+
accent: "#a78bfa",
|
|
688
|
+
success: "#6ee7b7",
|
|
689
|
+
warning: "#d4a574",
|
|
690
|
+
error: "#f87171",
|
|
691
|
+
info: "#93c5fd",
|
|
692
|
+
textMain: "#f5f5f7",
|
|
693
|
+
textMuted: "#9ca3af",
|
|
694
|
+
textDim: "#6b7280"
|
|
695
|
+
};
|
|
696
|
+
var SYMBOLS = {
|
|
697
|
+
check: "✔",
|
|
698
|
+
cross: "✖",
|
|
699
|
+
warning: "⚠",
|
|
700
|
+
arrow: "→",
|
|
701
|
+
arrowRight: "▸",
|
|
702
|
+
dot: "•",
|
|
703
|
+
bar: "│",
|
|
704
|
+
rule: "══",
|
|
705
|
+
dash: "──"
|
|
706
|
+
};
|
|
707
|
+
function rgb(hex) {
|
|
708
|
+
const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.trim());
|
|
709
|
+
if (!match) {
|
|
710
|
+
return "255;255;255";
|
|
711
|
+
}
|
|
712
|
+
return [match[1], match[2], match[3]].map((part) => Number.parseInt(part, 16)).join(";");
|
|
713
|
+
}
|
|
714
|
+
var ESC = String.fromCharCode(27);
|
|
715
|
+
var RESET = `${ESC}[0m`;
|
|
716
|
+
function colorEnabled(env = process.env, argv = process.argv, isTty = process.stdout.isTTY === true) {
|
|
717
|
+
if ("NO_COLOR" in env) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
if (argv.includes("--no-color")) {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
return isTty;
|
|
724
|
+
}
|
|
725
|
+
var STATUS_COLOR = {
|
|
726
|
+
ok: "success",
|
|
727
|
+
warn: "warning",
|
|
728
|
+
fail: "error",
|
|
729
|
+
info: "info"
|
|
730
|
+
};
|
|
731
|
+
var STATUS_MARK = {
|
|
732
|
+
ok: SYMBOLS.check,
|
|
733
|
+
warn: SYMBOLS.warning,
|
|
734
|
+
fail: SYMBOLS.cross,
|
|
735
|
+
info: SYMBOLS.arrowRight
|
|
736
|
+
};
|
|
737
|
+
var KV_WIDTH = 16;
|
|
738
|
+
function createStyle(enabled = colorEnabled()) {
|
|
739
|
+
const wrap = (code, text) => enabled ? `${ESC}[${code}m${text}${RESET}` : text;
|
|
740
|
+
const paint = (name, text) => wrap(`38;2;${rgb(COLORS[name])}`, text);
|
|
741
|
+
return {
|
|
742
|
+
enabled,
|
|
743
|
+
paint,
|
|
744
|
+
bold: (text) => wrap("1", text),
|
|
745
|
+
dim: (text) => paint("textDim", text),
|
|
746
|
+
heading: (text) => paint("accent", `${SYMBOLS.rule} ${text} ${SYMBOLS.rule}`),
|
|
747
|
+
footer: (text) => paint("textDim", `${SYMBOLS.dash} ${text} ${SYMBOLS.dash}`),
|
|
748
|
+
kv: (label, value, width = KV_WIDTH) => ` ${paint("textMuted", `${label}:`.padEnd(width))} ${value}`,
|
|
749
|
+
status: (level, text) => `${paint(STATUS_COLOR[level], STATUS_MARK[level])} ${text}`
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
var PLAIN = createStyle(false);
|
|
753
|
+
|
|
754
|
+
// src/platform/screen.ts
|
|
755
|
+
function render(screen, style) {
|
|
756
|
+
const out = [style.heading(screen.title.toUpperCase())];
|
|
757
|
+
if (screen.summary && screen.summary.length > 0) {
|
|
758
|
+
out.push(` ${screen.summary.join(style.dim(` ${SYMBOLS.bar} `))}`);
|
|
759
|
+
}
|
|
760
|
+
const width = Math.max(KV_WIDTH, ...screen.sections.flatMap((section) => (section.rows ?? []).map((row) => row.label.length + 1)));
|
|
761
|
+
for (const section of screen.sections) {
|
|
762
|
+
out.push("");
|
|
763
|
+
if (section.title) {
|
|
764
|
+
out.push(style.paint("accent", section.title));
|
|
765
|
+
}
|
|
766
|
+
for (const row of section.rows ?? []) {
|
|
767
|
+
const value = row.level ? style.status(row.level, row.value) : row.value;
|
|
768
|
+
out.push(style.kv(row.label, value, width));
|
|
769
|
+
}
|
|
770
|
+
for (const line of section.lines ?? []) {
|
|
771
|
+
out.push(line === "" ? "" : ` ${line}`);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (screen.footer) {
|
|
775
|
+
out.push("", style.footer(screen.footer));
|
|
776
|
+
}
|
|
777
|
+
return out.join(`
|
|
778
|
+
`);
|
|
779
|
+
}
|
|
21
780
|
|
|
22
781
|
// tools/init-project.ts
|
|
23
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
24
|
-
import { dirname, join, sep } from "node:path";
|
|
25
782
|
class UsageError extends Error {
|
|
26
783
|
}
|
|
27
784
|
function parseFlags(args) {
|
|
@@ -52,7 +809,7 @@ function usageText(style = PLAIN) {
|
|
|
52
809
|
return render(usageScreen(), style);
|
|
53
810
|
}
|
|
54
811
|
function launcherPath(home = runtimeHome()) {
|
|
55
|
-
return
|
|
812
|
+
return join4(home, "bin", "tlc-exec.mjs");
|
|
56
813
|
}
|
|
57
814
|
var SHIM_COMMAND = { command: "node", argsPrefix: [] };
|
|
58
815
|
var CURSOR_SHIM_SPECS = [
|
|
@@ -96,14 +853,14 @@ function claudeShimEntries(launcher) {
|
|
|
96
853
|
...spec.loopLimit !== undefined ? { loopLimit: spec.loopLimit } : {}
|
|
97
854
|
}));
|
|
98
855
|
}
|
|
99
|
-
var PROJECT_SHIMS = [
|
|
856
|
+
var PROJECT_SHIMS = [join4(".cursor", "hooks.json"), join4(".claude", "settings.json")];
|
|
100
857
|
var GITIGNORE_STATE = ".tlc/harness/state/";
|
|
101
858
|
function gitignoreEntries() {
|
|
102
859
|
return [GITIGNORE_STATE, ...PROJECT_SHIMS.map((path) => path.split(sep).join("/"))];
|
|
103
860
|
}
|
|
104
861
|
function mergeGitignore(root) {
|
|
105
|
-
const path =
|
|
106
|
-
const existing =
|
|
862
|
+
const path = join4(root, ".gitignore");
|
|
863
|
+
const existing = existsSync3(path) ? readFileSync3(path, "utf8") : "";
|
|
107
864
|
const lines = existing.split(`
|
|
108
865
|
`);
|
|
109
866
|
const missing = gitignoreEntries().filter((entry) => !lines.includes(entry));
|
|
@@ -125,15 +882,15 @@ function resolvePolicy(root, flags, stdinText) {
|
|
|
125
882
|
}
|
|
126
883
|
return JSON.parse(stdinText);
|
|
127
884
|
}
|
|
128
|
-
if (!flags.minimal && !flags.stdinJson &&
|
|
129
|
-
return JSON.parse(
|
|
885
|
+
if (!flags.minimal && !flags.stdinJson && existsSync3(projectConfigPath(root))) {
|
|
886
|
+
return JSON.parse(readFileSync3(projectConfigPath(root), "utf8"));
|
|
130
887
|
}
|
|
131
888
|
return DEFAULTS;
|
|
132
889
|
}
|
|
133
890
|
function detectProviders(dirs = {}) {
|
|
134
891
|
return {
|
|
135
|
-
cursor:
|
|
136
|
-
claude:
|
|
892
|
+
cursor: existsSync3(dirs.cursor ?? cursorConfigDir()),
|
|
893
|
+
claude: existsSync3(dirs.claude ?? claudeConfigDir())
|
|
137
894
|
};
|
|
138
895
|
}
|
|
139
896
|
function buildPlan(root, flags, stdinText, presence) {
|
|
@@ -149,28 +906,28 @@ function buildPlan(root, flags, stdinText, presence) {
|
|
|
149
906
|
function applyPlan(root, flags, presence, stdinText) {
|
|
150
907
|
const policy = resolvePolicy(root, flags, stdinText);
|
|
151
908
|
const configPath = projectConfigPath(root);
|
|
152
|
-
|
|
153
|
-
|
|
909
|
+
mkdirSync3(dirname3(configPath), { recursive: true });
|
|
910
|
+
writeFileSync3(configPath, `${JSON.stringify(policy, null, 2)}
|
|
154
911
|
`);
|
|
155
912
|
const launcher = launcherPath();
|
|
156
913
|
const cursor = presence.cursor ? (() => {
|
|
157
914
|
const result = applyCursorWiring({
|
|
158
|
-
target:
|
|
915
|
+
target: join4(root, ".cursor", "hooks.json"),
|
|
159
916
|
strategy: "replace",
|
|
160
917
|
entries: cursorShimEntries(launcher)
|
|
161
918
|
}, { force: flags.force });
|
|
162
919
|
return { skipped: false, status: result.status, target: result.target };
|
|
163
920
|
})() : { skipped: true };
|
|
164
921
|
const claude = presence.claude ? (() => {
|
|
165
|
-
const result = applyClaudeWiring(
|
|
922
|
+
const result = applyClaudeWiring(join4(root, ".claude", "settings.json"), claudeShimEntries(launcher));
|
|
166
923
|
return {
|
|
167
924
|
skipped: false,
|
|
168
925
|
status: result.ok ? result.changed ? "written" : "unchanged" : "failed",
|
|
169
|
-
target:
|
|
926
|
+
target: join4(root, ".claude", "settings.json")
|
|
170
927
|
};
|
|
171
928
|
})() : { skipped: true };
|
|
172
929
|
const gitignore = mergeGitignore(root);
|
|
173
|
-
|
|
930
|
+
writeFileSync3(join4(root, ".gitignore"), gitignore.text);
|
|
174
931
|
return { configPath, cursor, claude };
|
|
175
932
|
}
|
|
176
933
|
async function readStdin() {
|