@moda-ai/cli 1.55.60 → 1.55.61

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.
@@ -103,7 +103,7 @@ import {
103
103
  } from "./cli-0v6na3yp.js";
104
104
 
105
105
  // src/harness.ts
106
- import { createHash as createHash5 } from "node:crypto";
106
+ import { createHash as createHash6 } from "node:crypto";
107
107
  import { execFileSync as execFileSync3, spawn } from "node:child_process";
108
108
  import { homedir, tmpdir as tmpdir2 } from "node:os";
109
109
  import { fileURLToPath } from "node:url";
@@ -113,11 +113,11 @@ import {
113
113
  mkdtempSync as mkdtempSync2,
114
114
  mkdirSync as mkdirSync5,
115
115
  readFileSync as readFileSync12,
116
- readdirSync as readdirSync4,
116
+ readdirSync as readdirSync5,
117
117
  writeFileSync as writeFileSync5,
118
118
  rmSync as rmSync4
119
119
  } from "node:fs";
120
- import { basename as basename6, delimiter, dirname as dirname7, join as join9, relative as relative6, resolve as resolve10, sep as sep5 } from "node:path";
120
+ import { basename as basename7, delimiter, dirname as dirname7, join as join9, relative as relative6, resolve as resolve10, sep as sep5 } from "node:path";
121
121
  import { confirm, select } from "@inquirer/prompts";
122
122
 
123
123
  // src/analyst-tui.ts
