tldr-experts 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7051,9 +7051,9 @@ var bunRuntime = {
7051
7051
  clearTimeout(timer);
7052
7052
  opts.signal?.removeEventListener("abort", onAbort);
7053
7053
  const [stdout, stderr] = await withinGrace(collected);
7054
- return { exitCode, stdout, stderr, timedOut };
7054
+ return { exitCode, stdout, stderr, timedOut, spawnFailed: false };
7055
7055
  } catch (error) {
7056
- return { exitCode: 127, stdout: "", stderr: messageOf(error), timedOut };
7056
+ return { exitCode: 127, stdout: "", stderr: messageOf(error), timedOut, spawnFailed: true };
7057
7057
  }
7058
7058
  },
7059
7059
  async readText(path) {
@@ -7186,6 +7186,7 @@ var nodeRuntime = {
7186
7186
  spawn(cmd, args, opts = {}) {
7187
7187
  return new Promise((resolve) => {
7188
7188
  let timedOut = false;
7189
+ let spawnFailed = false;
7189
7190
  let settled = false;
7190
7191
  let stdout = "";
7191
7192
  let stderr = "";
@@ -7224,7 +7225,7 @@ var nodeRuntime = {
7224
7225
  clearTimeout(timer);
7225
7226
  if (graceTimer !== null)
7226
7227
  clearTimeout(graceTimer);
7227
- resolve({ exitCode, stdout, stderr, timedOut });
7228
+ resolve({ exitCode, stdout, stderr, timedOut, spawnFailed });
7228
7229
  };
7229
7230
  child.stdout?.on("data", (chunk) => {
7230
7231
  const text = chunk.toString("utf8");
@@ -7240,6 +7241,7 @@ var nodeRuntime = {
7240
7241
  child.stderr?.on("end", () => errSplitter?.end());
7241
7242
  child.on("error", (error) => {
7242
7243
  stderr += error.message;
7244
+ spawnFailed = true;
7243
7245
  finish(127);
7244
7246
  });
7245
7247
  child.on("exit", (code, signal) => {
@@ -7412,9 +7414,375 @@ function isEpicWorktreeOf(name, runId) {
7412
7414
  return name.startsWith(`${EPIC_WORKTREE_PREFIX}${runId}-`);
7413
7415
  }
7414
7416
 
7417
+ // src/core/schemas/validation.ts
7418
+ function result(issues, deprecations = []) {
7419
+ return { ok: issues.length === 0, issues, deprecations };
7420
+ }
7421
+ var VERSION_KEY = "version";
7422
+ var LEGACY_VERSION_KEY = "schema_version";
7423
+ var SCHEMA_VERSION = 1;
7424
+ var LEGACY_VERSION_NOTE = `${LEGACY_VERSION_KEY} is deprecated — say ${VERSION_KEY}: ${String(SCHEMA_VERSION)}`;
7425
+ function requireVersion(doc, issues, deprecations) {
7426
+ const declared = doc[VERSION_KEY];
7427
+ if (declared !== undefined) {
7428
+ if (declared !== SCHEMA_VERSION) {
7429
+ issues.push({
7430
+ path: VERSION_KEY,
7431
+ message: `unknown schema version ${describeValue(declared)} (expected ${String(SCHEMA_VERSION)})`
7432
+ });
7433
+ }
7434
+ return;
7435
+ }
7436
+ if (!(LEGACY_VERSION_KEY in doc) || doc[LEGACY_VERSION_KEY] === undefined) {
7437
+ issues.push({ path: VERSION_KEY, message: `missing required key \`${VERSION_KEY}\`` });
7438
+ return;
7439
+ }
7440
+ requireNumber(doc[LEGACY_VERSION_KEY], LEGACY_VERSION_KEY, issues);
7441
+ deprecations.push(LEGACY_VERSION_NOTE);
7442
+ }
7443
+ function isRecord(value) {
7444
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7445
+ }
7446
+ function joinPath(base, key) {
7447
+ return base === "" ? key : `${base}.${key}`;
7448
+ }
7449
+ function requireKeys(value, keys, base, issues) {
7450
+ for (const key of keys) {
7451
+ if (!(key in value) || value[key] === undefined) {
7452
+ issues.push({ path: joinPath(base, key), message: `missing required key \`${key}\`` });
7453
+ }
7454
+ }
7455
+ }
7456
+ function requireEnum(value, allowed, path, issues) {
7457
+ if (value === undefined)
7458
+ return;
7459
+ if (typeof value !== "string" || !allowed.includes(value)) {
7460
+ issues.push({ path, message: `expected one of ${allowed.join(" | ")}, got ${describe(value)}` });
7461
+ }
7462
+ }
7463
+ function requireArray(value, path, issues) {
7464
+ if (value === undefined)
7465
+ return false;
7466
+ if (!Array.isArray(value)) {
7467
+ issues.push({ path, message: `expected an array, got ${describe(value)}` });
7468
+ return false;
7469
+ }
7470
+ return true;
7471
+ }
7472
+ function requireNumber(value, path, issues) {
7473
+ if (value === undefined)
7474
+ return;
7475
+ if (typeof value !== "number" || Number.isNaN(value)) {
7476
+ issues.push({ path, message: `expected a number, got ${describe(value)}` });
7477
+ }
7478
+ }
7479
+ function requireString(value, path, issues) {
7480
+ if (value === undefined)
7481
+ return;
7482
+ if (typeof value !== "string") {
7483
+ issues.push({ path, message: `expected a string, got ${describe(value)}` });
7484
+ }
7485
+ }
7486
+ function requireRecord(value, path, issues) {
7487
+ if (value === undefined)
7488
+ return false;
7489
+ if (!isRecord(value)) {
7490
+ issues.push({ path, message: `expected a mapping, got ${describe(value)}` });
7491
+ return false;
7492
+ }
7493
+ return true;
7494
+ }
7495
+ function asDocument(input, issues) {
7496
+ if (!isRecord(input)) {
7497
+ issues.push({ path: "", message: `expected a mapping at the document root, got ${describe(input)}` });
7498
+ return null;
7499
+ }
7500
+ return input;
7501
+ }
7502
+ function describe(value) {
7503
+ if (value === null)
7504
+ return "null";
7505
+ if (Array.isArray(value))
7506
+ return "an array";
7507
+ return typeof value;
7508
+ }
7509
+ function describeValue(value) {
7510
+ return typeof value === "number" || typeof value === "boolean" ? String(value) : describe(value);
7511
+ }
7512
+
7513
+ // src/core/schemas/planCommon.ts
7514
+ var STORY_ID_RE = /^S\d{1,4}$/;
7515
+ var WAVE_ID_RE = /^W\d{1,3}$/;
7516
+ var MAX_LIST_ITEMS = 64;
7517
+ var MAX_ITEM_CHARS = 512;
7518
+ var MAX_WAVES = 32;
7519
+ var MAX_STORIES_PER_WAVE = 32;
7520
+ var MAX_PLAN_STORIES = 200;
7521
+ function requireStringList(value, path, issues, rules = {}) {
7522
+ if (value === undefined)
7523
+ return [];
7524
+ if (!Array.isArray(value)) {
7525
+ issues.push({ path, message: `expected a list, got ${describe2(value)}` });
7526
+ return [];
7527
+ }
7528
+ const max = rules.max ?? MAX_LIST_ITEMS;
7529
+ if (value.length > max) {
7530
+ issues.push({ path, message: `${value.length} items exceeds the ${max}-item cap` });
7531
+ }
7532
+ if (rules.nonEmpty === true && value.length === 0) {
7533
+ issues.push({ path, message: "must not be empty" });
7534
+ }
7535
+ const seen = new Set;
7536
+ const out = [];
7537
+ value.forEach((item, i) => {
7538
+ const at = `${path}[${i}]`;
7539
+ if (typeof item !== "string") {
7540
+ issues.push({ path: at, message: `expected a string, got ${describe2(item)}` });
7541
+ return;
7542
+ }
7543
+ if (item.trim() === "") {
7544
+ issues.push({ path: at, message: "must not be blank" });
7545
+ return;
7546
+ }
7547
+ if (item.length > MAX_ITEM_CHARS) {
7548
+ issues.push({
7549
+ path: at,
7550
+ message: `${item.length} characters exceeds the ${MAX_ITEM_CHARS}-character cap on one list item ` + "— split it into several items"
7551
+ });
7552
+ }
7553
+ if (rules.pattern !== undefined && !rules.pattern.test(item)) {
7554
+ issues.push({ path: at, message: `expected ${rules.patternName ?? String(rules.pattern)}, got \`${item}\`` });
7555
+ return;
7556
+ }
7557
+ if (rules.unique === true && seen.has(item)) {
7558
+ issues.push({ path: at, message: `\`${item}\` is listed twice` });
7559
+ return;
7560
+ }
7561
+ seen.add(item);
7562
+ out.push(item);
7563
+ });
7564
+ return out;
7565
+ }
7566
+ function requirePattern(value, pattern, patternName, path, issues) {
7567
+ if (value === undefined)
7568
+ return;
7569
+ if (typeof value !== "string") {
7570
+ issues.push({ path, message: `expected a string, got ${describe2(value)}` });
7571
+ return;
7572
+ }
7573
+ if (!pattern.test(value)) {
7574
+ issues.push({ path, message: `expected ${patternName}, got \`${value}\`` });
7575
+ }
7576
+ }
7577
+ function requireVersion1(doc, issues) {
7578
+ if (doc.version !== undefined && doc.version !== 1) {
7579
+ issues.push({ path: "version", message: `unknown schema version ${String(doc.version)} (expected 1)` });
7580
+ }
7581
+ }
7582
+ function describe2(value) {
7583
+ if (value === null)
7584
+ return "null";
7585
+ if (Array.isArray(value))
7586
+ return "a list";
7587
+ if (isRecord(value))
7588
+ return "a mapping";
7589
+ return typeof value;
7590
+ }
7591
+
7592
+ // src/core/schemas/frontMatter.ts
7593
+ var FENCE = "---";
7594
+ function splitFrontMatter(text) {
7595
+ const lines = text.split(`
7596
+ `);
7597
+ if ((lines[0] ?? "").trimEnd() !== FENCE) {
7598
+ return { present: false, raw: "", body: text, bodyLine: 1 };
7599
+ }
7600
+ for (let i = 1;i < lines.length; i++) {
7601
+ if ((lines[i] ?? "").trimEnd() !== FENCE)
7602
+ continue;
7603
+ return {
7604
+ present: true,
7605
+ raw: lines.slice(1, i).join(`
7606
+ `),
7607
+ body: lines.slice(i + 1).join(`
7608
+ `),
7609
+ bodyLine: i + 2
7610
+ };
7611
+ }
7612
+ return { present: false, raw: "", body: text, bodyLine: 1 };
7613
+ }
7614
+ function parseFrontMatter(text) {
7615
+ const frontMatter = splitFrontMatter(text);
7616
+ if (!frontMatter.present) {
7617
+ return {
7618
+ frontMatter,
7619
+ doc: null,
7620
+ issue: {
7621
+ path: "",
7622
+ message: "no YAML front matter — the file must open with `---` and close the block with `---`"
7623
+ }
7624
+ };
7625
+ }
7626
+ try {
7627
+ return { frontMatter, doc: parseYaml(frontMatter.raw), issue: null };
7628
+ } catch (error) {
7629
+ return {
7630
+ frontMatter,
7631
+ doc: null,
7632
+ issue: { path: "", message: `front matter is not valid YAML: ${message(error)}` }
7633
+ };
7634
+ }
7635
+ }
7636
+ function message(error) {
7637
+ return error instanceof Error ? error.message.split(`
7638
+ `)[0] ?? "parse error" : String(error);
7639
+ }
7640
+ // src/core/schemas/story.ts
7641
+ var DOD_FENCE_OPEN_RE = /^\s*```+\s*dod\s*$/i;
7642
+ var DOD_FENCE_CLOSE_RE = /^\s*```+\s*$/;
7643
+ var FENCE_OPEN_RE = DOD_FENCE_OPEN_RE;
7644
+ var FENCE_CLOSE_RE = DOD_FENCE_CLOSE_RE;
7645
+ function parseDodBlock(text) {
7646
+ const commands = [];
7647
+ let present = false;
7648
+ let inside = false;
7649
+ for (const line of text.split(`
7650
+ `)) {
7651
+ if (inside) {
7652
+ if (FENCE_CLOSE_RE.test(line)) {
7653
+ inside = false;
7654
+ continue;
7655
+ }
7656
+ const command = line.trim();
7657
+ if (command !== "" && !command.startsWith("#"))
7658
+ commands.push(command);
7659
+ continue;
7660
+ }
7661
+ if (FENCE_OPEN_RE.test(line)) {
7662
+ present = true;
7663
+ inside = true;
7664
+ }
7665
+ }
7666
+ return { present, commands };
7667
+ }
7668
+
7669
+ // src/hooks/lib/story.ts
7670
+ var DONE_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?status(?:\*\*)?\s*:\s*["'`]?done["'`]?\.?\s*$/i;
7671
+ var REPO_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?repo(?:\*\*)?\s*:\s*["'`]?([A-Za-z0-9._-]{1,64})["'`]?\s*$/i;
7672
+ var TIMEOUT_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?timeout_s(?:\*\*)?\s*:\s*(\d{1,6})\s*$/i;
7673
+ var FENCE_OPEN_RE2 = /^\s*```+\s*dod\s*$/i;
7674
+ var FENCE_CLOSE_RE2 = /^\s*```+\s*$/;
7675
+ function readStory(text) {
7676
+ const lines = text.split(`
7677
+ `);
7678
+ const dod = parseDodBlock(text);
7679
+ let setsDone = false;
7680
+ let repo = null;
7681
+ let timeoutS = null;
7682
+ let inDod = false;
7683
+ for (const line of lines) {
7684
+ if (inDod) {
7685
+ if (FENCE_CLOSE_RE2.test(line))
7686
+ inDod = false;
7687
+ continue;
7688
+ }
7689
+ if (FENCE_OPEN_RE2.test(line)) {
7690
+ inDod = true;
7691
+ continue;
7692
+ }
7693
+ if (!setsDone && DONE_RE.test(line))
7694
+ setsDone = true;
7695
+ if (repo === null) {
7696
+ const m = REPO_RE.exec(line);
7697
+ if (m?.[1] !== undefined)
7698
+ repo = m[1];
7699
+ }
7700
+ if (timeoutS === null) {
7701
+ const m = TIMEOUT_RE.exec(line);
7702
+ if (m?.[1] !== undefined)
7703
+ timeoutS = Number(m[1]);
7704
+ }
7705
+ }
7706
+ return { setsDone, repo, dodCommands: dod.commands, hasDodBlock: dod.present, timeoutS };
7707
+ }
7708
+ function isAllowedDodCommand(command, allowed) {
7709
+ return allowed.has(command);
7710
+ }
7711
+ var META_RE = /[|&;<>$`(){}*?~\\]/;
7712
+ function splitArgv(command) {
7713
+ if (/[\n\r]/.test(command))
7714
+ return null;
7715
+ const argv = [];
7716
+ const pattern = /"([^"]*)"|'([^']*)'|([^\s"']+)/g;
7717
+ for (const match of command.matchAll(pattern)) {
7718
+ if (match[1] !== undefined || match[2] !== undefined) {
7719
+ argv.push(match[1] ?? match[2] ?? "");
7720
+ continue;
7721
+ }
7722
+ const bare = match[3] ?? "";
7723
+ if (META_RE.test(bare))
7724
+ return null;
7725
+ argv.push(bare);
7726
+ }
7727
+ return argv.length === 0 ? null : argv;
7728
+ }
7729
+
7730
+ class DodCommandRefused extends Error {
7731
+ }
7732
+ async function runDodCommand(command, cwd, timeoutMs, allowed) {
7733
+ if (!isAllowedDodCommand(command, allowed)) {
7734
+ throw new DodCommandRefused(allowed.size === 0 ? `\`${command}\` cannot be run: .tldrx/workspace.yml declares no commands, so there is nothing ` + "to check it against. An empty allowlist is not a permit." : `\`${command}\` is not one of .tldrx/workspace.yml's commands. Declared: ` + `${[...allowed].map((c) => `\`${c}\``).join(", ")}.`);
7735
+ }
7736
+ const argv = splitArgv(command);
7737
+ if (argv === null) {
7738
+ throw new DodCommandRefused(`\`${command}\` needs a shell to run (it contains a metacharacter), and this gate does not open one. ` + "Put it in a script, declare the script under the repo's `commands:`, and cite that.");
7739
+ }
7740
+ const head = argv[0] ?? "";
7741
+ const { exitCode, stdout, stderr, timedOut } = await runtime.spawn(head, argv.slice(1), {
7742
+ cwd,
7743
+ timeoutMs
7744
+ });
7745
+ return { command, exitCode, timedOut, tail: lastMeaningfulLine(`${stdout}
7746
+ ${stderr}`) };
7747
+ }
7748
+ function lastMeaningfulLine(output, max = 200) {
7749
+ const lines = output.split(`
7750
+ `).map((l) => l.trimEnd()).filter((l) => l.trim() !== "");
7751
+ const last = lines[lines.length - 1] ?? "";
7752
+ return last.length > max ? `${last.slice(0, max - 1)}…` : last;
7753
+ }
7754
+
7415
7755
  // src/hooks/lib/workspace.ts
7416
7756
  import { existsSync as existsSync2, readdirSync, readFileSync, statSync as statSync2 } from "node:fs";
7417
7757
  import { basename, dirname as dirname3, isAbsolute, join as join2, resolve, sep } from "node:path";
7758
+
7759
+ // src/core/detect/probeCommands.ts
7760
+ var PROBE_STATUSES = ["ok", "failed", "timed-out", "unspawnable", "not-probed", "skipped"];
7761
+
7762
+ // src/core/schemas/workspace.ts
7763
+ function commandProbeIssues(probe, path) {
7764
+ if (!isRecord(probe))
7765
+ return [{ path, message: "expected a mapping" }];
7766
+ const issues = [];
7767
+ if (!PROBE_STATUSES.includes(probe.status)) {
7768
+ issues.push({ path: `${path}.status`, message: `expected one of ${PROBE_STATUSES.join(" | ")}` });
7769
+ }
7770
+ if (typeof probe.verified !== "boolean") {
7771
+ issues.push({ path: `${path}.verified`, message: "expected a boolean" });
7772
+ }
7773
+ if (probe.exit_code !== null && typeof probe.exit_code !== "number") {
7774
+ issues.push({ path: `${path}.exit_code`, message: "expected a number or null" });
7775
+ }
7776
+ if (typeof probe.at !== "string" || probe.at === "") {
7777
+ issues.push({ path: `${path}.at`, message: "expected a non-empty RFC3339 string" });
7778
+ }
7779
+ if (typeof probe.reason !== "string" || probe.reason === "") {
7780
+ issues.push({ path: `${path}.reason`, message: "expected a non-empty sentence saying why" });
7781
+ }
7782
+ return issues;
7783
+ }
7784
+
7785
+ // src/hooks/lib/workspace.ts
7418
7786
  var RUN_YML = "run.yml";
7419
7787
  function locateWork(filePath) {
7420
7788
  if (filePath === "")
@@ -7468,11 +7836,33 @@ function commandRolesOf(doc) {
7468
7836
  }
7469
7837
  return out;
7470
7838
  }
7839
+ function commandProbesOf(doc) {
7840
+ const out = new Map;
7841
+ const list = doc?.repos;
7842
+ if (!Array.isArray(list))
7843
+ return out;
7844
+ for (const entry of list) {
7845
+ if (typeof entry?.name !== "string")
7846
+ continue;
7847
+ const probes = new Map;
7848
+ const raw = entry.command_probes;
7849
+ if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
7850
+ for (const [slot, value] of Object.entries(raw)) {
7851
+ if (commandProbeIssues(value, slot).length > 0)
7852
+ continue;
7853
+ probes.set(slot, value);
7854
+ }
7855
+ }
7856
+ out.set(entry.name, probes);
7857
+ }
7858
+ return out;
7859
+ }
7471
7860
  function loadWorkspace(root) {
7472
7861
  const repos = new Map;
7473
7862
  const commands = new Set;
7474
7863
  const repoCommands = new Map;
7475
7864
  const commandRoles = new Map;
7865
+ const commandProbes = new Map;
7476
7866
  const defaultBranches = new Map;
7477
7867
  let seedTriageThresholdTokens = null;
7478
7868
  const empty = () => ({
@@ -7481,6 +7871,7 @@ function loadWorkspace(root) {
7481
7871
  commands,
7482
7872
  repoCommands,
7483
7873
  commandRoles,
7874
+ commandProbes,
7484
7875
  defaultBranches,
7485
7876
  seedTriageThresholdTokens
7486
7877
  });
@@ -7504,6 +7895,7 @@ function loadWorkspace(root) {
7504
7895
  if (!Array.isArray(list))
7505
7896
  return empty();
7506
7897
  const declared = commandRolesOf(doc);
7898
+ const measured = commandProbesOf(doc);
7507
7899
  for (const entry of list) {
7508
7900
  if (typeof entry?.name !== "string")
7509
7901
  continue;
@@ -7518,6 +7910,7 @@ function loadWorkspace(root) {
7518
7910
  }
7519
7911
  repoCommands.set(entry.name, own);
7520
7912
  commandRoles.set(entry.name, roles);
7913
+ commandProbes.set(entry.name, measured.get(entry.name) ?? new Map);
7521
7914
  }
7522
7915
  return empty();
7523
7916
  }
@@ -7933,14 +8326,14 @@ function parseFileSrc(raw, repos) {
7933
8326
  }
7934
8327
  var lineCountCache = new Map;
7935
8328
  var OK = { ok: true, outcome: "ok" };
7936
- function refused(message, resolved) {
7937
- return resolved === undefined ? { ok: false, outcome: "refused", message } : { ok: false, outcome: "refused", message, resolved };
8329
+ function refused(message2, resolved) {
8330
+ return resolved === undefined ? { ok: false, outcome: "refused", message: message2 } : { ok: false, outcome: "refused", message: message2, resolved };
7938
8331
  }
7939
- function unverified(message) {
7940
- return { ok: true, outcome: "unverified", message };
8332
+ function unverified(message2) {
8333
+ return { ok: true, outcome: "unverified", message: message2 };
7941
8334
  }
7942
- function noted(message) {
7943
- return { ok: true, outcome: "noted", message };
8335
+ function noted(message2) {
8336
+ return { ok: true, outcome: "noted", message: message2 };
7944
8337
  }
7945
8338
  function resolveSrc(ref, ctx, section, claim = "") {
7946
8339
  switch (ref.kind) {
@@ -8721,4 +9114,4 @@ function noneBullet(lookedAt) {
8721
9114
  return `- none [src: absent:${lookedAt}]`;
8722
9115
  }
8723
9116
 
8724
- export { runtime, parseYaml, parseYamlRepairing, STAGES_DIR, TEMPLATES_DIR, PROJECT_FRAMEWORK_DIR, PROJECT_WORK_DIR, readableSource, SRC_PATTERNS, srcRule, parseSrcToken, classifySrc, locateWork, findWorkspaceRoot, loadWorkspace, toSrcContext, repoPath, factsPath, stageYamlPath, listRunDirs, parseHandoff, isHandoff, BULLET_RULE, EMPTY_SECTION_RULE, validateHandoff, validateCitations, noneBullet };
9117
+ export { runtime, parseYaml, parseYamlRepairing, STAGES_DIR, TEMPLATES_DIR, PROJECT_FRAMEWORK_DIR, PROJECT_WORK_DIR, result, requireVersion, isRecord, requireKeys, requireEnum, requireArray, requireNumber, requireString, requireRecord, asDocument, STORY_ID_RE, WAVE_ID_RE, MAX_WAVES, MAX_STORIES_PER_WAVE, MAX_PLAN_STORIES, requireStringList, requirePattern, requireVersion1, parseFrontMatter, readStory, DodCommandRefused, runDodCommand, readableSource, SRC_PATTERNS, srcRule, parseSrcToken, classifySrc, locateWork, findWorkspaceRoot, loadWorkspace, toSrcContext, repoPath, factsPath, stageYamlPath, listRunDirs, parseHandoff, isHandoff, BULLET_RULE, EMPTY_SECTION_RULE, validateHandoff, validateCitations, noneBullet };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PROJECT_FRAMEWORK_DIR,
3
3
  PROJECT_WORK_DIR
4
- } from "./chunk-y3cxqvnx.js";
4
+ } from "./chunk-f6d6cp4h.js";
5
5
 
6
6
  // src/core/lock/workspaceLock.ts
7
7
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeSync, closeSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  toolInput
3
- } from "./chunk-2y3kwfrx.js";
3
+ } from "./chunk-dzm2bzqp.js";
4
4
 
5
5
  // src/hooks/lib/wouldBe.ts
6
6
  import { existsSync, readFileSync } from "node:fs";
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  wouldBeContent
4
- } from "./chunk-rk1nm39z.js";
4
+ } from "./chunk-w3jssmwe.js";
5
5
  import {
6
6
  claimSourcesDeny,
7
7
  claimSourcesEmptySectionDeny,
8
8
  claimSourcesMalformedDeny,
9
9
  claimSourcesUnresolvedDeny
10
- } from "./chunk-cf4az0j5.js";
10
+ } from "./chunk-7ye9jde9.js";
11
11
  import {
12
12
  allow,
13
13
  deny,
@@ -16,8 +16,8 @@ import {
16
16
  postContext,
17
17
  readPayload,
18
18
  runHook
19
- } from "./chunk-2y3kwfrx.js";
20
- import"./chunk-x3zy7zrn.js";
19
+ } from "./chunk-dzm2bzqp.js";
20
+ import"./chunk-3rwbyema.js";
21
21
  import {
22
22
  isHandoff,
23
23
  loadWorkspace,
@@ -25,7 +25,7 @@ import {
25
25
  toSrcContext,
26
26
  validateCitations,
27
27
  validateHandoff
28
- } from "./chunk-y3cxqvnx.js";
28
+ } from "./chunk-f6d6cp4h.js";
29
29
 
30
30
  // src/hooks/claim-sources.ts
31
31
  await runHook("claim-sources", async () => {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  wouldBeContent
4
- } from "./chunk-rk1nm39z.js";
4
+ } from "./chunk-w3jssmwe.js";
5
5
  import {
6
6
  dodGateDeny,
7
7
  dodGateInternalErrorDeny,
8
8
  dodGateMissingBlockDeny,
9
9
  dodGateRefusedCommandDeny
10
- } from "./chunk-cf4az0j5.js";
10
+ } from "./chunk-7ye9jde9.js";
11
11
  import {
12
12
  allow,
13
13
  deny,
@@ -15,111 +15,20 @@ import {
15
15
  filePathOf,
16
16
  isWriteOrEdit,
17
17
  readPayload
18
- } from "./chunk-2y3kwfrx.js";
19
- import"./chunk-x3zy7zrn.js";
20
- import {
21
- parseDodBlock
22
- } from "./chunk-rrkdfk7s.js";
23
- import"./chunk-5w5eg00k.js";
24
- import"./chunk-39zh2e44.js";
18
+ } from "./chunk-dzm2bzqp.js";
19
+ import"./chunk-3rwbyema.js";
25
20
  import {
21
+ DodCommandRefused,
26
22
  loadWorkspace,
27
23
  locateWork,
24
+ readStory,
28
25
  repoPath,
29
- runtime
30
- } from "./chunk-y3cxqvnx.js";
26
+ runDodCommand
27
+ } from "./chunk-f6d6cp4h.js";
31
28
 
32
29
  // src/hooks/dod-gate.ts
33
30
  import { basename, join } from "node:path";
34
31
  import { existsSync } from "node:fs";
35
-
36
- // src/hooks/lib/story.ts
37
- var DONE_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?status(?:\*\*)?\s*:\s*["'`]?done["'`]?\.?\s*$/i;
38
- var REPO_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?repo(?:\*\*)?\s*:\s*["'`]?([A-Za-z0-9._-]{1,64})["'`]?\s*$/i;
39
- var TIMEOUT_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?timeout_s(?:\*\*)?\s*:\s*(\d{1,6})\s*$/i;
40
- var FENCE_OPEN_RE = /^\s*```+\s*dod\s*$/i;
41
- var FENCE_CLOSE_RE = /^\s*```+\s*$/;
42
- function readStory(text) {
43
- const lines = text.split(`
44
- `);
45
- const dod = parseDodBlock(text);
46
- let setsDone = false;
47
- let repo = null;
48
- let timeoutS = null;
49
- let inDod = false;
50
- for (const line of lines) {
51
- if (inDod) {
52
- if (FENCE_CLOSE_RE.test(line))
53
- inDod = false;
54
- continue;
55
- }
56
- if (FENCE_OPEN_RE.test(line)) {
57
- inDod = true;
58
- continue;
59
- }
60
- if (!setsDone && DONE_RE.test(line))
61
- setsDone = true;
62
- if (repo === null) {
63
- const m = REPO_RE.exec(line);
64
- if (m?.[1] !== undefined)
65
- repo = m[1];
66
- }
67
- if (timeoutS === null) {
68
- const m = TIMEOUT_RE.exec(line);
69
- if (m?.[1] !== undefined)
70
- timeoutS = Number(m[1]);
71
- }
72
- }
73
- return { setsDone, repo, dodCommands: dod.commands, hasDodBlock: dod.present, timeoutS };
74
- }
75
- function isAllowedDodCommand(command, allowed) {
76
- return allowed.has(command);
77
- }
78
- var META_RE = /[|&;<>$`(){}*?~\\]/;
79
- function splitArgv(command) {
80
- if (/[\n\r]/.test(command))
81
- return null;
82
- const argv = [];
83
- const pattern = /"([^"]*)"|'([^']*)'|([^\s"']+)/g;
84
- for (const match of command.matchAll(pattern)) {
85
- if (match[1] !== undefined || match[2] !== undefined) {
86
- argv.push(match[1] ?? match[2] ?? "");
87
- continue;
88
- }
89
- const bare = match[3] ?? "";
90
- if (META_RE.test(bare))
91
- return null;
92
- argv.push(bare);
93
- }
94
- return argv.length === 0 ? null : argv;
95
- }
96
-
97
- class DodCommandRefused extends Error {
98
- }
99
- async function runDodCommand(command, cwd, timeoutMs, allowed) {
100
- if (!isAllowedDodCommand(command, allowed)) {
101
- throw new DodCommandRefused(allowed.size === 0 ? `\`${command}\` cannot be run: .tldrx/workspace.yml declares no commands, so there is nothing ` + "to check it against. An empty allowlist is not a permit." : `\`${command}\` is not one of .tldrx/workspace.yml's commands. Declared: ` + `${[...allowed].map((c) => `\`${c}\``).join(", ")}.`);
102
- }
103
- const argv = splitArgv(command);
104
- if (argv === null) {
105
- throw new DodCommandRefused(`\`${command}\` needs a shell to run (it contains a metacharacter), and this gate does not open one. ` + "Put it in a script, declare the script under the repo's `commands:`, and cite that.");
106
- }
107
- const head = argv[0] ?? "";
108
- const { exitCode, stdout, stderr, timedOut } = await runtime.spawn(head, argv.slice(1), {
109
- cwd,
110
- timeoutMs
111
- });
112
- return { command, exitCode, timedOut, tail: lastMeaningfulLine(`${stdout}
113
- ${stderr}`) };
114
- }
115
- function lastMeaningfulLine(output, max = 200) {
116
- const lines = output.split(`
117
- `).map((l) => l.trimEnd()).filter((l) => l.trim() !== "");
118
- const last = lines[lines.length - 1] ?? "";
119
- return last.length > max ? `${last.slice(0, max - 1)}…` : last;
120
- }
121
-
122
- // src/hooks/dod-gate.ts
123
32
  var DEFAULT_TIMEOUT_S = 900;
124
33
  var storyId = "?";
125
34
  try {