@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.
Files changed (41) hide show
  1. package/bin/tlc-build.mjs +15 -29
  2. package/bin/tlc-cli.ts +99 -3
  3. package/dist/compact-before.mjs +7961 -13
  4. package/dist/doctor.mjs +8480 -33
  5. package/dist/help-topic.mjs +14 -6
  6. package/dist/init-project.mjs +793 -36
  7. package/dist/install-runtime.mjs +7268 -17
  8. package/dist/lessons-cli.mjs +7044 -26
  9. package/dist/obs-cli.mjs +7037 -27
  10. package/dist/price-lookup.mjs +201 -11
  11. package/dist/prompt-submit.mjs +7975 -15
  12. package/dist/refresh-model-prices.mjs +7060 -25
  13. package/dist/response-after.mjs +7962 -12
  14. package/dist/run.mjs +7960 -10
  15. package/dist/session-end.mjs +8030 -15
  16. package/dist/session-start.mjs +8076 -21
  17. package/dist/shim.mjs +7024 -18
  18. package/dist/stop.mjs +8042 -29
  19. package/dist/subagent-start.mjs +7983 -13
  20. package/dist/subagent-stop.mjs +7962 -12
  21. package/dist/support.mjs +7168 -21
  22. package/dist/tlc-cli.mjs +8250 -65
  23. package/dist/tool-after.mjs +8177 -21
  24. package/dist/tool-before.mjs +7991 -16
  25. package/dist/tool-failure.mjs +7962 -15
  26. package/dist/uninstall-runtime.mjs +1008 -61
  27. package/docs/log.md +1 -1
  28. package/package.json +1 -1
  29. package/dist/chunks/compact-before-1e4qg1qt.mjs +0 -1185
  30. package/dist/chunks/compact-before-2hpbfxm5.mjs +0 -5782
  31. package/dist/chunks/compact-before-49j320yp.mjs +0 -1283
  32. package/dist/chunks/compact-before-4jrq0sqs.mjs +0 -61
  33. package/dist/chunks/compact-before-6w8n1vh1.mjs +0 -186
  34. package/dist/chunks/compact-before-7sdmwswh.mjs +0 -52
  35. package/dist/chunks/compact-before-beqpmqrm.mjs +0 -187
  36. package/dist/chunks/compact-before-j9y4jgn4.mjs +0 -845
  37. package/dist/chunks/compact-before-pk86tqx2.mjs +0 -118
  38. package/dist/chunks/compact-before-pkqk5v29.mjs +0 -137
  39. package/dist/chunks/compact-before-w1293m4n.mjs +0 -315
  40. package/dist/chunks/compact-before-wnnds45y.mjs +0 -26
  41. package/dist/chunks/compact-before-wt2c3nh4.mjs +0 -551
@@ -1,27 +1,784 @@
1
- import {
2
- applyCursorWiring,
3
- renderCursorHooksDocument
4
- } from "./chunks/compact-before-7sdmwswh.mjs";
5
- import"./chunks/compact-before-j9y4jgn4.mjs";
6
- import {
7
- applyClaudeWiring
8
- } from "./chunks/compact-before-w1293m4n.mjs";
9
- import {
10
- DEFAULTS,
11
- PLAIN,
12
- render
13
- } from "./chunks/compact-before-49j320yp.mjs";
14
- import {
15
- __require,
16
- claudeConfigDir,
17
- cursorConfigDir,
18
- projectConfigPath,
19
- runtimeHome
20
- } from "./chunks/compact-before-4jrq0sqs.mjs";
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 join(home, "bin", "tlc-exec.mjs");
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 = [join(".cursor", "hooks.json"), join(".claude", "settings.json")];
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 = join(root, ".gitignore");
106
- const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
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 && existsSync(projectConfigPath(root))) {
129
- return JSON.parse(readFileSync(projectConfigPath(root), "utf8"));
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: existsSync(dirs.cursor ?? cursorConfigDir()),
136
- claude: existsSync(dirs.claude ?? claudeConfigDir())
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
- mkdirSync(dirname(configPath), { recursive: true });
153
- writeFileSync(configPath, `${JSON.stringify(policy, null, 2)}
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: join(root, ".cursor", "hooks.json"),
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(join(root, ".claude", "settings.json"), claudeShimEntries(launcher));
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: join(root, ".claude", "settings.json")
926
+ target: join4(root, ".claude", "settings.json")
170
927
  };
171
928
  })() : { skipped: true };
172
929
  const gitignore = mergeGitignore(root);
173
- writeFileSync(join(root, ".gitignore"), gitignore.text);
930
+ writeFileSync3(join4(root, ".gitignore"), gitignore.text);
174
931
  return { configPath, cursor, claude };
175
932
  }
176
933
  async function readStdin() {