@tech-leads-club/harness-toolkit 0.3.4 → 0.3.6

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.
@@ -1,994 +1,14 @@
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
- }
780
-
781
- // tools/init-project.ts
782
- class UsageError extends Error {
783
- }
784
- function parseFlags(args) {
785
- return {
786
- dryRun: args.includes("--dry-run"),
787
- write: args.includes("--write") || args.includes("--minimal"),
788
- minimal: args.includes("--minimal"),
789
- stdinJson: args.includes("--stdin-json"),
790
- force: args.includes("--force")
791
- };
792
- }
793
- function usageScreen() {
794
- return {
795
- title: "harness init",
796
- sections: [
797
- {
798
- lines: ` tlc harness init --dry-run
1
+ import{createRequire as o}from"node:module";var P=o(import.meta.url);import{existsSync as M,mkdirSync as j$,readFileSync as u,writeFileSync as g}from"node:fs";import{dirname as E$,join as B,sep as N$}from"node:path";import{existsSync as k,mkdirSync as G$,readFileSync as B$,writeFileSync as Y$}from"node:fs";import{dirname as F$,join as f9}from"node:path";import{existsSync as n,mkdirSync as i,readFileSync as t,realpathSync as e,writeFileSync as $$}from"node:fs";import{dirname as Z$,join as e$}from"node:path";import{homedir as O}from"node:os";import{join as U}from"node:path";function a($){return U($,".tlc","harness")}function s(){return U(O(),".tlc","harness")}function j($=process.env){return $.TLC_HOME??s()}function F($){return U(a($),"config.json")}function _(){let $=process.env.CLAUDE_CONFIG_DIR?.trim();return $&&$.length>0?$:U(O(),".claude")}function C(){let $=process.env.CURSOR_CONFIG_DIR?.trim();return $&&$.length>0?$:U(O(),".cursor")}function A($){return $!==null&&typeof $==="object"&&!Array.isArray($)}function Q$($){return A($)}function f($,Q){if($===Q)return!0;if(Array.isArray($)||Array.isArray(Q)){if(!Array.isArray($)||!Array.isArray(Q)||$.length!==Q.length)return!1;return $.every((Z,J)=>f(Z,Q[J]))}if(A($)&&A(Q)){let Z=Object.keys($),J=Object.keys(Q);if(Z.length!==J.length)return!1;return Z.every((z)=>J.includes(z)&&f($[z],Q[z]))}return!1}function J$($){let Q={};for(let Z of $){let J={hooks:[{type:"command",command:Z.command,args:Z.args}]};Q[Z.hookEvent]=[...Q[Z.hookEvent]??[],J]}return Q}var N="tlc-exec.mjs";function z$($){return JSON.stringify($??null).includes(N)}function V$($,Q=e){try{return Q($)}catch{return $}}function E($,Q){return JSON.parse(JSON.stringify($??null,(Z,J)=>typeof J==="string"&&J.includes(N)?V$(J,Q):J))}function q$($,Q){let Z=J$(Q),J={};if($!==null&&$.trim()!==""){let X;try{X=JSON.parse($)}catch(Y){return{ok:!1,error:Y instanceof Error?Y.message:String(Y),block:JSON.stringify({hooks:Z},null,2)}}if(!A(X))return{ok:!1,error:"settings.json root is not a JSON object",block:JSON.stringify({hooks:Z},null,2)};J=X}let V={...Q$(J.hooks)?J.hooks:{}},q=!1;for(let[X,Y]of Object.entries(Z)){let G=V[X]??[],T=[...G.filter((r)=>!z$(r)),...Y];if(!f(E(G),E(T)))q=!0;V[X]=T}let H={...J,hooks:V};return{ok:!0,settingsText:JSON.stringify(H,null,2),changed:q}}function L($,Q){let Z=n($)?t($,"utf8"):null,J=q$(Z,Q);if(J.ok&&J.changed)i(Z$($),{recursive:!0}),$$($,J.settingsText,"utf8");return J}var H$="tlc-exec.mjs";function U$($){return $.includes(" ")?`"${$}"`:$}function K$($){return[$.command,...$.args].map(U$).join(" ")}function I($){let Q={};for(let Z of $){let J={command:K$(Z),timeout:Z.timeoutSeconds};if(Z.failClosed)J.failClosed=!0;if(Z.matcher!==void 0)J.matcher=Z.matcher;if(Z.loopLimit!==void 0)J.loop_limit=Z.loopLimit;Q[Z.hookEvent]=[...Q[Z.hookEvent]??[],J]}return{version:1,hooks:Q}}function A$($){return k($)&&B$($,"utf8").includes(H$)}function b($,{force:Q=!1}={}){let Z=$.target,J=I($.entries),z=`${JSON.stringify(J,null,2)}
2
+ `;if(k(Z)&&!Q){if(A$(Z))return{status:"unchanged",target:Z};return{status:"refused",target:Z,reason:`${Z} exists without harness entries — rerun with --force to overwrite, or merge manually.`}}return G$(F$(Z),{recursive:!0}),Y$(Z,z),{status:"written",target:Z}}var S=[{id:"typescript",extensions:[".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"python",extensions:[".py",".pyi",".pyw"],line:["#"],block:[['"""','"""'],["'''","'''"]],middle:[]},{id:"ruby",extensions:[".rb",".rake",".gemspec"],line:["#"],block:[["=begin","=end"]],middle:[]},{id:"shell",extensions:[".sh",".bash",".zsh",".ksh",".fish"],line:["#"],block:[],middle:[]},{id:"go",extensions:[".go"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"rust",extensions:[".rs"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"java",extensions:[".java"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"kotlin",extensions:[".kt",".kts"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"swift",extensions:[".swift"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"c",extensions:[".c",".h"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"cpp",extensions:[".cc",".cpp",".cxx",".hpp",".hh",".hxx"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"csharp",extensions:[".cs"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"php",extensions:[".php"],line:["//","#"],block:[["/*","*/"]],middle:["*"]},{id:"scala",extensions:[".scala",".sc"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"dart",extensions:[".dart"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"elixir",extensions:[".ex",".exs"],line:["#"],block:[],middle:[]},{id:"erlang",extensions:[".erl",".hrl"],line:["%"],block:[],middle:[]},{id:"haskell",extensions:[".hs"],line:["--"],block:[["{-","-}"]],middle:[]},{id:"lua",extensions:[".lua"],line:["--"],block:[["--[[","]]"]],middle:[]},{id:"sql",extensions:[".sql"],line:["--"],block:[["/*","*/"]],middle:["*"]},{id:"css",extensions:[".css",".scss",".sass",".less"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"yaml",extensions:[".yaml",".yml"],line:["#"],block:[],middle:[]},{id:"toml",extensions:[".toml"],line:["#"],block:[],middle:[]},{id:"ini",extensions:[".ini",".cfg",".conf",".properties"],line:[";","#"],block:[],middle:[]},{id:"dockerfile",extensions:[".dockerfile","dockerfile"],line:["#"],block:[],middle:[]},{id:"makefile",extensions:[".mk","makefile"],line:["#"],block:[],middle:[]},{id:"terraform",extensions:[".tf",".tfvars"],line:["#","//"],block:[["/*","*/"]],middle:["*"]},{id:"powershell",extensions:[".ps1",".psm1",".psd1"],line:["#"],block:[["<#","#>"]],middle:[]},{id:"perl",extensions:[".pl",".pm"],line:["#"],block:[],middle:[]},{id:"r",extensions:[".r"],line:["#"],block:[],middle:[]},{id:"julia",extensions:[".jl"],line:["#"],block:[["#=","=#"]],middle:[]},{id:"vue",extensions:[".vue",".svelte"],line:["//"],block:[["/*","*/"],["<!--","-->"]],middle:["*"]},{id:"html",extensions:[".html",".htm",".xml",".xhtml"],line:[],block:[["<!--","-->"]],middle:[]},{id:"graphql",extensions:[".graphql",".gql"],line:["#"],block:[],middle:[]},{id:"protobuf",extensions:[".proto"],line:["//"],block:[["/*","*/"]],middle:["*"]},{id:"zig",extensions:[".zig"],line:["//"],block:[],middle:[]},{id:"clojure",extensions:[".clj",".cljs",".cljc",".edn"],line:[";"],block:[],middle:[]},{id:"ocaml",extensions:[".ml",".mli"],line:[],block:[["(*","*)"]],middle:["*"]},{id:"fsharp",extensions:[".fs",".fsi",".fsx"],line:["//"],block:[["(*","*)"]],middle:["*"]},{id:"vim",extensions:[".vim"],line:['"'],block:[],middle:[]},{id:"tex",extensions:[".tex",".sty",".cls"],line:["%"],block:[],middle:[]}];function M$($){let Q=new Map;for(let Z of $){let J={line:Z.line,block:Z.block,middle:Z.middle};for(let z of Z.extensions)Q.set(z.toLowerCase(),J)}return Q}var O$=M$(S);var y9=O$.size;var y=6;var K={enabled:!0,signalPath:"obs.jsonl",debugPath:"debug.jsonl",debugEnabled:!1,includePayloads:!1,maxAttrChars:500,sessionCostAlertUsd:5,retentionDays:14,maxSignalEvents:50000,globalSpool:!1};var C$={enabled:!1,maxInjectSession:5,maxInjectRetry:8,maxCharsSession:900,maxCharsRetry:1400,promoteHitCount:2,decayLambda:0.02,projectBoost:1.5,syncRulesFile:"auto",gardenOnSessionEnd:!0},x={version:1,mode:"solo",codePaths:["src","apps","libs","packages"],grind:{enabled:!1,maxLoops:5,lintCommand:null,testCommand:null,appendFiles:"auto"},shipGate:{enabled:!1,runtimePathPrefixes:["src","apps","libs","packages","deploy","scripts"],runtimePathExcludes:[".tlc/","**/node_modules/","**/.git/"],evidenceDir:null,evidenceMaxAgeHours:48,emptyDiffAntiShip:!1,claimWindowMinutes:10},subagents:{enforceAllowlist:!1,requireModel:!1,allowedModels:[],blockedPatterns:["-fast(?:$|[^a-z0-9])","/fast(?:$|[^a-z0-9])"],minEffort:null,blockParentFast:!1,blockMode:"deny",readOnlyTypes:["explore"]},docs:{command:null,severity:"warn"},observe:{enabled:!1,rails:[]},comments:{enabled:!1,onViolation:"followup",mode:"declared"},supplyChain:{enabled:!1},duplication:{enabled:!1,minRun:y},obs:{globalSpool:!1,includePayloads:K.includePayloads,maxAttrChars:K.maxAttrChars,sessionCostAlertUsd:K.sessionCostAlertUsd,retentionDays:K.retentionDays},untrustedContent:{mode:"frame",enabled:!1,extraTools:[],extraCommandPatterns:[]},planGate:{enabled:!1,windowMinutes:120},shell:{catastrophicAsk:!0,stallDetection:!1,stallRepeatThreshold:3},intelligence:{gapFeedback:!0,failureClassification:!0,progressiveHandoff:!0,progressiveContext:!0,autopilot:!0,idleTurnGate:!1,budgetContinue:!1,budgetContinueAfterLoops:3,lessons:{...C$}},mcpPrime:[],bootstrapExtra:[]};var f$={structure:"#3d3a4a",accent:"#a78bfa",success:"#6ee7b7",warning:"#d4a574",error:"#f87171",info:"#93c5fd",textMain:"#f5f5f7",textMuted:"#9ca3af",textDim:"#6b7280"},W={check:"✔",cross:"✖",warning:"⚠",arrow:"→",arrowRight:"▸",dot:"•",bar:"│",rule:"══",dash:"──"};function L$($){let Q=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec($.trim());if(!Q)return"255;255;255";return[Q[1],Q[2],Q[3]].map((Z)=>Number.parseInt(Z,16)).join(";")}var w=String.fromCharCode(27),I$=`${w}[0m`;function R$($=process.env,Q=process.argv,Z=process.stdout.isTTY===!0){if("NO_COLOR"in $)return!1;if(Q.includes("--no-color"))return!1;return Z}var D$={ok:"success",warn:"warning",fail:"error",info:"info"},T$={ok:W.check,warn:W.warning,fail:W.cross,info:W.arrowRight},R=16;function P$($=R$()){let Q=(J,z)=>$?`${w}[${J}m${z}${I$}`:z,Z=(J,z)=>Q(`38;2;${L$(f$[J])}`,z);return{enabled:$,paint:Z,bold:(J)=>Q("1",J),dim:(J)=>Z("textDim",J),heading:(J)=>Z("accent",`${W.rule} ${J} ${W.rule}`),footer:(J)=>Z("textDim",`${W.dash} ${J} ${W.dash}`),kv:(J,z,V=R)=>` ${Z("textMuted",`${J}:`.padEnd(V))} ${z}`,status:(J,z)=>`${Z(D$[J],T$[J])} ${z}`}}var v=P$(!1);function m($,Q){let Z=[Q.heading($.title.toUpperCase())];if($.summary&&$.summary.length>0)Z.push(` ${$.summary.join(Q.dim(` ${W.bar} `))}`);let J=Math.max(R,...$.sections.flatMap((z)=>(z.rows??[]).map((V)=>V.label.length+1)));for(let z of $.sections){if(Z.push(""),z.title)Z.push(Q.paint("accent",z.title));for(let V of z.rows??[]){let q=V.level?Q.status(V.level,V.value):V.value;Z.push(Q.kv(V.label,q,J))}for(let V of z.lines??[])Z.push(V===""?"":` ${V}`)}if($.footer)Z.push("",Q.footer($.footer));return Z.join(`
3
+ `)}class D extends Error{}function k$($){return{dryRun:$.includes("--dry-run"),write:$.includes("--write")||$.includes("--minimal"),minimal:$.includes("--minimal"),stdinJson:$.includes("--stdin-json"),force:$.includes("--force")}}function b$(){return{title:"harness init",sections:[{lines:` tlc harness init --dry-run
799
4
  tlc harness init --write [--stdin-json] [--force]
800
5
  tlc harness init --minimal
801
6
 
802
7
  --minimal writes a safe agnostic stub (grind/ship off). Prefer the harness-init skill for full discovery.`.split(`
803
- `)
804
- }
805
- ]
806
- };
807
- }
808
- function usageText(style = PLAIN) {
809
- return render(usageScreen(), style);
810
- }
811
- function launcherPath(home = runtimeHome()) {
812
- return join4(home, "bin", "tlc-exec.mjs");
813
- }
814
- var SHIM_COMMAND = { command: "node", argsPrefix: [] };
815
- var CURSOR_SHIM_SPECS = [
816
- { hookEvent: "sessionStart", handler: "session-start", timeoutSeconds: 10 },
817
- { hookEvent: "sessionEnd", handler: "session-end", timeoutSeconds: 10 },
818
- { hookEvent: "preToolUse", handler: "tool-before", timeoutSeconds: 10 },
819
- { hookEvent: "beforeShellExecution", handler: "tool-before", timeoutSeconds: 10 },
820
- { hookEvent: "beforeMCPExecution", handler: "tool-before", timeoutSeconds: 10 },
821
- { hookEvent: "beforeReadFile", handler: "tool-before", timeoutSeconds: 5 },
822
- { hookEvent: "subagentStart", handler: "subagent-start", timeoutSeconds: 5 },
823
- { hookEvent: "stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
824
- { hookEvent: "afterAgentResponse", handler: "response-after", timeoutSeconds: 5, matcher: "AgentResponse" }
825
- ];
826
- var CLAUDE_SHIM_SPECS = [
827
- { hookEvent: "SessionStart", handler: "session-start", timeoutSeconds: 10 },
828
- { hookEvent: "SessionEnd", handler: "session-end", timeoutSeconds: 10 },
829
- { hookEvent: "PreToolUse", handler: "tool-before", timeoutSeconds: 10 },
830
- { hookEvent: "SubagentStart", handler: "subagent-start", timeoutSeconds: 5 },
831
- { hookEvent: "Stop", handler: "stop", timeoutSeconds: 120, loopLimit: 5 },
832
- { hookEvent: "MessageDisplay", handler: "response-after", timeoutSeconds: 5 }
833
- ];
834
- function cursorShimEntries(launcher) {
835
- const { command, argsPrefix } = SHIM_COMMAND;
836
- return CURSOR_SHIM_SPECS.map((spec) => ({
837
- hookEvent: spec.hookEvent,
838
- handler: spec.handler,
839
- command,
840
- args: [...argsPrefix, launcher, "shim", spec.handler],
841
- timeoutSeconds: spec.timeoutSeconds,
842
- ...spec.loopLimit !== undefined ? { loopLimit: spec.loopLimit } : {},
843
- ...spec.matcher !== undefined ? { matcher: spec.matcher } : {}
844
- }));
845
- }
846
- function claudeShimEntries(launcher) {
847
- return CLAUDE_SHIM_SPECS.map((spec) => ({
848
- hookEvent: spec.hookEvent,
849
- handler: spec.handler,
850
- command: "node",
851
- args: [launcher, "shim", spec.handler],
852
- timeoutSeconds: spec.timeoutSeconds,
853
- ...spec.loopLimit !== undefined ? { loopLimit: spec.loopLimit } : {}
854
- }));
855
- }
856
- var PROJECT_SHIMS = [join4(".cursor", "hooks.json"), join4(".claude", "settings.json")];
857
- var GITIGNORE_STATE = ".tlc/harness/state/";
858
- function gitignoreEntries() {
859
- return [GITIGNORE_STATE, ...PROJECT_SHIMS.map((path) => path.split(sep).join("/"))];
860
- }
861
- function mergeGitignore(root) {
862
- const path = join4(root, ".gitignore");
863
- const existing = existsSync3(path) ? readFileSync3(path, "utf8") : "";
864
- const lines = existing.split(`
865
- `);
866
- const missing = gitignoreEntries().filter((entry) => !lines.includes(entry));
867
- if (missing.length === 0) {
868
- return { text: existing.endsWith(`
869
- `) || existing === "" ? existing : `${existing}
870
- `, changed: false };
871
- }
872
- lines.push(...missing);
873
- const withoutTrailingBlank = lines.filter((line, index, all) => line.length > 0 || index < all.length - 1);
874
- return { text: `${withoutTrailingBlank.join(`
875
- `).replace(/\n+$/, "")}
876
- `, changed: true };
877
- }
878
- function resolvePolicy(root, flags, stdinText) {
879
- if (flags.stdinJson && !flags.minimal) {
880
- if (!stdinText || stdinText.trim() === "") {
881
- throw new Error("stdin-json: empty stdin");
882
- }
883
- return JSON.parse(stdinText);
884
- }
885
- if (!flags.minimal && !flags.stdinJson && existsSync3(projectConfigPath(root))) {
886
- return JSON.parse(readFileSync3(projectConfigPath(root), "utf8"));
887
- }
888
- return DEFAULTS;
889
- }
890
- function detectProviders(dirs = {}) {
891
- return {
892
- cursor: existsSync3(dirs.cursor ?? cursorConfigDir()),
893
- claude: existsSync3(dirs.claude ?? claudeConfigDir())
894
- };
895
- }
896
- function buildPlan(root, flags, stdinText, presence) {
897
- const policy = resolvePolicy(root, flags, stdinText);
898
- const launcher = launcherPath();
899
- return {
900
- policy,
901
- cursorHooksDocument: presence.cursor ? renderCursorHooksDocument(cursorShimEntries(launcher)) : null,
902
- claudeHooksPreview: presence.claude ? claudeShimEntries(launcher) : null,
903
- gitignoreEntries: gitignoreEntries()
904
- };
905
- }
906
- function applyPlan(root, flags, presence, stdinText) {
907
- const policy = resolvePolicy(root, flags, stdinText);
908
- const configPath = projectConfigPath(root);
909
- mkdirSync3(dirname3(configPath), { recursive: true });
910
- writeFileSync3(configPath, `${JSON.stringify(policy, null, 2)}
911
- `);
912
- const launcher = launcherPath();
913
- const cursor = presence.cursor ? (() => {
914
- const result = applyCursorWiring({
915
- target: join4(root, ".cursor", "hooks.json"),
916
- strategy: "replace",
917
- entries: cursorShimEntries(launcher)
918
- }, { force: flags.force });
919
- return { skipped: false, status: result.status, target: result.target };
920
- })() : { skipped: true };
921
- const claude = presence.claude ? (() => {
922
- const result = applyClaudeWiring(join4(root, ".claude", "settings.json"), claudeShimEntries(launcher));
923
- return {
924
- skipped: false,
925
- status: result.ok ? result.changed ? "written" : "unchanged" : "failed",
926
- target: join4(root, ".claude", "settings.json")
927
- };
928
- })() : { skipped: true };
929
- const gitignore = mergeGitignore(root);
930
- writeFileSync3(join4(root, ".gitignore"), gitignore.text);
931
- return { configPath, cursor, claude };
932
- }
933
- async function readStdin() {
934
- const chunks = [];
935
- for await (const chunk of process.stdin) {
936
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
937
- }
938
- return Buffer.concat(chunks).toString("utf8").trim();
939
- }
940
- async function main(argv) {
941
- const root = process.env.TLC_PROJECT_DIR ?? process.cwd();
942
- const flags = parseFlags(argv);
943
- if (!flags.dryRun && !flags.write) {
944
- throw new UsageError(usageText());
945
- }
946
- const stdinText = flags.stdinJson ? await readStdin() : null;
947
- const presence = detectProviders();
948
- if (flags.dryRun) {
949
- console.log(JSON.stringify(buildPlan(root, flags, stdinText, presence), null, 2));
950
- return;
951
- }
952
- const outcome = applyPlan(root, flags, presence, stdinText);
953
- console.log(`wrote ${outcome.configPath}`);
954
- if (outcome.cursor.skipped) {
955
- console.log("init: cursor not installed — skipped project hooks.json");
956
- } else {
957
- console.log(`hooks: ${outcome.cursor.status} ${outcome.cursor.target}`);
958
- }
959
- if (outcome.claude.skipped) {
960
- console.log("init: claude not installed — skipped project settings.json");
961
- } else {
962
- console.log(`hooks: ${outcome.claude.status} ${outcome.claude.target}`);
963
- }
964
- console.log("updated .gitignore harness entries");
965
- }
966
- if (__require.main == __require.module) {
967
- try {
968
- await main(process.argv.slice(2));
969
- } catch (error) {
970
- if (error instanceof UsageError) {
971
- console.error(error.message);
972
- process.exit(1);
973
- }
974
- throw error;
975
- }
976
- }
977
- export {
978
- usageText,
979
- usageScreen,
980
- resolvePolicy,
981
- parseFlags,
982
- mergeGitignore,
983
- main,
984
- launcherPath,
985
- gitignoreEntries,
986
- detectProviders,
987
- cursorShimEntries,
988
- claudeShimEntries,
989
- buildPlan,
990
- applyPlan,
991
- UsageError,
992
- PROJECT_SHIMS,
993
- GITIGNORE_STATE
994
- };
8
+ `)}]}}function S$($=v){return m(b$(),$)}function h($=j()){return B($,"bin","tlc-exec.mjs")}var y$={command:"node",argsPrefix:[]},x$=[{hookEvent:"sessionStart",handler:"session-start",timeoutSeconds:10},{hookEvent:"sessionEnd",handler:"session-end",timeoutSeconds:10},{hookEvent:"preToolUse",handler:"tool-before",timeoutSeconds:10},{hookEvent:"beforeShellExecution",handler:"tool-before",timeoutSeconds:10},{hookEvent:"beforeMCPExecution",handler:"tool-before",timeoutSeconds:10},{hookEvent:"beforeReadFile",handler:"tool-before",timeoutSeconds:5},{hookEvent:"subagentStart",handler:"subagent-start",timeoutSeconds:5},{hookEvent:"stop",handler:"stop",timeoutSeconds:120,loopLimit:5},{hookEvent:"afterAgentResponse",handler:"response-after",timeoutSeconds:5,matcher:"AgentResponse"}],w$=[{hookEvent:"SessionStart",handler:"session-start",timeoutSeconds:10},{hookEvent:"SessionEnd",handler:"session-end",timeoutSeconds:10},{hookEvent:"PreToolUse",handler:"tool-before",timeoutSeconds:10},{hookEvent:"SubagentStart",handler:"subagent-start",timeoutSeconds:5},{hookEvent:"Stop",handler:"stop",timeoutSeconds:120,loopLimit:5},{hookEvent:"MessageDisplay",handler:"response-after",timeoutSeconds:5}];function p($){let{command:Q,argsPrefix:Z}=y$;return x$.map((J)=>({hookEvent:J.hookEvent,handler:J.handler,command:Q,args:[...Z,$,"shim",J.handler],timeoutSeconds:J.timeoutSeconds,...J.loopLimit!==void 0?{loopLimit:J.loopLimit}:{},...J.matcher!==void 0?{matcher:J.matcher}:{}}))}function c($){return w$.map((Q)=>({hookEvent:Q.hookEvent,handler:Q.handler,command:"node",args:[$,"shim",Q.handler],timeoutSeconds:Q.timeoutSeconds,...Q.loopLimit!==void 0?{loopLimit:Q.loopLimit}:{}}))}var v$=[B(".cursor","hooks.json"),B(".claude","settings.json")],m$=".tlc/harness/state/";function l(){return[m$,...v$.map(($)=>$.split(N$).join("/"))]}function g$($){let Q=B($,".gitignore"),Z=M(Q)?u(Q,"utf8"):"",J=Z.split(`
9
+ `),z=l().filter((q)=>!J.includes(q));if(z.length===0)return{text:Z.endsWith(`
10
+ `)||Z===""?Z:`${Z}
11
+ `,changed:!1};return J.push(...z),{text:`${J.filter((q,H,X)=>q.length>0||H<X.length-1).join(`
12
+ `).replace(/\n+$/,"")}
13
+ `,changed:!0}}function d($,Q,Z){if(Q.stdinJson&&!Q.minimal){if(!Z||Z.trim()==="")throw Error("stdin-json: empty stdin");return JSON.parse(Z)}if(!Q.minimal&&!Q.stdinJson&&M(F($)))return JSON.parse(u(F($),"utf8"));return x}function u$($={}){return{cursor:M($.cursor??C()),claude:M($.claude??_())}}function h$($,Q,Z,J){let z=d($,Q,Z),V=h();return{policy:z,cursorHooksDocument:J.cursor?I(p(V)):null,claudeHooksPreview:J.claude?c(V):null,gitignoreEntries:l()}}function p$($,Q,Z,J){let z=d($,Q,J),V=F($);j$(E$(V),{recursive:!0}),g(V,`${JSON.stringify(z,null,2)}
14
+ `);let q=h(),H=Z.cursor?(()=>{let G=b({target:B($,".cursor","hooks.json"),strategy:"replace",entries:p(q)},{force:Q.force});return{skipped:!1,status:G.status,target:G.target}})():{skipped:!0},X=Z.claude?(()=>{let G=L(B($,".claude","settings.json"),c(q));return{skipped:!1,status:G.ok?G.changed?"written":"unchanged":"failed",target:B($,".claude","settings.json")}})():{skipped:!0},Y=g$($);return g(B($,".gitignore"),Y.text),{configPath:V,cursor:H,claude:X}}async function c$(){let $=[];for await(let Q of process.stdin)$.push(Buffer.isBuffer(Q)?Q:Buffer.from(Q));return Buffer.concat($).toString("utf8").trim()}async function l$($){let Q=process.env.TLC_PROJECT_DIR??process.cwd(),Z=k$($);if(!Z.dryRun&&!Z.write)throw new D(S$());let J=Z.stdinJson?await c$():null,z=u$();if(Z.dryRun){console.log(JSON.stringify(h$(Q,Z,J,z),null,2));return}let V=p$(Q,Z,z,J);if(console.log(`wrote ${V.configPath}`),V.cursor.skipped)console.log("init: cursor not installed — skipped project hooks.json");else console.log(`hooks: ${V.cursor.status} ${V.cursor.target}`);if(V.claude.skipped)console.log("init: claude not installed — skipped project settings.json");else console.log(`hooks: ${V.claude.status} ${V.claude.target}`);console.log("updated .gitignore harness entries")}if(P.main==P.module)try{await l$(process.argv.slice(2))}catch($){if($ instanceof D)console.error($.message),process.exit(1);throw $}export{S$ as usageText,b$ as usageScreen,d as resolvePolicy,k$ as parseFlags,g$ as mergeGitignore,l$ as main,h as launcherPath,l as gitignoreEntries,u$ as detectProviders,p as cursorShimEntries,c as claudeShimEntries,h$ as buildPlan,p$ as applyPlan,D as UsageError,v$ as PROJECT_SHIMS,m$ as GITIGNORE_STATE};