@@ -12111,7 +12111,98 @@ function enrichScanCaptureMetadata(graph) {
12111
12111
 
12112
12112
  // src/harness-candidates.ts
12113
12113
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
12114
- import { basename as basename5, dirname as dirname6, extname, resolve as resolve6 } from "node:path";
12114
+ import { basename as basename6, dirname as dirname6, extname as extname2, resolve as resolve6 } from "node:path";
12115
+
12116
+ // src/harness-eval-suites.ts
12117
+ import { basename as basename5, extname } from "node:path";
12118
+ var EVAL_FILE_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".js", ".mjs", ".cjs", ".py", ".yaml", ".yml"]);
12119
+ var EVAL_CASE_DATA_EXTENSIONS = new Set([".json", ".jsonl", ".toml"]);
12120
+ var EVAL_DIR_SEGMENT = /^(?:evals|evaluations)$/i;
12121
+ var EVAL_HARNESS_DIR_SEGMENT = /^eval$/i;
12122
+ var EVAL_FILE_NAME = /(?:^|[._-])evals?(?:[._-]|$)|^promptfoo/i;
12123
+ var DOCS_ROOT = /^(?:docs?|documentation)\//i;
12124
+ var HIDDEN_DIR = /(?:^|\/)\.[^/]+\//;
12125
+ var EVAL_SCRIPT_NAME = /(?:^|[._-])(?:judges?|grader|grading|benchmarks?)(?:[._-]|$)/i;
12126
+ var SCRIPTS_ROOT = /^(?:scripts?|bin)\//i;
12127
+ var EVALUATION_NAME = /(?:^|[._-])evaluat(?:e|ion|ions|or|ors)(?:[._-]|$)/i;
12128
+ var TEST_TREE = /(?:^|\/)(?:tests?|__tests__)\//i;
12129
+ var EVAL_SEED_SCRIPT = /^(?:seed|fetch|scrape|download|populate|extract)[-_]/i;
12130
+ var EVAL_NON_CASE_DATA = /^(?:leaderboards?|results?|scores?|reports?|outputs?|summary|tsconfig|jsconfig|package(?:-lock)?)(?:[._-][^/]*)?\.[^.]+$/i;
12131
+ var TEST_FILE_NAME = /(?:^|\/)(?:test_[^/]*\.py|[^/]*_test\.py|[^/]*\.(?:test|spec)\.[^/]+)$/i;
12132
+ var EVAL_TEST_NAME = /\.evals?\.(?:test|spec)\.[^/.]+$/i;
12133
+ var EVAL_IMPORT = /(?:\bfrom\s+|\bimport\s*\(\s*|\brequire\s*\(\s*|^\s*import\s+)['"][^'"]*(?:\/(?:evals?|evaluations)\/|[\/._-]evals?(?:[._-][^'"/]*)?['"])/m;
12134
+ function evalDirectoryIndex(path) {
12135
+ return path.split("/").slice(0, -1).findIndex((segment) => EVAL_DIR_SEGMENT.test(segment) || EVAL_HARNESS_DIR_SEGMENT.test(segment));
12136
+ }
12137
+ function isEvalHarnessDirectory(path, index) {
12138
+ return index >= 0 && EVAL_HARNESS_DIR_SEGMENT.test(path.split("/")[index] ?? "");
12139
+ }
12140
+ function isEvalSuitePath(path) {
12141
+ const ext = extname(path).toLowerCase();
12142
+ const name = basename5(path);
12143
+ if (DOCS_ROOT.test(path) || HIDDEN_DIR.test(path) || /\.d\.[cm]?ts$/i.test(path) || EVAL_SEED_SCRIPT.test(name))
12144
+ return false;
12145
+ const inEvalDir = evalDirectoryIndex(path) >= 0;
12146
+ if (EVAL_CASE_DATA_EXTENSIONS.has(ext))
12147
+ return inEvalDir && !EVAL_NON_CASE_DATA.test(name);
12148
+ if (!EVAL_FILE_EXTENSIONS.has(ext))
12149
+ return false;
12150
+ return inEvalDir || EVAL_FILE_NAME.test(name) || SCRIPTS_ROOT.test(path) && EVAL_SCRIPT_NAME.test(name) || TEST_TREE.test(path) && EVALUATION_NAME.test(name);
12151
+ }
12152
+ function isTestOfEvalCode(path, text) {
12153
+ return TEST_FILE_NAME.test(path) && !EVAL_TEST_NAME.test(path) && evalDirectoryIndex(path) < 0 && EVAL_IMPORT.test(text);
12154
+ }
12155
+ function isEvalModelSource(path) {
12156
+ if (!isEvalSuitePath(path))
12157
+ return false;
12158
+ return evalDirectoryIndex(path) >= 0 || EVAL_TEST_NAME.test(path) || !TEST_FILE_NAME.test(path);
12159
+ }
12160
+ function evalFileSignal(path, text) {
12161
+ if (!isEvalSuitePath(path) || isTestOfEvalCode(path, text))
12162
+ return;
12163
+ return evalDirectoryIndex(path) < 0 ? "eval.file_name" : "eval.suite_dir";
12164
+ }
12165
+ var EVAL_RUNNER_NAMES = [
12166
+ /^(?:run|runner|main|index|eval|evaluate|cli)\.[^.]+$/i,
12167
+ /^(?:run|runner|main|eval|evaluate)[._-]/i,
12168
+ /(?:^|[._-])evals?(?:[._-]|$)/i
12169
+ ];
12170
+ function evalSuiteKey(path) {
12171
+ const index = evalDirectoryIndex(path);
12172
+ const segments = path.split("/");
12173
+ if (isEvalHarnessDirectory(path, index))
12174
+ return segments.slice(0, index + 1).join("/");
12175
+ if (index < 0 || segments.length - 1 <= index + 1)
12176
+ return path;
12177
+ return segments.slice(0, index + 2).join("/");
12178
+ }
12179
+ function promoteEvalSuites(files, confidence2, naming) {
12180
+ const suites = new Map;
12181
+ for (const file of files) {
12182
+ const key = evalSuiteKey(file.path);
12183
+ suites.set(key, [...suites.get(key) ?? [], file]);
12184
+ }
12185
+ return [...suites].map(([key, members]) => {
12186
+ const sorted = [...members].sort((a, b) => a.path.localeCompare(b.path));
12187
+ const runner = EVAL_RUNNER_NAMES.map((name) => sorted.find((file) => name.test(basename5(file.path)))).find(Boolean) ?? sorted[0];
12188
+ const cited = [runner, ...sorted.filter((file) => file !== runner)];
12189
+ const isDirectory = key !== runner.path;
12190
+ const isHarness = isDirectory && isEvalHarnessDirectory(runner.path, evalDirectoryIndex(runner.path));
12191
+ return {
12192
+ id: naming.artifactId(key),
12193
+ type: "eval",
12194
+ name: isHarness ? `${key} eval harness` : isDirectory ? `${basename5(key)} eval suite` : basename5(key),
12195
+ scope: "global",
12196
+ sourcePath: runner.path,
12197
+ usedByAgentIds: [],
12198
+ confidence: confidence2,
12199
+ confidenceReason: isDirectory ? `Deterministic: eval suite directory ${key} (${members.length} source file${members.length === 1 ? "" : "s"}); not checked by the analyst.` : naming.fileReason(runner),
12200
+ evidence: cited.map((file) => ({ path: file.path, line: 1, reason: `deterministic detector ${file.signal}` }))
12201
+ };
12202
+ });
12203
+ }
12204
+
12205
+ // src/harness-candidates.ts
12115
12206
  var MAX_CANDIDATES_PER_FILE_PER_SIGNAL = 20;
12116
12207
  var MIN_PROMPT_LITERAL_CHARS = 160;
12117
12208
  var CONFIDENCE_WEIGHT = {
@@ -12335,6 +12426,168 @@ var LINE_PATTERNS = [
12335
12426
  }
12336
12427
  ];
12337
12428
  var CODE_EXTENSIONS = new Set([...JS, ...PY]);
12429
+ var PROVIDER_MODEL_ID = /^(?:anthropic|openai|google|meta-llama|mistralai|deepseek|x-ai|perplexity|qwen|cohere|moonshotai|z-ai|minimax|amazon|nvidia|microsoft|ai21|nousresearch|bytedance|baidu|inception|openrouter)\/[a-z0-9][\w.:-]*$/i;
12430
+ var PROVIDER_COLON_MODEL_ID = /^(?:openai|anthropic|google_genai|google_vertexai|google|groq|mistralai|bedrock|bedrock_converse|azure_openai|ollama|fireworks|together|deepseek|xai|perplexity|cohere|openrouter):([a-z0-9][\w./:-]*)$/i;
12431
+ var CHOSEN_VALUE_PREFIX = /(?:\b[A-Za-z_]\w*\s*=|\bmodel\w*["']?\s*:)\s*$/i;
12432
+ function isChosenProviderColonModel(value, before) {
12433
+ const model = PROVIDER_COLON_MODEL_ID.exec(value)?.[1];
12434
+ return Boolean(model && PROVIDER_MODEL_NAME.test(model) && CHOSEN_VALUE_PREFIX.test(before));
12435
+ }
12436
+ var PROVIDER_MODEL_NAME = /\d|claude|gpt|gemini|gemma|llama|mistral|mixtral|qwen|deepseek|grok|sonar|kimi|glm|command|minimax|auto/i;
12437
+ var BARE_MODEL_ID = /^(?:claude-|gpt-|chatgpt-|gemini-|text-embedding-|deepseek-|mistral-|llama-?|grok-|command-|qwen|kimi-|glm-)[\w.:-]*$/i;
12438
+ var O_SERIES_MODEL_ID = /^o[1-9](?:-(?:mini|pro|preview|deep-research))*(?:-\d{4}-\d{2}-\d{2})?$/i;
12439
+ var NOT_MODEL_ID = /^(?:claude-(?:code|ai|desktop|agent-sdk)|gpt-tokenizer|gpt-3-encoder|gemini-cli)\b/i;
12440
+ var FILE_NAME_SUFFIX = /\.(?:[cm]?[jt]sx?|json|ya?ml|toml|md|txt|py|png|jpe?g|gif|svg|webp|css|html?)$/i;
12441
+ var MODEL_CANDIDATE_VALUE = /^[A-Za-z0-9][\w./:-]{2,80}$/;
12442
+ var IMPORT_LINE = /^\s*(?:import\b|export\s+(?:\*|\{[^}]*\})\s*from\b|from\s+\S+\s+import\b)|\brequire\s*\(|\bimport\s*\(/;
12443
+ var CLASS_ATTRIBUTE_PREFIX = /\bclass(?:Name|List)?\s*[=:]\s*\{?\s*$/;
12444
+ var NON_MODEL_SOURCE_PATH = /\.d\.[cm]?ts$|\.min\.[cm]?js$|(?:^|\/)(?:public|static)\/|(?:^|\/)(?:package(?:-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$/i;
12445
+ var MODEL_ID_EXTENSIONS = new Set([...CODE_EXTENSIONS, ".json", ".jsonc", ".yaml", ".yml", ".toml"]);
12446
+ function isModelIdLiteral(value) {
12447
+ if (value.length < 4 || value.length > 80 || /\s/.test(value) || NOT_MODEL_ID.test(value) || FILE_NAME_SUFFIX.test(value))
12448
+ return false;
12449
+ if (PROVIDER_MODEL_ID.test(value))
12450
+ return PROVIDER_MODEL_NAME.test(value.slice(value.indexOf("/") + 1));
12451
+ if (O_SERIES_MODEL_ID.test(value))
12452
+ return true;
12453
+ return BARE_MODEL_ID.test(value) && /\d/.test(value);
12454
+ }
12455
+ var HASH_COMMENT_EXTENSIONS = new Set([...PY, ".yaml", ".yml", ".toml"]);
12456
+ var REGEX_PRECEDER = /(?:^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|case|in|of|delete|void|throw|new|yield|await))\s*$/;
12457
+ function startsRegexLiteral(line, start) {
12458
+ const next = line[start + 1];
12459
+ if (next === undefined || next === "/" || next === "*" || next === ">" || line[start - 1] === "<")
12460
+ return false;
12461
+ return REGEX_PRECEDER.test(line.slice(0, start));
12462
+ }
12463
+ function regexLiteralEnd(line, start) {
12464
+ let inClass = false;
12465
+ for (let j = start + 1;j < line.length; j += 1) {
12466
+ const ch = line[j];
12467
+ if (ch === "\\") {
12468
+ j += 1;
12469
+ continue;
12470
+ }
12471
+ if (ch === "[")
12472
+ inClass = true;
12473
+ else if (ch === "]")
12474
+ inClass = false;
12475
+ else if (ch === "/" && !inClass)
12476
+ return j + 1;
12477
+ }
12478
+ return line.length;
12479
+ }
12480
+ function stringLiteralsInLine(line, state, hashComments) {
12481
+ const out = [];
12482
+ const closeAt = (from, close, escapes) => {
12483
+ for (let j = from;j < line.length; j += 1) {
12484
+ if (escapes && line[j] === "\\") {
12485
+ j += 1;
12486
+ continue;
12487
+ }
12488
+ if (line.startsWith(close, j))
12489
+ return j;
12490
+ }
12491
+ return -1;
12492
+ };
12493
+ let i = 0;
12494
+ if (state.open) {
12495
+ const close = state.open === "/*" ? "*/" : state.open;
12496
+ const at = closeAt(0, close, state.open !== "/*");
12497
+ if (at < 0)
12498
+ return out;
12499
+ i = at + close.length;
12500
+ state.open = null;
12501
+ }
12502
+ while (i < line.length) {
12503
+ const ch = line[i];
12504
+ if (hashComments ? ch === "#" : line.startsWith("//", i))
12505
+ break;
12506
+ if (!hashComments && line.startsWith("/*", i)) {
12507
+ const at2 = closeAt(i + 2, "*/", false);
12508
+ if (at2 < 0) {
12509
+ state.open = "/*";
12510
+ break;
12511
+ }
12512
+ i = at2 + 2;
12513
+ continue;
12514
+ }
12515
+ if (!hashComments && ch === "/" && startsRegexLiteral(line, i)) {
12516
+ i = regexLiteralEnd(line, i);
12517
+ continue;
12518
+ }
12519
+ if (ch !== '"' && ch !== "'" && ch !== "`") {
12520
+ i += 1;
12521
+ continue;
12522
+ }
12523
+ const triple = hashComments && line.startsWith(ch.repeat(3), i);
12524
+ const quote = triple ? ch.repeat(3) : ch;
12525
+ const at = closeAt(i + quote.length, quote, true);
12526
+ if (at < 0) {
12527
+ if (triple || ch === "`")
12528
+ state.open = quote;
12529
+ break;
12530
+ }
12531
+ out.push({ value: line.slice(i + quote.length, at), index: i, end: at + quote.length });
12532
+ i = at + quote.length;
12533
+ }
12534
+ return out;
12535
+ }
12536
+ function modelIdLiteralsInScannedLine(line, state, hashComments) {
12537
+ const literals = stringLiteralsInLine(line, state, hashComments);
12538
+ if (line.length > 4000 || IMPORT_LINE.test(line))
12539
+ return [];
12540
+ return literals.filter((literal) => {
12541
+ if (!MODEL_CANDIDATE_VALUE.test(literal.value))
12542
+ return false;
12543
+ const before = line.slice(0, literal.index);
12544
+ return (isModelIdLiteral(literal.value) || isChosenProviderColonModel(literal.value, before)) && !CLASS_ATTRIBUTE_PREFIX.test(before);
12545
+ });
12546
+ }
12547
+ function modelIdLiteralsInLines(lines, path) {
12548
+ const hashComments = HASH_COMMENT_EXTENSIONS.has(extname2(path).toLowerCase());
12549
+ const state = { open: null };
12550
+ return lines.map((line) => modelIdLiteralsInScannedLine(line, state, hashComments));
12551
+ }
12552
+ var HIGH_CONFIDENCE_MODEL_PATTERNS = LINE_PATTERNS.filter((pattern) => pattern.kind === "model_config" && pattern.confidence === "high");
12553
+ function detectModelIdLiterals(file, out) {
12554
+ if (!MODEL_ID_EXTENSIONS.has(extname2(file.path).toLowerCase()) || NON_MODEL_SOURCE_PATH.test(file.path))
12555
+ return;
12556
+ const lines = file.text.split(`
12557
+ `);
12558
+ const literals = modelIdLiteralsInLines(lines, file.path);
12559
+ for (let i = 0;i < lines.length; i += 1) {
12560
+ const line = lines[i];
12561
+ if (literals[i].length === 0)
12562
+ continue;
12563
+ if (HIGH_CONFIDENCE_MODEL_PATTERNS.some((pattern) => pattern.pattern.test(line)))
12564
+ continue;
12565
+ out.push({
12566
+ kind: "model_config",
12567
+ path: file.path,
12568
+ startLine: i + 1,
12569
+ endLine: i + 1,
12570
+ confidence: "high",
12571
+ signal: "model.id_literal",
12572
+ excerpt: normalizeExcerpt(line)
12573
+ });
12574
+ }
12575
+ }
12576
+ function detectEvalFile(file, out) {
12577
+ const signal = evalFileSignal(file.path, file.text);
12578
+ if (!signal)
12579
+ return;
12580
+ out.push({
12581
+ kind: "eval_file",
12582
+ path: file.path,
12583
+ startLine: 1,
12584
+ endLine: Math.max(1, file.text.split(`
12585
+ `).length),
12586
+ confidence: "high",
12587
+ signal,
12588
+ excerpt: normalizeExcerpt(file.text.slice(0, 300))
12589
+ });
12590
+ }
12338
12591
  var PROMPT_DOC_EXTENSIONS = new Set([".md", ".mdc", ".txt", ".prompt", ".jinja", ".jinja2", ".j2", ".hbs", ".tpl"]);
12339
12592
  var UPPERCASE_DOC_NAME = /^[A-Z0-9][A-Z0-9_-]*\.(?:md|mdc|txt)$/;
12340
12593
  var NON_PROMPT_DOC_BASENAMES = new Set([
@@ -12396,7 +12649,7 @@ function lineAtIndex(offsets, index) {
12396
12649
  return lo + 1;
12397
12650
  }
12398
12651
  function detectLinePatterns(file, out) {
12399
- const ext = extname(file.path).toLowerCase();
12652
+ const ext = extname2(file.path).toLowerCase();
12400
12653
  const lines = file.text.split(`
12401
12654
  `);
12402
12655
  const perSignal = new Map;
@@ -12473,7 +12726,7 @@ function detectAdjacentStringBlocks(file, out) {
12473
12726
  }
12474
12727
  var PROMPT_MODULE_NAME_HINT = /(^|[._-])(system[._-]?)?prompts?([._-]|$)/i;
12475
12728
  function detectPromptModule(file, out) {
12476
- const base = basename5(file.path, extname(file.path));
12729
+ const base = basename6(file.path, extname2(file.path));
12477
12730
  if (!PROMPT_MODULE_NAME_HINT.test(base))
12478
12731
  return;
12479
12732
  out.push({
@@ -12488,7 +12741,7 @@ function detectPromptModule(file, out) {
12488
12741
  });
12489
12742
  }
12490
12743
  function detectPromptLiterals(file, out) {
12491
- const ext = extname(file.path).toLowerCase();
12744
+ const ext = extname2(file.path).toLowerCase();
12492
12745
  const offsets = buildLineIndex(file.text);
12493
12746
  let found = 0;
12494
12747
  const record = (startIndex, endIndex, content, signal) => {
@@ -12533,10 +12786,10 @@ function detectPromptLiterals(file, out) {
12533
12786
  }
12534
12787
  }
12535
12788
  function detectPromptFile(file, out) {
12536
- const ext = extname(file.path).toLowerCase();
12789
+ const ext = extname2(file.path).toLowerCase();
12537
12790
  if (!PROMPT_DOC_EXTENSIONS.has(ext))
12538
12791
  return;
12539
- const base = basename5(file.path).toLowerCase();
12792
+ const base = basename6(file.path).toLowerCase();
12540
12793
  if (NON_PROMPT_DOC_BASENAMES.has(base))
12541
12794
  return;
12542
12795
  if (/(^|\/)(docs?|documentation|examples?|\.github)\//i.test(file.path))
@@ -12553,7 +12806,7 @@ function detectPromptFile(file, out) {
12553
12806
  } else if (PROMPTS_DIR.test(dir)) {
12554
12807
  confidence2 = "high";
12555
12808
  signal = "prompt_file.prompts_dir";
12556
- } else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(basename5(file.path))) {
12809
+ } else if (dirHint && score >= 3 && !UPPERCASE_DOC_NAME.test(basename6(file.path))) {
12557
12810
  confidence2 = "medium";
12558
12811
  signal = "prompt_file.agent_dir_instruction_text";
12559
12812
  } else if (score >= 5) {
@@ -12574,10 +12827,10 @@ function detectPromptFile(file, out) {
12574
12827
  });
12575
12828
  }
12576
12829
  function detectPromptLoaderReferences(file, filesByPath, out) {
12577
- if (!LOADER_CODE_EXTENSIONS.has(extname(file.path).toLowerCase()) && extname(file.path).toLowerCase() !== ".md")
12830
+ if (!LOADER_CODE_EXTENSIONS.has(extname2(file.path).toLowerCase()) && extname2(file.path).toLowerCase() !== ".md")
12578
12831
  return;
12579
12832
  const extension = "(?:\\.txt|\\.md)";
12580
- const pattern = extname(file.path).toLowerCase() === ".rs" ? new RegExp(`\\binclude_str!\\s*\\(\\s*["']([^"']+${extension})["']\\s*\\)`, "g") : new RegExp(`\\b(?:Template\\.from_file|from_file|read_text|readFileSync)\\s*\\(\\s*["']([^"']+${extension})["']`, "g");
12833
+ const pattern = extname2(file.path).toLowerCase() === ".rs" ? new RegExp(`\\binclude_str!\\s*\\(\\s*["']([^"']+${extension})["']\\s*\\)`, "g") : new RegExp(`\\b(?:Template\\.from_file|from_file|read_text|readFileSync)\\s*\\(\\s*["']([^"']+${extension})["']`, "g");
12581
12834
  let found = 0;
12582
12835
  let match;
12583
12836
  while (found < MAX_CANDIDATES_PER_FILE_PER_SIGNAL && (match = pattern.exec(file.text)) !== null) {
@@ -12585,7 +12838,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
12585
12838
  const target = normalizeLoaderTarget(file.path, reference);
12586
12839
  if (!target || !filesByPath.has(target))
12587
12840
  continue;
12588
- const targetBase = basename5(target).toLowerCase();
12841
+ const targetBase = basename6(target).toLowerCase();
12589
12842
  const highConfidence = RUST_PROMPT_PATH_HINT.test(target) || /Template\.from_file/i.test(match[0]);
12590
12843
  if (!highConfidence)
12591
12844
  continue;
@@ -12597,7 +12850,7 @@ function detectPromptLoaderReferences(file, filesByPath, out) {
12597
12850
  endLine: 1,
12598
12851
  confidence: "high",
12599
12852
  signal: /Template\.from_file/i.test(match[0]) ? "prompt_file.template_loader_reference" : "prompt_file.loader_reference",
12600
- excerpt: normalizeExcerpt(`${basename5(file.path)} loads ${targetBase}`)
12853
+ excerpt: normalizeExcerpt(`${basename6(file.path)} loads ${targetBase}`)
12601
12854
  });
12602
12855
  }
12603
12856
  }
@@ -12625,10 +12878,12 @@ function detectHarnessCandidatesInFiles(files) {
12625
12878
  if (typeof file.text !== "string" || file.text.length === 0)
12626
12879
  continue;
12627
12880
  const scoped = { path: file.path, text: file.text };
12628
- const ext = extname(file.path).toLowerCase();
12881
+ const ext = extname2(file.path).toLowerCase();
12629
12882
  if (CODE_EXTENSIONS.has(ext) || ext === ".json" || ext === ".yaml" || ext === ".yml" || ext === ".toml") {
12630
12883
  detectLinePatterns(scoped, candidates);
12631
12884
  }
12885
+ detectModelIdLiterals(scoped, candidates);
12886
+ detectEvalFile(scoped, candidates);
12632
12887
  if (CODE_EXTENSIONS.has(ext)) {
12633
12888
  detectAdjacentStringBlocks(scoped, candidates);
12634
12889
  detectPromptModule(scoped, candidates);
@@ -12658,7 +12913,7 @@ function detectHarnessCandidates(rootDir) {
12658
12913
  const skipped = [];
12659
12914
  const root = resolve6(rootDir);
12660
12915
  const inventory = inventoryFiles(root, skipped, {
12661
- includeFile: (path) => PROMPT_DOC_EXTENSIONS.has(extname(path).toLowerCase()) || [".json", ".yaml", ".yml"].includes(extname(path).toLowerCase()) || extname(path).toLowerCase() === ".rs" && RUST_PROMPT_PATH_HINT.test(path)
12916
+ includeFile: (path) => PROMPT_DOC_EXTENSIONS.has(extname2(path).toLowerCase()) || [".json", ".jsonc", ".jsonl", ".yaml", ".yml", ".toml"].includes(extname2(path).toLowerCase()) || extname2(path).toLowerCase() === ".rs" && RUST_PROMPT_PATH_HINT.test(path)
12662
12917
  });
12663
12918
  const { candidates, files } = detectHarnessCandidatesInFiles(inventory);
12664
12919
  const durationMs = Math.max(1, Date.now() - startedAt);
@@ -12691,7 +12946,7 @@ function writeHarnessCandidatesReport(report, outPath) {
12691
12946
 
12692
12947
  // src/harness-prompt-sources.ts
12693
12948
  import { createHash as createHash4 } from "node:crypto";
12694
- import { extname as extname6 } from "node:path";
12949
+ import { extname as extname7 } from "node:path";
12695
12950
 
12696
12951
  // src/prompt-source-verification/index.ts
12697
12952
  import { readFileSync as readFileSync10 } from "node:fs";
@@ -12699,7 +12954,7 @@ import { z } from "zod";
12699
12954
 
12700
12955
  // src/prompt-source-validation.ts
12701
12956
  import { existsSync as existsSync7, lstatSync as lstatSync5 } from "node:fs";
12702
- import { extname as extname2, relative as relative4, resolve as resolve7 } from "node:path";
12957
+ import { extname as extname3, relative as relative4, resolve as resolve7 } from "node:path";
12703
12958
  function checkPromptMapping(rootDir, unknownMapping) {
12704
12959
  const repairs = [];
12705
12960
  const unresolved = [];
@@ -12779,12 +13034,12 @@ function normalizePromptMapping(value) {
12779
13034
  return normalized;
12780
13035
  }
12781
13036
  function isPromptDefinitionPath(path) {
12782
- return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(extname2(path).toLowerCase());
13037
+ return /(^|\/)(?:prompts?|templates?)(?:\/|\.)/i.test(path) || /\.prompt\.(?:md|txt|ya?ml|json)$/i.test(path) || [".jinja", ".j2", ".mustache", ".hbs"].includes(extname3(path).toLowerCase());
12783
13038
  }
12784
13039
 
12785
13040
  // src/prompt-source-verification/evidence.ts
12786
13041
  import { readFileSync as readFileSync9 } from "node:fs";
12787
- import { extname as extname5 } from "node:path";
13042
+ import { extname as extname6 } from "node:path";
12788
13043
 
12789
13044
  // src/prompt-source-verification/paths.ts
12790
13045
  import { lstatSync as lstatSync6, realpathSync as realpathSync6 } from "node:fs";
@@ -12812,13 +13067,13 @@ function safePromptEvidencePath(rootDir, path) {
12812
13067
  // src/prompt-source-verification/sink-proof.ts
12813
13068
  var import_typescript = __toESM(require_typescript(), 1);
12814
13069
  import { execFileSync as execFileSync2 } from "node:child_process";
12815
- import { extname as extname3 } from "node:path";
13070
+ import { extname as extname4 } from "node:path";
12816
13071
  import { readFileSync as readFileSync7 } from "node:fs";
12817
13072
  function proveSink(root, sink) {
12818
13073
  const file = safePromptEvidencePath(root, sink.path);
12819
13074
  const cache = new Map;
12820
13075
  if (!cache.has(file)) {
12821
- if (extname3(file) === ".py") {
13076
+ if (extname4(file) === ".py") {
12822
13077
  try {
12823
13078
  const rows = JSON.parse(execFileSync2("python3", ["-I", "-c", PYTHON_PROOF], { input: readFileSync7(file, "utf8"), encoding: "utf8", timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }));
12824
13079
  cache.set(file, new Map(Object.entries(rows).map(([line, proof]) => [Number(line), proof])));
@@ -12988,7 +13243,7 @@ print(json.dumps(rows))
12988
13243
 
12989
13244
  // src/prompt-source-location.ts
12990
13245
  import { readFileSync as readFileSync8 } from "node:fs";
12991
- import { extname as extname4 } from "node:path";
13246
+ import { extname as extname5 } from "node:path";
12992
13247
  function structuredExcerpt(text, source) {
12993
13248
  if (source.selector?.pointer === undefined)
12994
13249
  return { line: 1, endLine: text.split(`
@@ -13028,7 +13283,7 @@ function structuredExcerpt(text, source) {
13028
13283
  }
13029
13284
  function promptDefinitionExcerpt(rootDir, source) {
13030
13285
  const file = safePromptEvidencePath(rootDir, source.path);
13031
- const ext = extname4(file).toLowerCase();
13286
+ const ext = extname5(file).toLowerCase();
13032
13287
  if (ext === ".py") {
13033
13288
  const extraction = extractPythonSource(rootDir, file, source.selector?.symbol);
13034
13289
  if (!extraction.range)
@@ -13052,7 +13307,7 @@ function promptDefinitionRange(rootDir, source) {
13052
13307
  // src/prompt-source-verification/evidence.ts
13053
13308
  function verifyPromptSourceEvidence(rootDir, source) {
13054
13309
  const checked = checkPromptMapping(rootDir, source);
13055
- const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(extname5(source.path).toLowerCase());
13310
+ const dedicated = isPromptDefinitionPath(source.path) && [".md", ".txt", ".jinja", ".j2", ".mustache", ".hbs"].includes(extname6(source.path).toLowerCase());
13056
13311
  return {
13057
13312
  accepted: checked.ok && dedicated && checked.kind !== "builder",
13058
13313
  reason: dedicated ? "Dedicated authored prompt file" : "Candidate requires independent semantic review",
@@ -13306,7 +13561,7 @@ function discoverHarnessPromptSources(rootDir, existing = []) {
13306
13561
  }
13307
13562
  try {
13308
13563
  let found = [];
13309
- const ext = extname6(path).toLowerCase();
13564
+ const ext = extname7(path).toLowerCase();
13310
13565
  if (CODE.has(ext)) {
13311
13566
  if (attempts++ >= MAX_CODE_PROMPT_PATHS) {
13312
13567
  issues.push({ path, reason: "Code prompt scan limit (64 files) reached; deferred to analyst review" });
@@ -13574,8 +13829,9 @@ function applyPromptDiscovery(graph, discovery) {
13574
13829
  }
13575
13830
 
13576
13831
  // src/harness-candidate-promotion.ts
13577
- import { readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
13578
- import { extname as extname7, posix, resolve as resolve9 } from "node:path";
13832
+ import { createHash as createHash5 } from "node:crypto";
13833
+ import { readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync3 } from "node:fs";
13834
+ import { extname as extname8, posix, resolve as resolve9 } from "node:path";
13579
13835
 
13580
13836
  // src/harness-js-source.ts
13581
13837
  var import_typescript2 = __toESM(require_typescript(), 1);
@@ -13944,7 +14200,11 @@ var SIGNAL_DESCRIPTIONS = {
13944
14200
  "prompt_file.agent_dir_instruction_text": "instruction-like text in an agent directory",
13945
14201
  "prompt_file.loader_reference": "a file loaded as a prompt by code",
13946
14202
  "prompt_file.template_loader_reference": "a file loaded as a prompt template by code",
13947
- "model.assignment": "a model id assigned in code"
14203
+ "model.assignment": "a model id assigned in code",
14204
+ "model.registry_key": "a model registry key",
14205
+ "model.id_literal": "a model id string literal",
14206
+ "eval.suite_dir": "a file under an eval directory",
14207
+ "eval.file_name": "a file named as an eval"
13948
14208
  };
13949
14209
  function detectorConfidenceReason(candidate) {
13950
14210
  const what = SIGNAL_DESCRIPTIONS[candidate.signal] ?? `the ${candidate.signal} pattern`;
@@ -14420,7 +14680,7 @@ function localNameIn(specifiers, identifier) {
14420
14680
  function importedLocalName(importerPath, lines, identifier, definitionPath, exportsDefault = false) {
14421
14681
  const text = lines.join(`
14422
14682
  `);
14423
- const python = extname7(importerPath) === ".py";
14683
+ const python = extname8(importerPath) === ".py";
14424
14684
  if (python) {
14425
14685
  for (const match of text.matchAll(/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm)) {
14426
14686
  if (!specifierResolvesTo(match[1], importerPath, definitionPath, true))
@@ -14480,7 +14740,7 @@ function calleeImportedFromModelFile(callee, path, lines, modelFiles) {
14480
14740
  const base = callee.split(".")[0];
14481
14741
  const text = lines.join(`
14482
14742
  `);
14483
- const python = extname7(path) === ".py";
14743
+ const python = extname8(path) === ".py";
14484
14744
  const statements = python ? [/^[ \t]*from\s+([.\w]+)\s+import\s+(\([^)]*\)|[^\n]*)/gm] : [
14485
14745
  /\bimport\s+(?:type\s+)?(?:([A-Za-z_$][\w$]*)\s*,?\s*)?(?:\{([^}]*)\}|\*\s*as\s+([A-Za-z_$][\w$]*))?\s*from\s*["']([^"']+)["']/g,
14486
14746
  /\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*(?:await\s+)?(?:require|import)\(\s*["']([^"']+)["']\s*\)/g
@@ -14703,7 +14963,7 @@ function hasNearbyRole(lines, line) {
14703
14963
  return false;
14704
14964
  }
14705
14965
  function isDirectModelInput(key, value, path, lines, line, modelFiles) {
14706
- if (CONFIG_EXTENSIONS.has(extname7(path).toLowerCase())) {
14966
+ if (CONFIG_EXTENSIONS.has(extname8(path).toLowerCase())) {
14707
14967
  return DIRECT_PROMPT_KEYS.has(key) && value.length >= MIN_CONFIG_PROMPT_CHARS;
14708
14968
  }
14709
14969
  if (modelFiles.has(path)) {
@@ -14743,7 +15003,7 @@ function fileReachesModel(path, lines, modelFiles) {
14743
15003
  return true;
14744
15004
  if (lines.some((line) => MODEL_ENDPOINT.test(line)))
14745
15005
  return true;
14746
- const python = extname7(path) === ".py";
15006
+ const python = extname8(path) === ".py";
14747
15007
  return importSpecifiers(lines).some((spec) => [...modelFiles].some((modelFile) => specifierResolvesTo(spec, path, modelFile, python)));
14748
15008
  }
14749
15009
  function modelFileImports(path, rootDir, modelFiles, fileCache) {
@@ -14751,7 +15011,7 @@ function modelFileImports(path, rootDir, modelFiles, fileCache) {
14751
15011
  if (modelFile === path)
14752
15012
  return false;
14753
15013
  const lines = readFileLines(rootDir, modelFile, fileCache);
14754
- return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path, extname7(modelFile) === ".py")));
15014
+ return Boolean(lines && importSpecifiers(lines).some((spec) => specifierResolvesTo(spec, modelFile, path, extname8(modelFile) === ".py")));
14755
15015
  });
14756
15016
  }
14757
15017
  var PASS_THROUGH_CALLEE = /^(?:Object\.(?:freeze|seal|assign)|structuredClone|Promise\.resolve|JSON\.stringify|(?:_\.|lodash\.)?(?:cloneDeep|clone|merge)|deepFreeze|defu)$/;
@@ -14821,7 +15081,7 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
14821
15081
  use = { path: candidate.path, line: definition.line, key: "system-injection" };
14822
15082
  }
14823
15083
  const jsSource = jsSources.get(`${rootDir}::${candidate.path}`);
14824
- if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(extname7(candidate.path).toLowerCase())) {
15084
+ if (!use && definition.key && !PROMPT_CONFIG_KEYS.has(definition.key) && !CONFIG_EXTENSIONS.has(extname8(candidate.path).toLowerCase())) {
14825
15085
  const exportUsed = () => modelFileImports(candidate.path, rootDir, files.model, fileCache);
14826
15086
  if (jsSource) {
14827
15087
  const consumer = keyedValueConsumer(jsSource, definition.line, definition.key, definition.column);
@@ -14871,63 +15131,248 @@ function promotePromptLiteralCandidate(candidate, rootDir, confidence2, fileCach
14871
15131
  body: definition.text
14872
15132
  };
14873
15133
  }
14874
- function promoteModelConfigCandidate(candidate, rootDir, confidence2, fileCache) {
14875
- const lines = readFileLines(rootDir, candidate.path, fileCache);
14876
- const sourceLine = lines?.[candidate.startLine - 1] ?? "";
14877
- const assignment = MODEL_ASSIGNMENT_VALUE.exec(sourceLine);
14878
- const [field, assigned] = [assignment?.[1], assignment?.[2]];
14879
- if (field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(assigned ?? ""))
14880
- return;
14881
- const registryKey = candidate.signal === "model.registry_key" ? REGISTRY_KEY.exec(sourceLine)?.[1] : undefined;
14882
- const model = (assigned && MODEL_STRING.exec(assigned)?.[1]) ?? registryKey ?? MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
14883
- if (!model)
14884
- return;
14885
- if (MODEL_FAMILY_ALIAS.test(model.split("/").pop() ?? model))
14886
- return;
14887
- if (TEST_PATH2.test(candidate.path) && !usedOutsideTests(rootDir, model))
14888
- return;
14889
- return {
14890
- id: stableId("model_config", `${pathStem(candidate.path)}:L${candidate.startLine}`),
14891
- type: "model_config",
14892
- name: model,
14893
- scope: "shared",
14894
- usedByAgentIds: [],
14895
- confidence: confidence2,
14896
- confidenceReason: detectorConfidenceReason(candidate),
14897
- evidence: candidateEvidence(candidate)
14898
- };
15134
+ var ALIAS_KEY_SAME_LINE = /^\s*:\s*\{[^}]*?["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]/i;
15135
+ var BLOCK_KEY = /^\s*:\s*\{\s*$/;
15136
+ var MODEL_FIELD = /["']?\b(?:model|model_name|model_id|modelName|modelId|[A-Z_]*MODEL[A-Z_]*)["']?\s*[:=]\s*["'`]/i;
15137
+ var ASSIGNED_FIELD = /([A-Za-z_$][\w$]*)["']?\s*[:=]\s*(?:[A-Za-z_$][\w$.()]*\(\s*)?$/;
15138
+ var COMPARED_BEFORE = /(?:\.(?:startsWith|endsWith|includes|indexOf|match|test)\(\s*|[!=]==?\s*|\bcase\s+|[\w$\])]\[\s*)$/;
15139
+ var COMPARED_AFTER = /^\s*[!=]==?/;
15140
+ function isComparedLiteral(sourceLine, literal) {
15141
+ return COMPARED_BEFORE.test(sourceLine.slice(0, literal.index)) || COMPARED_AFTER.test(sourceLine.slice(literal.end));
15142
+ }
15143
+ var ALIAS_BLOCK_MAX_LINES = 200;
15144
+ function isModelAliasKey(lines, row, literal, path = "") {
15145
+ const after = lines[row].slice(literal.end);
15146
+ if (ALIAS_KEY_SAME_LINE.test(after))
15147
+ return true;
15148
+ const separator = /^\s*:\s*/.exec(after);
15149
+ if (separator && modelIdLiteralsAt(lines, path, row).some((other) => other.index === literal.end + separator[0].length))
15150
+ return true;
15151
+ if (!BLOCK_KEY.test(after))
15152
+ return false;
15153
+ let depth = 1;
15154
+ for (const next of lines.slice(row + 1, row + 1 + ALIAS_BLOCK_MAX_LINES)) {
15155
+ if (depth === 1 && MODEL_FIELD.test(next))
15156
+ return true;
15157
+ for (const ch of next) {
15158
+ if (ch === "{")
15159
+ depth += 1;
15160
+ else if (ch === "}")
15161
+ depth -= 1;
15162
+ }
15163
+ if (depth <= 0)
15164
+ return false;
15165
+ }
15166
+ return false;
14899
15167
  }
14900
- var nonTestSourcePaths = new Map;
14901
- var modelUsedOutsideTests = new Map;
14902
- var MAX_MODEL_SOURCE_BYTES = 1024 * 1024;
14903
- var MODEL_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
14904
- var NON_SOURCE_DIR = /(^|\/)(?:docs?|\.moda)\//i;
14905
- function usedOutsideTests(rootDir, modelId) {
14906
- let results = modelUsedOutsideTests.get(rootDir);
14907
- if (!results) {
14908
- results = new Map;
14909
- modelUsedOutsideTests.set(rootDir, results);
15168
+ var lineLiterals = new WeakMap;
15169
+ function modelIdLiteralsAt(lines, path, row) {
15170
+ let literals = lineLiterals.get(lines);
15171
+ if (!literals) {
15172
+ literals = modelIdLiteralsInLines(lines, path);
15173
+ lineLiterals.set(lines, literals);
15174
+ }
15175
+ return literals[row] ?? [];
15176
+ }
15177
+ function modelIdsOnCandidateLine(candidate, lines) {
15178
+ const rows = lines ?? [];
15179
+ const row = candidate.startLine - 1;
15180
+ const sourceLine = rows[row] ?? "";
15181
+ const ids = [];
15182
+ const literals = modelIdLiteralsAt(rows, candidate.path, row);
15183
+ const isAliasKey = (value) => literals.some((literal) => literal.value === value && isModelAliasKey(rows, row, literal, candidate.path));
15184
+ if (candidate.signal !== "model.id_literal") {
15185
+ const assignment = MODEL_ASSIGNMENT_VALUE.exec(sourceLine);
15186
+ const [field, assigned] = [assignment?.[1], assignment?.[2]];
15187
+ const preferenceOnly = Boolean(field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(assigned ?? ""));
15188
+ const registryKey = candidate.signal === "model.registry_key" ? REGISTRY_KEY.exec(sourceLine)?.[1] : undefined;
15189
+ const primary = preferenceOnly ? undefined : (assigned && MODEL_STRING.exec(assigned)?.[1]) ?? registryKey ?? MODEL_STRING.exec(sourceLine)?.[1] ?? MODEL_STRING.exec(candidate.excerpt)?.[1];
15190
+ const compared = (value) => literals.some((literal) => literal.value === value && isComparedLiteral(sourceLine, literal));
15191
+ if (primary && !isAliasKey(primary) && !compared(primary))
15192
+ ids.push(primary);
15193
+ }
15194
+ for (const literal of literals) {
15195
+ const { value, index } = literal;
15196
+ if (ids.includes(value) || isModelAliasKey(rows, row, literal, candidate.path))
15197
+ continue;
15198
+ if (isComparedLiteral(sourceLine, literal))
15199
+ continue;
15200
+ const field = ASSIGNED_FIELD.exec(sourceLine.slice(0, index))?.[1];
15201
+ if (field && MODEL_PREFERENCE_FIELD.test(field) && !FULL_MODEL_ID.test(value))
15202
+ continue;
15203
+ ids.push(value);
14910
15204
  }
14911
- const cached = results.get(modelId);
14912
- if (cached !== undefined)
15205
+ return ids.filter((id) => !MODEL_FAMILY_ALIAS.test(id.split("/").pop() ?? id));
15206
+ }
15207
+ var OTHER_TEST_PATH = /(?:^|\/)(?:e2e|testdata|__mocks__|spec)\/|\.stories\.[^/.]+$|(?:^|\/)(?:test_[^/]*|[^/]*_test|conftest)\.py$/i;
15208
+ var NON_SOURCE_DIR = /(^|\/)(?:docs?|\.moda)\//i;
15209
+ var EMPTY_MODEL_PATH_CONTEXT = { productionScripts: new Set, rewritingPackages: new Set };
15210
+ function isHarnessModelPath(path, context = EMPTY_MODEL_PATH_CONTEXT) {
15211
+ if (NON_SOURCE_DIR.test(path))
15212
+ return false;
15213
+ if (isEvalModelSource(path))
15214
+ return true;
15215
+ if (SCRIPTS_DIR.test(path) && !isUnderAny(path, context.productionScripts))
15216
+ return false;
15217
+ if (isUnderAny(path, context.rewritingPackages) && !SERVER_SIDE_PATH.test(path))
15218
+ return false;
15219
+ return evalDirectoryIndex(path) < 0 && !TEST_PATH2.test(path) && !OTHER_TEST_PATH.test(path);
15220
+ }
15221
+ function modelPathContext(rootDir) {
15222
+ return { productionScripts: productionScriptPaths(rootDir), rewritingPackages: modelRewritingPackages(rootDir) };
15223
+ }
15224
+ var SERVER_SIDE_PATH = /(?:^|\/)(?:server|host|api)\/|[.+]server\.[cm]?[jt]s$/;
15225
+ var MODEL_REWRITE_CALL = /\bmodel\s*[:=]\s*[a-z]\w*\(\s*(?:\w+\.)*(?:requested\w*|\w*[mM]odel\w*)\s*,/;
15226
+ var REWRITE_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|svelte|vue|py)$/i;
15227
+ var modelRewriteCache = new Map;
15228
+ function modelRewritingPackages(rootDir) {
15229
+ const root = resolve9(rootDir);
15230
+ const cached = modelRewriteCache.get(root);
15231
+ if (cached)
14913
15232
  return cached;
14914
- let paths = nonTestSourcePaths.get(rootDir);
14915
- if (!paths) {
14916
- paths = inventoryFiles(rootDir, [], { includeFile: (path) => MODEL_SOURCE_FILE.test(path) }).map((file) => file.path).filter((path) => !TEST_PATH2.test(path) && MODEL_SOURCE_FILE.test(path) && !NON_SOURCE_DIR.test(path));
14917
- nonTestSourcePaths.set(rootDir, paths);
15233
+ const packageRoots = new Set;
15234
+ const sources = [];
15235
+ walkRepo(root, (path, name) => {
15236
+ if (name === "package.json" || name === "pyproject.toml")
15237
+ packageRoots.add(posix.dirname(path));
15238
+ else if (REWRITE_SOURCE_FILE.test(name))
15239
+ sources.push(path);
15240
+ });
15241
+ const found = new Set;
15242
+ for (const path of sources) {
15243
+ let dir = posix.dirname(path);
15244
+ while (dir !== "." && !packageRoots.has(dir))
15245
+ dir = posix.dirname(dir);
15246
+ if (dir === "." || found.has(dir))
15247
+ continue;
15248
+ let text;
15249
+ try {
15250
+ if (statSync3(resolve9(root, path)).size > 512 * 1024)
15251
+ continue;
15252
+ text = readFileSync11(resolve9(root, path), "utf8");
15253
+ } catch {
15254
+ continue;
15255
+ }
15256
+ if (MODEL_REWRITE_CALL.test(text))
15257
+ found.add(dir);
14918
15258
  }
14919
- const used = paths.some((path) => {
15259
+ modelRewriteCache.set(root, found);
15260
+ return found;
15261
+ }
15262
+ var MAX_REPO_SCAN_DEPTH = 8;
15263
+ function walkRepo(root, onFile) {
15264
+ const visit = (dir, depth) => {
15265
+ let entries;
14920
15266
  try {
14921
- const absolute = resolve9(rootDir, path);
14922
- if (statSync3(absolute).size > MAX_MODEL_SOURCE_BYTES)
14923
- return false;
14924
- return readFileSync11(absolute, "utf8").includes(modelId);
15267
+ entries = readdirSync4(resolve9(root, dir), { withFileTypes: true });
14925
15268
  } catch {
14926
- return false;
15269
+ return;
15270
+ }
15271
+ for (const entry of entries) {
15272
+ const path = dir ? `${dir}/${entry.name}` : entry.name;
15273
+ if (entry.isDirectory()) {
15274
+ if (depth < MAX_REPO_SCAN_DEPTH && !entry.name.startsWith(".") && entry.name !== "node_modules")
15275
+ visit(path, depth + 1);
15276
+ } else if (entry.isFile()) {
15277
+ onFile(path, entry.name);
15278
+ }
15279
+ }
15280
+ };
15281
+ visit("", 0);
15282
+ }
15283
+ function isUnderAny(path, prefixes) {
15284
+ for (const prefix of prefixes)
15285
+ if (path === prefix || path.startsWith(`${prefix}/`))
15286
+ return true;
15287
+ return false;
15288
+ }
15289
+ var SCRIPTS_DIR = /^scripts\//;
15290
+ var DEPLOY_CONFIG_FILE = /^(?:Dockerfile(?:\.[\w.-]+)?|[\w.-]+\.Dockerfile|Procfile|fly\.toml|wrangler(?:\.[\w-]+)?\.(?:toml|jsonc?)|docker-compose(?:\.[\w-]+)?\.ya?ml|compose\.ya?ml|render\.ya?ml|railway\.json|vercel\.json|app\.ya?ml|crontab)$/i;
15291
+ var DEPLOY_PACKAGE_SCRIPT = /^(?:start|serve|prod|deploy|cron|worker)(?:[:_-]|$)/i;
15292
+ var SCRIPT_REFERENCE = /(?:^|[\s"'`=(,./])(scripts\/[\w./-]*[\w-])/g;
15293
+ var COMMENT_LINE = /^\s*(?:#|\/\/|\/\*|\*)/;
15294
+ var productionScriptCache = new Map;
15295
+ function productionScriptPaths(rootDir) {
15296
+ const root = resolve9(rootDir);
15297
+ const cached = productionScriptCache.get(root);
15298
+ if (cached)
15299
+ return cached;
15300
+ const found = new Set;
15301
+ const addReferences = (text) => {
15302
+ for (const line of text.split(`
15303
+ `)) {
15304
+ if (COMMENT_LINE.test(line))
15305
+ continue;
15306
+ for (const match of line.matchAll(SCRIPT_REFERENCE))
15307
+ found.add(match[1].replace(/\/+$/, ""));
14927
15308
  }
15309
+ };
15310
+ walkRepo(root, (path, name) => {
15311
+ const isDeployConfig = DEPLOY_CONFIG_FILE.test(name);
15312
+ if (!isDeployConfig && name !== "package.json")
15313
+ return;
15314
+ const dir = posix.dirname(path);
15315
+ if (isDeployConfig && SCRIPTS_DIR.test(path))
15316
+ found.add(dir);
15317
+ let text;
15318
+ try {
15319
+ text = readFileSync11(resolve9(root, path), "utf8");
15320
+ } catch {
15321
+ return;
15322
+ }
15323
+ if (isDeployConfig) {
15324
+ addReferences(text);
15325
+ return;
15326
+ }
15327
+ try {
15328
+ const scripts = JSON.parse(text).scripts ?? {};
15329
+ for (const [scriptName, command] of Object.entries(scripts)) {
15330
+ if (DEPLOY_PACKAGE_SCRIPT.test(scriptName) && typeof command === "string")
15331
+ addReferences(command);
15332
+ }
15333
+ } catch {}
14928
15334
  });
14929
- results.set(modelId, used);
14930
- return used;
15335
+ productionScriptCache.set(root, found);
15336
+ return found;
15337
+ }
15338
+ var MAX_MODEL_EVIDENCE = 5;
15339
+ var MAX_EVAL_SUITE_FILES = 100;
15340
+ function capPromotedEvidence(artifact) {
15341
+ const max = artifact.type === "model_config" ? MAX_MODEL_EVIDENCE : artifact.type === "eval" ? MAX_EVAL_SUITE_FILES : undefined;
15342
+ if (max !== undefined && artifact.evidence.length > max)
15343
+ artifact.evidence = artifact.evidence.slice(0, max);
15344
+ }
15345
+ function distinctStableId(prefix, raw) {
15346
+ return `${stableId(prefix, raw.replace(/\./g, "-"))}_${createHash5("sha256").update(raw).digest("hex").slice(0, 8)}`;
15347
+ }
15348
+ function promoteModelIds(occurrences, context) {
15349
+ const out = [];
15350
+ for (const [key, { id, at }] of occurrences) {
15351
+ const inHarness = at.filter((entry) => isHarnessModelPath(entry.path, context));
15352
+ if (inHarness.length === 0)
15353
+ continue;
15354
+ const seen = new Set;
15355
+ const cited = [...inHarness, ...at.filter((entry) => !isHarnessModelPath(entry.path, context))].filter((entry) => {
15356
+ const key2 = `${entry.path}:${entry.line}`;
15357
+ if (seen.has(key2))
15358
+ return false;
15359
+ seen.add(key2);
15360
+ return true;
15361
+ });
15362
+ const first2 = cited[0];
15363
+ const what = SIGNAL_DESCRIPTIONS[first2.signal] ?? `the ${first2.signal} pattern`;
15364
+ out.push({
15365
+ id: distinctStableId("model_config", key),
15366
+ type: "model_config",
15367
+ name: id,
15368
+ scope: "shared",
15369
+ usedByAgentIds: [],
15370
+ confidence: Math.max(...cited.map((entry) => entry.confidence)),
15371
+ confidenceReason: `Deterministic: model id ${id} found by ${what} at ${first2.path}:${first2.line} (${inHarness.length} harness occurrence${inHarness.length === 1 ? "" : "s"}); not checked by the analyst.`,
15372
+ evidence: cited.map((entry) => ({ path: entry.path, line: entry.line, reason: `deterministic detector ${entry.signal}` }))
15373
+ });
15374
+ }
15375
+ return out;
14931
15376
  }
14932
15377
  function mergePromotedDuplicate(existing, next) {
14933
15378
  for (const entry of next.evidence) {
@@ -14939,10 +15384,16 @@ function mergePromotedDuplicate(existing, next) {
14939
15384
  if (!existing.body && next.body)
14940
15385
  existing.body = next.body;
14941
15386
  }
14942
- function promoteHarnessCandidates(report, rootDir) {
15387
+ function promoteHarnessCandidates(report, rootDir, options = {}) {
14943
15388
  jsSources.clear();
15389
+ productionScriptCache.clear();
15390
+ modelRewriteCache.clear();
14944
15391
  try {
14945
- return promoteAll(report, rootDir);
15392
+ const artifacts = promoteAll(report, rootDir);
15393
+ if (!options.fullEvidence)
15394
+ for (const artifact of artifacts)
15395
+ capPromotedEvidence(artifact);
15396
+ return artifacts;
14946
15397
  } finally {
14947
15398
  jsSources.clear();
14948
15399
  }
@@ -14954,6 +15405,15 @@ function promoteAll(report, rootDir) {
14954
15405
  model: new Set(report.candidates.filter((candidate) => candidate.kind === "llm_call_site" || candidate.kind === "framework_construct").map((candidate) => candidate.path)),
14955
15406
  searchable: new Set(report.files.map((file) => file.path))
14956
15407
  };
15408
+ const modelIds = new Map;
15409
+ const evalFiles = [];
15410
+ const add = (artifact) => {
15411
+ const existing = byId.get(artifact.id);
15412
+ if (existing)
15413
+ mergePromotedDuplicate(existing, artifact);
15414
+ else
15415
+ byId.set(artifact.id, artifact);
15416
+ };
14957
15417
  for (const candidate of report.candidates) {
14958
15418
  const confidence2 = PROMOTED_CONFIDENCE[candidate.confidence];
14959
15419
  if (confidence2 === undefined)
@@ -14977,20 +15437,32 @@ function promoteAll(report, rootDir) {
14977
15437
  break;
14978
15438
  case "model_config":
14979
15439
  if (candidate.confidence === "high") {
14980
- artifact = promoteModelConfigCandidate(candidate, rootDir, confidence2, fileCache);
15440
+ for (const id of modelIdsOnCandidateLine(candidate, readFileLines(rootDir, candidate.path, fileCache))) {
15441
+ const key = id.toLowerCase();
15442
+ const entry = modelIds.get(key) ?? { id, at: [] };
15443
+ entry.at.push({ path: candidate.path, line: candidate.startLine, signal: candidate.signal, confidence: confidence2 });
15444
+ modelIds.set(key, entry);
15445
+ }
14981
15446
  }
14982
15447
  break;
15448
+ case "eval_file":
15449
+ if (candidate.confidence === "high")
15450
+ evalFiles.push(candidate);
15451
+ break;
14983
15452
  default:
14984
15453
  break;
14985
15454
  }
14986
15455
  if (!artifact)
14987
15456
  continue;
14988
- const existing = byId.get(artifact.id);
14989
- if (existing)
14990
- mergePromotedDuplicate(existing, artifact);
14991
- else
14992
- byId.set(artifact.id, artifact);
14993
- }
15457
+ add(artifact);
15458
+ }
15459
+ for (const artifact of promoteModelIds(modelIds, modelPathContext(rootDir)))
15460
+ add(artifact);
15461
+ for (const artifact of promoteEvalSuites(evalFiles, PROMOTED_CONFIDENCE.high, {
15462
+ artifactId: (suiteKey) => distinctStableId("eval", suiteKey),
15463
+ fileReason: detectorConfidenceReason
15464
+ }))
15465
+ add(artifact);
14994
15466
  return [...byId.values()];
14995
15467
  }
14996
15468
 
@@ -15325,7 +15797,7 @@ async function runHarnessCommand(context) {
15325
15797
  throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
15326
15798
  }
15327
15799
  if (context.flags["github-actions"] === "true") {
15328
- const { runGithubActionsAnalyze } = await import("./harness-github-actions-acxh0dbf.js");
15800
+ const { runGithubActionsAnalyze } = await import("./harness-github-actions-xbfvjpva.js");
15329
15801
  const result = await runGithubActionsAnalyze(rootDir, {
15330
15802
  writeReport: (report2) => {
15331
15803
  const normalized = normalizeHarnessReport(report2);
@@ -16012,7 +16484,7 @@ function isToolGroupPlaceholder(name) {
16012
16484
  function isDetectedToolDefinitionReason(reason) {
16013
16485
  return typeof reason === "string" && (reason.startsWith("deterministic detector ") || reason.startsWith("Tool definition detected"));
16014
16486
  }
16015
- var MODEL_SOURCE_FILE2 = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
16487
+ var MODEL_SOURCE_FILE = /\.(?:[cm]?[jt]sx?|py|json|jsonc|ya?ml|toml)$/i;
16016
16488
  var NON_SOURCE_DIR2 = /(^|\/)(?:docs?|\.moda)\//i;
16017
16489
  function isSearchableModelId(id) {
16018
16490
  return id.length >= 4 && !/\s/.test(id) && /[\d/.:_-]/.test(id);
@@ -16060,7 +16532,7 @@ function modelIdsUsedInSource(rootDir, ids) {
16060
16532
  return true;
16061
16533
  let entries;
16062
16534
  try {
16063
- entries = readdirSync4(current).sort();
16535
+ entries = readdirSync5(current).sort();
16064
16536
  } catch {
16065
16537
  return true;
16066
16538
  }
@@ -16072,7 +16544,7 @@ function modelIdsUsedInSource(rootDir, ids) {
16072
16544
  }
16073
16545
  if (!stat.isFile() || stat.size > MAX_SCANNED_FILE_BYTES || isSecretishPath(rel2))
16074
16546
  return true;
16075
- if (TEST_PATH2.test(rel2) || !MODEL_SOURCE_FILE2.test(rel2))
16547
+ if (TEST_PATH2.test(rel2) || !MODEL_SOURCE_FILE.test(rel2))
16076
16548
  return true;
16077
16549
  if (files >= maxFiles) {
16078
16550
  complete = false;
@@ -16259,7 +16731,7 @@ var CONCERN_PROMOTED_ARTIFACT_TYPES = {
16259
16731
  topology: new Set,
16260
16732
  tools: new Set(["tool"]),
16261
16733
  prompts: new Set(["prompt"]),
16262
- evals: new Set(["model_config"])
16734
+ evals: new Set(["model_config", "eval"])
16263
16735
  };
16264
16736
  function deterministicArtifacts(rootDir, concern) {
16265
16737
  const graph = { artifacts: [], unknowns: [] };
@@ -16275,21 +16747,43 @@ function mergeDeterministicArtifacts(graph, rootDir, options = {}) {
16275
16747
  const allowedTypes = options.concern ? CONCERN_PROMOTED_ARTIFACT_TYPES[options.concern] : undefined;
16276
16748
  let promoted;
16277
16749
  try {
16278
- promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir);
16750
+ promoted = promoteHarnessCandidates(detectHarnessCandidates(rootDir), rootDir, { fullEvidence: true });
16279
16751
  } catch {
16280
16752
  return { added: 0 };
16281
16753
  }
16282
16754
  backfillRefutationEvidenceReasons(graph);
16283
16755
  const refuted = analystRefutations(graph);
16284
16756
  const candidatePaths = new Set(promoted.flatMap((artifact) => artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean)));
16757
+ const modelLineCounts = new Map;
16758
+ for (const artifact of promoted) {
16759
+ if (artifact.type !== "model_config")
16760
+ continue;
16761
+ for (const entry of artifact.evidence) {
16762
+ const key = `${entry.path}:${entry.line}`;
16763
+ modelLineCounts.set(key, (modelLineCounts.get(key) ?? 0) + 1);
16764
+ }
16765
+ }
16766
+ const sharedModelLines = new Set([...modelLineCounts].filter(([, count]) => count > 1).map(([key]) => key));
16767
+ const pathContext = modelPathContext(rootDir);
16285
16768
  let added = 0;
16286
16769
  for (const artifact of promoted) {
16287
16770
  if (allowedTypes && !allowedTypes.has(artifact.type))
16288
16771
  continue;
16289
- if (graphCoversPromotedArtifact(graph, artifact))
16772
+ if (graphCoversPromotedArtifact(graph, artifact, sharedModelLines))
16290
16773
  continue;
16774
+ if ((artifact.type === "model_config" || artifact.type === "eval") && artifact.evidence.length > 1 && !refuted.candidateIds.has(artifact.id)) {
16775
+ const kept = artifact.evidence.filter((entry) => !isRefutedByAnalyst({ ...artifact, evidence: [entry] }, refuted, candidatePaths));
16776
+ if (kept.length === 0)
16777
+ continue;
16778
+ if (artifact.type === "model_config" && !kept.some((entry) => isHarnessModelPath(entry.path, pathContext)))
16779
+ continue;
16780
+ artifact.evidence = kept;
16781
+ if (artifact.sourcePath && !kept.some((entry) => entry.path === artifact.sourcePath))
16782
+ artifact.sourcePath = kept[0].path;
16783
+ }
16291
16784
  if (isRefutedByAnalyst(artifact, refuted, candidatePaths))
16292
16785
  continue;
16786
+ capPromotedEvidence(artifact);
16293
16787
  graph.artifacts.push(artifact);
16294
16788
  added += 1;
16295
16789
  }
@@ -16312,12 +16806,13 @@ var DETECTOR_ARTIFACT_TYPES = {
16312
16806
  message_construction: "prompt",
16313
16807
  tool_definition: "tool",
16314
16808
  tool_schema: "tool",
16315
- model_config: "model_config"
16809
+ model_config: "model_config",
16810
+ eval_file: "eval"
16316
16811
  };
16317
16812
  function refutedTypes(...texts) {
16318
16813
  const types = new Set;
16319
16814
  for (const text of texts) {
16320
- for (const match of String(text ?? "").matchAll(/\b(prompt_literal|prompt_file|message_construction|tool_definition|tool_schema|model_config)\b/g)) {
16815
+ for (const match of String(text ?? "").matchAll(/\b(prompt_literal|prompt_file|message_construction|tool_definition|tool_schema|model_config|eval_file)\b/g)) {
16321
16816
  types.add(DETECTOR_ARTIFACT_TYPES[match[1]]);
16322
16817
  }
16323
16818
  }
@@ -16379,7 +16874,26 @@ function artifactSourceKey(artifact) {
16379
16874
  const pinned = Array.isArray(artifact.evidence) ? artifact.evidence.find((entry) => typeof entry.path === "string" && entry.path.length > 0) : undefined;
16380
16875
  return pinned ? normalizePath2(pinned.path) : undefined;
16381
16876
  }
16382
- function graphCoversPromotedArtifact(graph, promoted) {
16877
+ function mentionsModelId(text, id) {
16878
+ if (!id)
16879
+ return false;
16880
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16881
+ return new RegExp(`(?<![\\w./@-])${escaped}(?![\\w/-]|\\.\\w)`, "i").test(text);
16882
+ }
16883
+ function artifactPaths(artifact) {
16884
+ const paths = Array.isArray(artifact.evidence) ? artifact.evidence.map((entry) => typeof entry.path === "string" ? normalizePath2(entry.path) : "").filter(Boolean) : [];
16885
+ return artifact.sourcePath ? [normalizePath2(artifact.sourcePath), ...paths] : paths;
16886
+ }
16887
+ function graphCoversPromotedArtifact(graph, promoted, sharedModelLines = new Set) {
16888
+ if (promoted.type === "model_config" && graph.artifacts.some((existing) => existing.type === "model_config" && mentionsModelId(`${existing.name ?? ""}
16889
+ ${existing.body ?? ""}`, promoted.name)))
16890
+ return true;
16891
+ if (promoted.type === "eval") {
16892
+ const suite = new Set(artifactPaths(promoted));
16893
+ const suiteKey = promoted.sourcePath ? evalSuiteKey(normalizePath2(promoted.sourcePath)) : undefined;
16894
+ if (graph.artifacts.some((existing) => existing.type === "eval" && artifactPaths(existing).some((path) => suite.has(path) || suiteKey !== undefined && evalSuiteKey(path.replace(/\/+$/, "")) === suiteKey)))
16895
+ return true;
16896
+ }
16383
16897
  const promotedName = promoted.name.toLowerCase();
16384
16898
  const isDefinitionLine = (entry) => typeof entry.line === "number" && !/^(?:passed to a model call|prompt-named literal referenced)/.test(entry.reason ?? "");
16385
16899
  const promotedLines = promoted.evidence.filter(isDefinitionLine);
@@ -16395,7 +16909,7 @@ function graphCoversPromotedArtifact(graph, promoted) {
16395
16909
  if (!existingSource || !promotedSource || existingSource === promotedSource && !distinctInline)
16396
16910
  return true;
16397
16911
  }
16398
- if (existing.type === promoted.type && Array.isArray(existing.evidence) && existing.evidence.some((entry) => isDefinitionLine(entry) && promotedLines.some((line) => line.path === entry.path && line.line === entry.line))) {
16912
+ if (existing.type === promoted.type && !(promoted.type === "model_config" && isModelIdLiteral(existing.name ?? "")) && Array.isArray(existing.evidence) && existing.evidence.some((entry) => isDefinitionLine(entry) && !(promoted.type === "model_config" && sharedModelLines.has(`${entry.path}:${entry.line}`)) && promotedLines.some((line) => line.path === entry.path && line.line === entry.line))) {
16399
16913
  return true;
16400
16914
  }
16401
16915
  if (isWholeFilePrompt && existing.sourcePath === promoted.sourcePath)
@@ -17401,7 +17915,7 @@ var CONCERN_CANDIDATE_KINDS = {
17401
17915
  topology: ["llm_call_site", "framework_construct"],
17402
17916
  tools: ["tool_definition", "tool_schema"],
17403
17917
  prompts: ["prompt_file", "prompt_literal", "message_construction"],
17404
- evals: ["model_config"]
17918
+ evals: ["model_config", "eval_file"]
17405
17919
  };
17406
17920
  var MAX_SEED_CANDIDATES_PER_KIND = 40;
17407
17921
  var MAX_INLINE_CANDIDATE_EXCERPT_CHARS = 500;
@@ -17414,7 +17928,8 @@ var SEED_CANDIDATE_KIND_LABELS = {
17414
17928
  message_construction: "Message constructions",
17415
17929
  prompt_file: "Prompt files",
17416
17930
  prompt_literal: "Inline prompt literals",
17417
- model_config: "Model configuration"
17931
+ model_config: "Model configuration",
17932
+ eval_file: "Eval suite files"
17418
17933
  };
17419
17934
  function renderAnalystCandidateLines(report, concern) {
17420
17935
  const allowedKinds = concern ? new Set(CONCERN_CANDIDATE_KINDS[concern]) : undefined;
@@ -17435,7 +17950,7 @@ function renderAnalystCandidateLines(report, concern) {
17435
17950
  concern ? 'Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file, drop false positives — things that are not harness components (SQL, UI props or display copy, URL templates, test fixtures, utilities), never a real component merely because it is unused (list refuted ones in unknowns: an unknown whose id starts with "refuted_", with one evidence entry {path, line, reason} per refuted candidate, using the path:line shown for that candidate and starting the reason with its [detector]), and hunt for in-concern items the patterns cannot see. Do not cite this list as evidence; cite the files.' : 'Each entry is path:line [detector] excerpt. Candidates are pattern matches — verify each against the real file and drop false positives — things that are not harness components (SQL, UI props or display copy, URL templates, test fixtures, utilities), never a real component merely because it is unused. Verified deterministic candidates are also auto-merged into the graph after your run, so prioritize (1) agents, topology, and relationships, (2) verifying or refuting these candidates (list refuted ones in unknowns: an unknown whose id starts with "refuted_", with one evidence entry {path, line, reason} per refuted candidate, using the path:line shown for that candidate and starting the reason with its [detector]; refuted candidates are never auto-merged), and (3) artifacts the patterns cannot see (runtime agents, prompts assembled at runtime, cross-file wiring) — rather than re-listing every candidate as an artifact. Do not cite this list as evidence; cite the files.'
17436
17951
  ];
17437
17952
  if (concern === "evals") {
17438
- lines.push("Note: evals, guardrails, and memory/retrieval stores have no deterministic detector — only model_config candidates are seeded. Discover the rest by investigation.");
17953
+ lines.push("Note: guardrails and memory/retrieval stores have no deterministic detector — only model_config and eval_file candidates are seeded. Discover the rest by investigation.");
17439
17954
  }
17440
17955
  for (const [kind, label] of Object.entries(SEED_CANDIDATE_KIND_LABELS)) {
17441
17956
  const bucket = byKind.get(kind);
@@ -17500,7 +18015,7 @@ function readDirectoryPreflightPaths(rootDir) {
17500
18015
  const absolute = current === "." ? rootDir : join9(rootDir, current);
17501
18016
  let entries;
17502
18017
  try {
17503
- entries = readdirSync4(absolute);
18018
+ entries = readdirSync5(absolute);
17504
18019
  } catch {
17505
18020
  continue;
17506
18021
  }
@@ -17536,7 +18051,7 @@ function shouldSkipPreflightPath(path) {
17536
18051
  return isSecretishPath(normalized);
17537
18052
  }
17538
18053
  function isManifestLikePath(path) {
17539
- const name = basename6(path).toLowerCase();
18054
+ const name = basename7(path).toLowerCase();
17540
18055
  return /^(package\.json|pyproject\.toml|requirements.*\.txt|uv\.lock|poetry\.lock|pnpm-lock\.yaml|bun\.lockb?|yarn\.lock|cargo\.toml|go\.mod|deno\.json|turbo\.json|langgraph\.json|agents\.json|config\.(ts|js|json|yaml|yml|toml))$/.test(name);
17541
18056
  }
17542
18057
  function isDocLikePath(path) {
@@ -17566,7 +18081,7 @@ function resolveAnalystCommand(adapter) {
17566
18081
  return findOnPath("cursor-agent") || findOnPath("cursor-cli") || findOnPath("cursor") || findOnPath("/Applications/Cursor.app/Contents/Resources/app/bin/cursor");
17567
18082
  }
17568
18083
  function cursorSubcommandArgs(command, args) {
17569
- const name = basename6(command);
18084
+ const name = basename7(command);
17570
18085
  return name === "cursor" || name === "cursor-cli" ? ["agent", ...args] : args;
17571
18086
  }
17572
18087
  function findOnPath(command) {
@@ -17595,7 +18110,7 @@ function scanHarness(options = {}) {
17595
18110
  const readPlan = buildHarnessReadPlan(rootDir, options.pathWasExplicit === true);
17596
18111
  const graph = {
17597
18112
  schemaVersion: SCHEMA_VERSION,
17598
- harnessId: stableId("harness", repo.name || basename6(rootDir) || "repo"),
18113
+ harnessId: stableId("harness", repo.name || basename7(rootDir) || "repo"),
17599
18114
  tenant: options.tenantId ? {
17600
18115
  tenantId: options.tenantId,
17601
18116
  source: options.tenantSource ?? "scan_option"
@@ -17666,7 +18181,7 @@ function collectSnapshotInputs(rootDir, maxFiles, maxTextBytes) {
17666
18181
  if (stat.isDirectory()) {
17667
18182
  if (path !== "." && (isSkippedDir(path) || isSkippedPathPrefix(path)))
17668
18183
  return;
17669
- for (const entry of readdirSync4(current).sort())
18184
+ for (const entry of readdirSync5(current).sort())
17670
18185
  walk(join9(current, entry));
17671
18186
  return;
17672
18187
  }
@@ -18009,7 +18524,7 @@ function findFresherHarnessReports(rootDir) {
18009
18524
  visited += 1;
18010
18525
  let entries;
18011
18526
  try {
18012
- entries = readdirSync4(dir, { withFileTypes: true });
18527
+ entries = readdirSync5(dir, { withFileTypes: true });
18013
18528
  } catch {
18014
18529
  return;
18015
18530
  }
@@ -18257,7 +18772,7 @@ function inventoryFiles(rootDir, skipped, options = {}) {
18257
18772
  recordSkipped(skipped, { path: rel2, reason: "ignored directory" });
18258
18773
  return;
18259
18774
  }
18260
- for (const entry of readdirSync4(current).sort()) {
18775
+ for (const entry of readdirSync5(current).sort()) {
18261
18776
  walk(join9(current, entry));
18262
18777
  }
18263
18778
  return;
@@ -18344,7 +18859,7 @@ function numericEnvAllowZero(name, fallback) {
18344
18859
  function detectRepo(rootDir, files) {
18345
18860
  const packageJson = files.find((file) => file.path === "package.json");
18346
18861
  const pyproject = files.find((file) => file.path === "pyproject.toml");
18347
- const name = packageJson?.text ? safePackageName(packageJson.text) ?? basename6(rootDir) : pyproject?.text ? safeTomlName(pyproject.text) ?? basename6(rootDir) : basename6(rootDir);
18862
+ const name = packageJson?.text ? safePackageName(packageJson.text) ?? basename7(rootDir) : pyproject?.text ? safeTomlName(pyproject.text) ?? basename7(rootDir) : basename7(rootDir);
18348
18863
  const packageManagers = [
18349
18864
  files.some((file) => file.path === "bun.lock" || file.path === "bun.lockb") ? "bun" : null,
18350
18865
  files.some((file) => file.path === "pnpm-lock.yaml") ? "pnpm" : null,
@@ -18618,7 +19133,7 @@ function maybeAddSkillArtifact(graph, file) {
18618
19133
  upsertArtifact(graph, {
18619
19134
  id: stableId("skill", file.path),
18620
19135
  type: "skill",
18621
- name: titleFromId(basename6(file.path).replace(/\.(md|mdc)$/i, "")),
19136
+ name: titleFromId(basename7(file.path).replace(/\.(md|mdc)$/i, "")),
18622
19137
  scope: "global",
18623
19138
  sourcePath: file.path,
18624
19139
  contentHash: hashText(file.text ?? ""),
@@ -18661,7 +19176,7 @@ function inferRuntimeAgent(file, graph) {
18661
19176
  return null;
18662
19177
  if (!/\.(ts|tsx|js|mjs|py)$/i.test(file.path))
18663
19178
  return null;
18664
- if (basename6(file.path) === "__init__.py")
19179
+ if (basename7(file.path) === "__init__.py")
18665
19180
  return null;
18666
19181
  const loweredPath = file.path.toLowerCase();
18667
19182
  const loweredText = file.text.toLowerCase();
@@ -18768,7 +19283,7 @@ function inferArtifactsForFile(file, graph) {
18768
19283
  return false;
18769
19284
  if (artifact.sourcePath === file.path)
18770
19285
  return true;
18771
- const base = basename6(artifact.sourcePath);
19286
+ const base = basename7(artifact.sourcePath);
18772
19287
  if (text.includes(base) || text.includes(artifact.sourcePath))
18773
19288
  return true;
18774
19289
  if (artifact.type === "tool") {
@@ -19690,13 +20205,13 @@ function titleFromId(id) {
19690
20205
  return id.replace(/^(agent|family|channel|prompt|tool|skill|eval|identity|deploy|entry|env)_/, "").split(/[_/.-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ") || id;
19691
20206
  }
19692
20207
  function promptNameFromPath(path) {
19693
- return titleFromId(basename6(path).replace(/\.(prompt\.)?(md|json|ya?ml)$/i, ""));
20208
+ return titleFromId(basename7(path).replace(/\.(prompt\.)?(md|json|ya?ml)$/i, ""));
19694
20209
  }
19695
20210
  function normalizePath2(path) {
19696
20211
  return path.split(sep5).join("/");
19697
20212
  }
19698
20213
  function hashText(text) {
19699
- return createHash5("sha256").update(text).digest("hex");
20214
+ return createHash6("sha256").update(text).digest("hex");
19700
20215
  }
19701
20216
 
19702
20217
  export { startRemoteAnalyze, pollRemoteAnalyzeUntilDone, isRecoverableRemoteRun, checkPromptMapping, safePromptEvidencePath, discoverHarnessPromptSources, runHarnessCommand, writeHarnessAnalyzeResult, emitWrittenArtifacts, analyzeHarnessPrompts, isToolGroupPlaceholder, repairExternalAnalystReport, deriveRelationshipsFromGraph, deterministicArtifacts, mergeDeterministicArtifacts, analystRefutations, describeCodingAgentEvent, analystEventProgress, isRetryableExternalAnalystFailure, sleep, summarizeInvalidAnalystReport, isDeterministicLocalAnalyst, resolveHarnessAnalyzeConcern, resolveExternalHarnessAnalystInvocation, detectAvailableCodingAgents, codingAgentDisplayName, runCodingAgent, readOnlyClaudeExtraArgs, resolveAnalystModel, renderExternalHarnessAnalystPrompt, CONCERN_CANDIDATE_KINDS, SEED_CANDIDATE_KIND_LABELS, renderAnalystCandidateLines, renderAnalystSeedLines, truncateText, scanHarness, summarizeHarness, buildSourceSnapshot, writeHumanProgress, describeSyncIdentity, describeSyncTenantLabel, findFresherHarnessReports, writeHarnessArtifacts, assertHarnessGraphSyncQuality, graphForSync, inventoryFiles, numericEnv, numericEnvAllowZero, computeDeterministicFloor, stableId, titleFromId, promptNameFromPath };