paqad-ai 1.58.0 → 1.60.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # paqad-ai
2
2
 
3
+ ## 1.60.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2b2ee2c: Planning now sees the codebase: on the feature-development route, the background context
8
+ worker composes a token-capped `## Existing surface` section into the session-context
9
+ artifact. It lists the exported symbols that already exist for the files/modules the prompt
10
+ and working set implicate — as signature cards (`name(signature) — file:line · called from N
11
+ places · module`) ranked by the repo-map's structural importance (PageRank) — so the model
12
+ reuses what exists instead of rewriting it. Signatures and caller counts come from the
13
+ code-knowledge index when present, falling back to name-only cards otherwise. The section is
14
+ budget-capped (config `existing_surface_tokens`, default 1000) with an honest truncation
15
+ line, appears only for feature-development (every other route stays token-neutral), and rides
16
+ the existing artifact so Codex/Gemini/advisory hosts get it through the same file. This gives
17
+ the built-but-unconsumed repo-map its first live consumer.
18
+
19
+ ## 1.59.0
20
+
21
+ ### Minor Changes
22
+
23
+ - deb1cb9: Make the end-of-change verdict visible, honest, and enforced (#368). The completion backstop now surfaces the one end-of-change receipt to the developer whether the verdict passes, fails, or is inconclusive — a failing "Needs your attention" verdict can no longer be hidden on stderr while a PR ships. A hard-failing feature-development change now blocks the turn at the Stop seam via the documented `decision:block` channel (exit 2 is a no-op there), bounded by the stop-hook loop guard. A feature-development change with no real `paqad-ai checks run` report reads as Inconclusive and shows its `checks` stage as unverified rather than done. The `checks.block_on_failure` escalation stays script-enforced; `review_findings` / `stale_docs` are documented as agent-raised and Decision-Pause-enforced rather than falsely claiming script enforcement. The cross-provider verdict tiering (hook-surfaced on Claude, agent-narrated on Codex/Gemini/advisory) is documented so the promise is truthful per host.
24
+
25
+ Also (AC-D2, from the second reproduction on the #355 session): the evidence receipt's `verification_result` is now three-way — `FAILED` only on a genuine failure, `INCONCLUSIVE` when a measure could not run (a `blocked` ratchet from unwired/absent tooling) or could not be judged, and `PASSED` when every row passed. A couldn't-verify is no longer reported as a failure, so an unwired quality-ratchet measure can no longer manufacture a false `FAILED` on the attestation.
26
+
3
27
  ## 1.58.0
4
28
 
5
29
  ### Minor Changes
package/dist/cli/index.js CHANGED
@@ -359,6 +359,7 @@ function defaultIntelligenceConfig() {
359
359
  rag_relief_floor: 0.35,
360
360
  rag_top_n: 20,
361
361
  rag_max_file_size: 153600,
362
+ existing_surface_tokens: DEFAULT_EXISTING_SURFACE_TOKENS,
362
363
  benchmark_gates: { ...DEFAULT_BENCHMARK_GATES },
363
364
  benchmark_eval: { ...DEFAULT_BENCHMARK_EVAL },
364
365
  adaptive_retrieval: {
@@ -384,6 +385,7 @@ function normalizeIntelligenceConfig(input3) {
384
385
  rag_relief_floor: input3.rag_relief_floor ?? defaults.rag_relief_floor,
385
386
  rag_top_n: input3.rag_top_n ?? defaults.rag_top_n,
386
387
  rag_max_file_size: input3.rag_max_file_size ?? defaults.rag_max_file_size,
388
+ existing_surface_tokens: input3.existing_surface_tokens ?? defaults.existing_surface_tokens,
387
389
  rag_base_branch: input3.rag_base_branch ?? defaults.rag_base_branch,
388
390
  benchmark_gates: {
389
391
  ...DEFAULT_BENCHMARK_GATES,
@@ -421,7 +423,7 @@ function normalizeIntelligenceConfig(input3) {
421
423
  }
422
424
  return normalized;
423
425
  }
424
- var EMBEDDING_PROVIDERS, LOCAL_EMBEDDING_MODELS, DEFAULT_LOCAL_EMBEDDING_MODEL, DEFAULT_BENCHMARK_EVAL, DEFAULT_ADAPTIVE_RETRIEVAL, DEFAULT_RERANKING, DEFAULT_METADATA_FILTERS, DEFAULT_ACTION_ROUTING, DEFAULT_BENCHMARK_GATES;
426
+ var EMBEDDING_PROVIDERS, LOCAL_EMBEDDING_MODELS, DEFAULT_LOCAL_EMBEDDING_MODEL, DEFAULT_BENCHMARK_EVAL, DEFAULT_ADAPTIVE_RETRIEVAL, DEFAULT_RERANKING, DEFAULT_METADATA_FILTERS, DEFAULT_ACTION_ROUTING, DEFAULT_EXISTING_SURFACE_TOKENS, DEFAULT_BENCHMARK_GATES;
425
427
  var init_project_intelligence = __esm({
426
428
  "src/core/project-intelligence.ts"() {
427
429
  "use strict";
@@ -461,6 +463,7 @@ var init_project_intelligence = __esm({
461
463
  DEFAULT_ACTION_ROUTING = {
462
464
  enabled: false
463
465
  };
466
+ DEFAULT_EXISTING_SURFACE_TOKENS = 1e3;
464
467
  DEFAULT_BENCHMARK_GATES = {
465
468
  hit_at_5_improvement_pct: 20,
466
469
  task_success_rate_improvement_pct: 10,
@@ -665,7 +668,8 @@ function resolveFrameworkConfigFromMap(raw) {
665
668
  rag_relief_floor: rn("rag_relief_floor"),
666
669
  rag_top_n: rn("rag_top_n"),
667
670
  rag_max_file_size: rn("rag_max_file_size"),
668
- rag_base_branch: raw.get("rag_base_branch")?.trim() || void 0
671
+ rag_base_branch: raw.get("rag_base_branch")?.trim() || void 0,
672
+ existing_surface_tokens: rn("existing_surface_tokens")
669
673
  });
670
674
  return {
671
675
  paqad: { enabled: rb("paqad_enable") },
@@ -1029,6 +1033,11 @@ function frameworkOverridesToFlat(overrides) {
1029
1033
  put("rag_top_n", i.rag_top_n, d.intelligence.rag_top_n);
1030
1034
  put("rag_max_file_size", i.rag_max_file_size, d.intelligence.rag_max_file_size);
1031
1035
  put("rag_base_branch", i.rag_base_branch, d.intelligence.rag_base_branch);
1036
+ put(
1037
+ "existing_surface_tokens",
1038
+ i.existing_surface_tokens,
1039
+ d.intelligence.existing_surface_tokens
1040
+ );
1032
1041
  }
1033
1042
  if (overrides.strictness) {
1034
1043
  const s = overrides.strictness;
@@ -1388,6 +1397,15 @@ var init_framework_config = __esm({
1388
1397
  section: "Intelligence / RAG",
1389
1398
  comment: "Base branch for branch-aware RAG. Unset auto-detects main->master."
1390
1399
  },
1400
+ {
1401
+ key: "existing_surface_tokens",
1402
+ env: "PAQAD_EXISTING_SURFACE_TOKENS",
1403
+ type: "number",
1404
+ default: 1e3,
1405
+ group: "rag",
1406
+ section: "Intelligence / RAG",
1407
+ comment: 'Token budget for the feature-development "Existing surface" planning digest (existing symbols the model should reuse). Cards drop by rank past this budget.'
1408
+ },
1391
1409
  // ── models group ───────────────────────────────────────────────────────
1392
1410
  {
1393
1411
  key: "research_depth",
@@ -1640,7 +1658,8 @@ var init_framework_config = __esm({
1640
1658
  "rag_relief_floor",
1641
1659
  "rag_top_n",
1642
1660
  "rag_max_file_size",
1643
- "rag_base_branch"
1661
+ "rag_base_branch",
1662
+ "existing_surface_tokens"
1644
1663
  ]
1645
1664
  },
1646
1665
  {
@@ -3638,6 +3657,7 @@ var init_project_profile_schema = __esm({
3638
3657
  rag_top_n: { type: "integer", minimum: 1 },
3639
3658
  rag_max_file_size: { type: "integer", minimum: 1 },
3640
3659
  rag_base_branch: { type: "string" },
3660
+ existing_surface_tokens: { type: "integer", minimum: 1 },
3641
3661
  benchmark_gates: {
3642
3662
  type: "object",
3643
3663
  additionalProperties: false,
@@ -8443,6 +8463,17 @@ function renderDefaultFeatureDevelopmentPolicyYaml() {
8443
8463
  return `# Feature Development Stage Policy
8444
8464
  # This file customizes how the built-in feature-development workflow behaves in this project.
8445
8465
  # The framework still owns routing, phase order, and mandatory safety stages.
8466
+ #
8467
+ # Enforcement tiers (issue #368) \u2014 an escalation is honoured on one of two tiers, and
8468
+ # this contract never claims script enforcement it does not have:
8469
+ # - SCRIPT-ENFORCED (deterministic): checks.block_on_failure and the mandatory-stage
8470
+ # completeness gate. \`paqad-ai checks run\` exits non-zero on any red command and the
8471
+ # completion backstop reads its report; a feature-dev change with no report reads
8472
+ # Inconclusive, never a vacuous pass.
8473
+ # - AGENT-RAISED -> DECISION-PAUSE-ENFORCED: review.escalation.review_findings and
8474
+ # documentation_sync.escalation.stale_docs need model judgment to raise, so no script
8475
+ # detects them; once raised as a \`stop\`, the Decision-Pause gate holds edits until the
8476
+ # packet resolves. Not a false "a script proves this" promise.
8446
8477
  schema_version: "1"
8447
8478
  merge_mode: append
8448
8479
 
@@ -25056,10 +25087,12 @@ ${blocks}
25056
25087
  async function writeRuleContext(projectRoot, options = {}) {
25057
25088
  const loadRules = options.loadRules ?? true;
25058
25089
  const store = loadRules ? await readCompiledRules(projectRoot) : null;
25090
+ const existingSurfaceSection = loadRules ? options.existingSurfaceSection?.trim() ?? "" : "";
25059
25091
  const memorySection = options.memorySection?.trim() ?? "";
25060
25092
  const retrievalSection = options.retrievalSection?.trim() ?? "";
25061
25093
  const driftSection = options.driftSection?.trim() ?? "";
25062
- if (!store && !memorySection && !retrievalSection && !driftSection) return null;
25094
+ if (!store && !existingSurfaceSection && !memorySection && !retrievalSection && !driftSection)
25095
+ return null;
25063
25096
  let markdown = "";
25064
25097
  if (store) {
25065
25098
  const changedPaths = (await loadChangeEvidence(projectRoot)).files;
@@ -25071,7 +25104,7 @@ ${DECISION_PAUSE_REMINDER}
25071
25104
  `;
25072
25105
  }
25073
25106
  }
25074
- for (const section of [memorySection, retrievalSection, driftSection]) {
25107
+ for (const section of [existingSurfaceSection, memorySection, retrievalSection, driftSection]) {
25075
25108
  if (section) {
25076
25109
  markdown = markdown ? `${markdown}
25077
25110
  ${section}
@@ -30327,7 +30360,7 @@ init_cancelled_error();
30327
30360
  init_events();
30328
30361
 
30329
30362
  // src/index.ts
30330
- var VERSION = "1.58.0";
30363
+ var VERSION = "1.60.0";
30331
30364
 
30332
30365
  // src/cli/commands/audit.ts
30333
30366
  init_esm_shims();
@@ -33240,7 +33273,8 @@ var FIELD_CHECKS = {
33240
33273
  rag_similarity_threshold: (value) => typeof value === "number" && value >= 0 && value <= 1 ? null : "Expected a number between 0 and 1.",
33241
33274
  rag_top_n: (value) => typeof value === "number" && Number.isInteger(value) && value > 0 ? null : "Expected a positive integer.",
33242
33275
  rag_max_file_size: (value) => typeof value === "number" && Number.isInteger(value) && value > 0 ? null : "Expected a positive integer.",
33243
- rag_base_branch: (value) => typeof value === "string" ? null : "Expected a string."
33276
+ rag_base_branch: (value) => typeof value === "string" ? null : "Expected a string.",
33277
+ existing_surface_tokens: (value) => typeof value === "number" && Number.isInteger(value) && value > 0 ? null : "Expected a positive integer."
33244
33278
  };
33245
33279
  function putRagConfig(projectRoot, candidate) {
33246
33280
  if (candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
@@ -42477,6 +42511,7 @@ var KNOB_CONSUMERS = {
42477
42511
  rag_top_n: "RAG retrieval depth",
42478
42512
  rag_max_file_size: "RAG index build",
42479
42513
  rag_base_branch: "branch-aware RAG index",
42514
+ existing_surface_tokens: "existing-surface planning digest",
42480
42515
  research_depth: NOTHING,
42481
42516
  model_default: "model selection (skills/model-selector)",
42482
42517
  model_reasoning: "model routing (budget optimizer)",
@@ -42648,8 +42683,8 @@ function createDecisionCommand() {
42648
42683
  // src/cli/commands/rag.ts
42649
42684
  init_esm_shims();
42650
42685
  init_project_intelligence();
42651
- import { readFileSync as readFileSync103 } from "fs";
42652
- import { isAbsolute as isAbsolute11, join as join207 } from "path";
42686
+ import { readFileSync as readFileSync104 } from "fs";
42687
+ import { isAbsolute as isAbsolute11, join as join208 } from "path";
42653
42688
  import { confirm, input as input2, select as select3 } from "@inquirer/prompts";
42654
42689
  import { Command as Command28 } from "commander";
42655
42690
 
@@ -42985,6 +43020,293 @@ ${lines}
42985
43020
  `;
42986
43021
  }
42987
43022
 
43023
+ // src/context/existing-surface.ts
43024
+ init_esm_shims();
43025
+ import { readFileSync as readFileSync101 } from "fs";
43026
+ import { join as join204 } from "path";
43027
+ init_project_intelligence();
43028
+
43029
+ // src/rag/repo-map.ts
43030
+ init_esm_shims();
43031
+ var PAGERANK_DAMPING = 0.85;
43032
+ function pageRank(seedNodes, edges, options = {}) {
43033
+ const damping = options.damping ?? PAGERANK_DAMPING;
43034
+ const iterations = options.iterations ?? 50;
43035
+ const tolerance = options.tolerance ?? 1e-6;
43036
+ const nodes = new Set(seedNodes);
43037
+ for (const edge of edges) {
43038
+ nodes.add(edge.from);
43039
+ nodes.add(edge.to);
43040
+ }
43041
+ const nodeList = [...nodes];
43042
+ const n = nodeList.length;
43043
+ if (n === 0) {
43044
+ return /* @__PURE__ */ new Map();
43045
+ }
43046
+ const outLinks = /* @__PURE__ */ new Map();
43047
+ for (const edge of edges) {
43048
+ if (edge.from === edge.to) {
43049
+ continue;
43050
+ }
43051
+ const list = outLinks.get(edge.from) ?? [];
43052
+ list.push(edge.to);
43053
+ outLinks.set(edge.from, list);
43054
+ }
43055
+ let rank = new Map(nodeList.map((node) => [node, 1 / n]));
43056
+ const base = (1 - damping) / n;
43057
+ for (let iteration = 0; iteration < iterations; iteration++) {
43058
+ const next = new Map(nodeList.map((node) => [node, base]));
43059
+ let danglingMass = 0;
43060
+ for (const node of nodeList) {
43061
+ const links = outLinks.get(node);
43062
+ if (!links || links.length === 0) {
43063
+ danglingMass += rank.get(node) ?? 0;
43064
+ }
43065
+ }
43066
+ const danglingShare = damping * danglingMass / n;
43067
+ for (const node of nodeList) {
43068
+ if (danglingShare > 0) {
43069
+ next.set(node, (next.get(node) ?? 0) + danglingShare);
43070
+ }
43071
+ const links = outLinks.get(node);
43072
+ if (!links || links.length === 0) {
43073
+ continue;
43074
+ }
43075
+ const contribution = damping * (rank.get(node) ?? 0) / links.length;
43076
+ for (const target of links) {
43077
+ next.set(target, (next.get(target) ?? 0) + contribution);
43078
+ }
43079
+ }
43080
+ let delta = 0;
43081
+ for (const node of nodeList) {
43082
+ delta += Math.abs((next.get(node) ?? 0) - (rank.get(node) ?? 0));
43083
+ }
43084
+ rank = next;
43085
+ if (delta < tolerance) {
43086
+ break;
43087
+ }
43088
+ }
43089
+ return rank;
43090
+ }
43091
+ var DEFAULT_REPO_MAP_TOKEN_BUDGET = 1500;
43092
+ var MAX_SYMBOLS_PER_FILE = 6;
43093
+ function estimateTokens(text) {
43094
+ return Math.ceil(text.length / 4);
43095
+ }
43096
+ function formatEntryLine2(entry) {
43097
+ const parts = [`- \`${entry.path}\``];
43098
+ if (entry.module) {
43099
+ parts.push(`\xB7 ${entry.module}`);
43100
+ }
43101
+ if (entry.symbols.length > 0) {
43102
+ parts.push(`\xB7 ${entry.symbols.slice(0, MAX_SYMBOLS_PER_FILE).join(", ")}`);
43103
+ }
43104
+ return parts.join(" ");
43105
+ }
43106
+ function buildRepoMap(files, edges, options = {}) {
43107
+ const tokenBudget = options.tokenBudget ?? DEFAULT_REPO_MAP_TOKEN_BUDGET;
43108
+ const ranks = pageRank(
43109
+ files.map((file) => file.path),
43110
+ edges
43111
+ );
43112
+ const entries = files.map((file) => ({
43113
+ path: file.path,
43114
+ module: file.module,
43115
+ symbols: file.symbols ?? [],
43116
+ rank: ranks.get(file.path) ?? 0
43117
+ })).sort((left, right) => {
43118
+ if (right.rank !== left.rank) {
43119
+ return right.rank - left.rank;
43120
+ }
43121
+ return left.path < right.path ? -1 : left.path > right.path ? 1 : 0;
43122
+ });
43123
+ if (entries.length === 0) {
43124
+ return { entries, skeleton: "", truncated: false };
43125
+ }
43126
+ const header = `## Repo map \u2014 ${entries.length} files ranked by structural importance
43127
+ > Embedding-free orientation. Use it to decide where to read or grep; verify against the live files.
43128
+ `;
43129
+ let tokens = estimateTokens(header);
43130
+ const lines = [];
43131
+ let truncated = false;
43132
+ for (const entry of entries) {
43133
+ const line = formatEntryLine2(entry);
43134
+ const lineTokens = estimateTokens(line) + 1;
43135
+ if (lines.length > 0 && tokens + lineTokens > tokenBudget) {
43136
+ truncated = true;
43137
+ break;
43138
+ }
43139
+ lines.push(line);
43140
+ tokens += lineTokens;
43141
+ }
43142
+ const body = truncated ? `${lines.join("\n")}
43143
+ \u2026[repo map truncated to fit the token budget]` : lines.join("\n");
43144
+ return { entries, skeleton: `${header}
43145
+ ${body}
43146
+ `, truncated };
43147
+ }
43148
+ async function buildProjectRepoMap(projectRoot, options) {
43149
+ const edges = await scanImports({
43150
+ projectRoot,
43151
+ files: options.files,
43152
+ aliases: options.aliases ?? { "@/": "src/" }
43153
+ });
43154
+ const files = options.files.map((path11) => ({
43155
+ path: path11,
43156
+ module: options.moduleOf?.(path11),
43157
+ symbols: options.symbolsOf?.(path11)
43158
+ }));
43159
+ return buildRepoMap(files, edges, { tokenBudget: options.tokenBudget });
43160
+ }
43161
+
43162
+ // src/context/existing-surface.ts
43163
+ init_contextual_blurb();
43164
+ var EXISTING_SURFACE_HEADING = "## Existing surface";
43165
+ var EXISTING_SURFACE_FRAMING = "> Before writing new helpers, check this surface \u2014 these already exist in this project.";
43166
+ function estimateTokens2(text) {
43167
+ return Math.ceil(text.length / 4);
43168
+ }
43169
+ function formatCardLine(card) {
43170
+ const head = card.signature?.trim() ? card.signature.trim() : card.name;
43171
+ const location = card.line ? `${card.file}:${card.line}` : card.file;
43172
+ const parts = [`- \`${head}\` \u2014 ${location}`];
43173
+ if (typeof card.callerCount === "number") {
43174
+ parts.push(`\xB7 called from ${card.callerCount} ${card.callerCount === 1 ? "place" : "places"}`);
43175
+ }
43176
+ if (card.module) {
43177
+ parts.push(`\xB7 ${card.module}`);
43178
+ }
43179
+ return parts.join(" ");
43180
+ }
43181
+ function headerFor(shown) {
43182
+ return `${EXISTING_SURFACE_HEADING} \u2014 ${shown} existing symbol${shown === 1 ? "" : "s"} for the files in play`;
43183
+ }
43184
+ function composeExistingSurfaceSection(cards, options = {}) {
43185
+ if (cards.length === 0) {
43186
+ return "";
43187
+ }
43188
+ const tokenBudget = options.tokenBudget ?? DEFAULT_EXISTING_SURFACE_TOKENS;
43189
+ let tokens = estimateTokens2(EXISTING_SURFACE_FRAMING) + estimateTokens2(headerFor(cards.length));
43190
+ const lines = [];
43191
+ let truncated = false;
43192
+ for (const card of cards) {
43193
+ const line = formatCardLine(card);
43194
+ const lineTokens = estimateTokens2(line) + 1;
43195
+ if (lines.length > 0 && tokens + lineTokens > tokenBudget) {
43196
+ truncated = true;
43197
+ break;
43198
+ }
43199
+ lines.push(line);
43200
+ tokens += lineTokens;
43201
+ }
43202
+ const remaining = cards.length - lines.length;
43203
+ const trailer = truncated && remaining > 0 ? `
43204
+ \u2026and ${remaining} more exported symbol${remaining === 1 ? "" : "s"} \u2014 run \`paqad-ai index query <name>\` to look one up.` : "";
43205
+ return `${headerFor(lines.length)}
43206
+ ${EXISTING_SURFACE_FRAMING}
43207
+
43208
+ ${lines.join("\n")}${trailer}
43209
+ `;
43210
+ }
43211
+ var SOURCE_EXT_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|php|dart)$/;
43212
+ function toPosix(path11) {
43213
+ return path11.replace(/\\/g, "/").replace(/^\.\//, "");
43214
+ }
43215
+ function modulePrefix(path11) {
43216
+ return path11.split("/").slice(0, 2).join("/");
43217
+ }
43218
+ function basenameNoExt(path11) {
43219
+ const base = path11.slice(path11.lastIndexOf("/") + 1);
43220
+ const dot = base.lastIndexOf(".");
43221
+ return dot <= 0 ? base : base.slice(0, dot);
43222
+ }
43223
+ function selectCandidateFiles(allFiles, workingSet, query, index) {
43224
+ const working = new Set(workingSet);
43225
+ const workingModules = new Set([...working].map(modulePrefix));
43226
+ if (working.size === 0 && query.trim().length === 0) {
43227
+ return [];
43228
+ }
43229
+ const queryLower = query.toLowerCase();
43230
+ const wanted = /* @__PURE__ */ new Set();
43231
+ for (const file of allFiles) {
43232
+ if (working.has(file) || workingModules.size > 0 && workingModules.has(modulePrefix(file))) {
43233
+ wanted.add(file);
43234
+ continue;
43235
+ }
43236
+ const base = basenameNoExt(file).toLowerCase();
43237
+ if (base.length >= 4 && queryLower.includes(base)) {
43238
+ wanted.add(file);
43239
+ }
43240
+ }
43241
+ if (index && queryLower.length > 0) {
43242
+ for (const symbol of index.symbols) {
43243
+ if (symbol.name.length >= 5 && queryLower.includes(symbol.name.toLowerCase())) {
43244
+ wanted.add(symbol.file);
43245
+ }
43246
+ }
43247
+ }
43248
+ const universe = new Set(allFiles);
43249
+ return [...wanted].filter((file) => universe.has(file));
43250
+ }
43251
+ async function gatherExistingSurface(projectRoot, options = {}) {
43252
+ try {
43253
+ const tokenBudget = options.tokenBudget ?? DEFAULT_EXISTING_SURFACE_TOKENS;
43254
+ const query = options.query ?? "";
43255
+ const index = readCodeKnowledgeIndex(projectRoot);
43256
+ const workingSet = (options.changedPaths ?? []).map(toPosix).filter((path11) => SOURCE_EXT_RE.test(path11));
43257
+ const allFiles = index ? index.files.map((file) => file.path) : scanWorkingTree(projectRoot, SOURCE_GLOBS).map(toPosix);
43258
+ const candidates = selectCandidateFiles(allFiles, workingSet, query, index);
43259
+ if (candidates.length === 0) {
43260
+ return "";
43261
+ }
43262
+ const symbolsByFile = /* @__PURE__ */ new Map();
43263
+ if (index) {
43264
+ for (const symbol of index.symbols) {
43265
+ const list = symbolsByFile.get(symbol.file) ?? [];
43266
+ list.push(symbol);
43267
+ symbolsByFile.set(symbol.file, list);
43268
+ }
43269
+ }
43270
+ const moduleResolver = buildModuleRoleResolver(projectRoot);
43271
+ const repoMap = await buildProjectRepoMap(projectRoot, {
43272
+ files: candidates,
43273
+ moduleOf: (path11) => moduleResolver(path11),
43274
+ symbolsOf: index ? (path11) => (symbolsByFile.get(path11) ?? []).map((symbol) => symbol.name) : (path11) => extractNames(projectRoot, path11)
43275
+ });
43276
+ const cards = [];
43277
+ for (const entry of repoMap.entries) {
43278
+ if (index) {
43279
+ const symbols = (symbolsByFile.get(entry.path) ?? []).slice().sort((a, b) => b.caller_count - a.caller_count || a.name.localeCompare(b.name));
43280
+ for (const symbol of symbols) {
43281
+ cards.push({
43282
+ name: symbol.name,
43283
+ signature: symbol.signature,
43284
+ file: symbol.file,
43285
+ line: symbol.line,
43286
+ callerCount: symbol.caller_count,
43287
+ module: symbol.module_slug ?? entry.module ?? void 0
43288
+ });
43289
+ }
43290
+ } else {
43291
+ for (const name of entry.symbols) {
43292
+ cards.push({ name, file: entry.path, module: entry.module });
43293
+ }
43294
+ }
43295
+ }
43296
+ return composeExistingSurfaceSection(cards, { tokenBudget });
43297
+ } catch {
43298
+ return "";
43299
+ }
43300
+ }
43301
+ function extractNames(projectRoot, relPath) {
43302
+ try {
43303
+ const content = readFileSync101(join204(projectRoot, relPath), "utf8");
43304
+ return extractSymbols(relPath, content).map((symbol) => symbol.name);
43305
+ } catch {
43306
+ return [];
43307
+ }
43308
+ }
43309
+
42988
43310
  // src/context/retrieval-context.ts
42989
43311
  init_esm_shims();
42990
43312
  init_project_intelligence();
@@ -43154,11 +43476,11 @@ async function gatherWorkingSetSlices(projectRoot, options = {}) {
43154
43476
  // src/pipeline/session-route.ts
43155
43477
  init_esm_shims();
43156
43478
  init_paths();
43157
- import { mkdirSync as mkdirSync51, readFileSync as readFileSync101, writeFileSync as writeFileSync43 } from "fs";
43158
- import { dirname as dirname86, join as join204 } from "path";
43479
+ import { mkdirSync as mkdirSync51, readFileSync as readFileSync102, writeFileSync as writeFileSync43 } from "fs";
43480
+ import { dirname as dirname86, join as join205 } from "path";
43159
43481
  var SESSION_ROUTE_FILE = ".session-route.json";
43160
43482
  function sessionRouteDir(projectRoot) {
43161
- return join204(projectRoot, dirname86(PATHS.CONTEXT_SESSION_ARTIFACT));
43483
+ return join205(projectRoot, dirname86(PATHS.CONTEXT_SESSION_ARTIFACT));
43162
43484
  }
43163
43485
  var VALID_WORKFLOWS2 = new Set(ROUTED_WORKFLOWS);
43164
43486
  function compositionForRoute(route) {
@@ -43173,7 +43495,7 @@ function compositionForRoute(route) {
43173
43495
  function readSessionRoute(projectRoot) {
43174
43496
  try {
43175
43497
  const parsed = JSON.parse(
43176
- readFileSync101(join204(sessionRouteDir(projectRoot), SESSION_ROUTE_FILE), "utf8")
43498
+ readFileSync102(join205(sessionRouteDir(projectRoot), SESSION_ROUTE_FILE), "utf8")
43177
43499
  );
43178
43500
  if (typeof parsed.workflow !== "string" || !VALID_WORKFLOWS2.has(parsed.workflow)) {
43179
43501
  return null;
@@ -43192,12 +43514,12 @@ init_recorder();
43192
43514
 
43193
43515
  // src/rag/background-sync.ts
43194
43516
  init_esm_shims();
43195
- import { join as join205 } from "path";
43517
+ import { join as join206 } from "path";
43196
43518
  init_paths();
43197
43519
  init_service();
43198
43520
  var STALE_LOCK_MS2 = 10 * 60 * 1e3;
43199
43521
  async function backgroundIndexSync(projectRoot, providerFactory) {
43200
- const lockDir = join205(projectRoot, PATHS.LOCKS_DIR, "rag-sync.lock");
43522
+ const lockDir = join206(projectRoot, PATHS.LOCKS_DIR, "rag-sync.lock");
43201
43523
  const lock = tryAcquireLock(lockDir, { staleLockMs: STALE_LOCK_MS2 });
43202
43524
  if (!lock.acquired) {
43203
43525
  return { synced: false, reason: "in-flight" };
@@ -43224,8 +43546,8 @@ async function backgroundIndexSync(projectRoot, providerFactory) {
43224
43546
  init_esm_shims();
43225
43547
  init_atomic_artifact();
43226
43548
  import { execFileSync as execFileSync10 } from "child_process";
43227
- import { readFileSync as readFileSync102 } from "fs";
43228
- import { join as join206 } from "path";
43549
+ import { readFileSync as readFileSync103 } from "fs";
43550
+ import { join as join207 } from "path";
43229
43551
 
43230
43552
  // src/background/debounce-marker.ts
43231
43553
  init_esm_shims();
@@ -43301,11 +43623,11 @@ function computeBaseDrift(projectRoot, options = {}) {
43301
43623
  async function refreshBaseDrift(projectRoot, options = {}) {
43302
43624
  const now = options.now ?? Date.now;
43303
43625
  const minIntervalMs = options.minIntervalMs ?? DEFAULT_BASE_DRIFT_INTERVAL_MS;
43304
- const markerPath = join206(projectRoot, PATHS.BASE_DRIFT_MARKER);
43626
+ const markerPath = join207(projectRoot, PATHS.BASE_DRIFT_MARKER);
43305
43627
  if (shouldDebounce(markerPath, minIntervalMs, now)) {
43306
43628
  return { refreshed: false, reason: "debounced" };
43307
43629
  }
43308
- const lockDir = join206(projectRoot, PATHS.BASE_DRIFT_LOCK);
43630
+ const lockDir = join207(projectRoot, PATHS.BASE_DRIFT_LOCK);
43309
43631
  const lock = tryAcquireLock(lockDir, { staleLockMs: STALE_LOCK_MS3 });
43310
43632
  if (!lock.acquired) {
43311
43633
  return { refreshed: false, reason: "in-flight" };
@@ -43329,7 +43651,7 @@ async function refreshBaseDrift(projectRoot, options = {}) {
43329
43651
  const snapshot = computeBaseDrift(projectRoot, options);
43330
43652
  if (snapshot) {
43331
43653
  await atomicWriteFile(
43332
- join206(projectRoot, PATHS.BASE_DRIFT_STATE),
43654
+ join207(projectRoot, PATHS.BASE_DRIFT_STATE),
43333
43655
  `${JSON.stringify(snapshot, null, 2)}
43334
43656
  `
43335
43657
  );
@@ -43344,7 +43666,7 @@ async function refreshBaseDrift(projectRoot, options = {}) {
43344
43666
  function loadBaseDrift(projectRoot) {
43345
43667
  try {
43346
43668
  const parsed = JSON.parse(
43347
- readFileSync102(join206(projectRoot, PATHS.BASE_DRIFT_STATE), "utf8")
43669
+ readFileSync103(join207(projectRoot, PATHS.BASE_DRIFT_STATE), "utf8")
43348
43670
  );
43349
43671
  if (typeof parsed === "object" && parsed !== null && typeof parsed.base_branch === "string" && typeof parsed.ahead === "number") {
43350
43672
  return parsed;
@@ -43842,9 +44164,18 @@ function createRagCommand() {
43842
44164
  }
43843
44165
  const route = readSessionRoute(options.projectRoot);
43844
44166
  const { loadRules, retrieves } = compositionForRoute(route);
43845
- const ragEnabled = resolveFrameworkConfig(options.projectRoot).intelligence.rag_enabled;
44167
+ const intelligence = resolveFrameworkConfig(options.projectRoot).intelligence;
44168
+ const existingSurfaceSection = loadRules ? await gatherExistingSurface(options.projectRoot, {
44169
+ changedPaths: (await loadChangeEvidence(options.projectRoot)).files,
44170
+ query: route?.query,
44171
+ tokenBudget: intelligence.existing_surface_tokens
44172
+ }) : "";
44173
+ const ragEnabled = intelligence.rag_enabled;
43846
44174
  if (!ragEnabled) {
43847
- const target2 = await refreshRuleContext(options.projectRoot, { loadRules });
44175
+ const target2 = await refreshRuleContext(options.projectRoot, {
44176
+ existingSurfaceSection,
44177
+ loadRules
44178
+ });
43848
44179
  if (!options.quiet) {
43849
44180
  process.stdout.write(
43850
44181
  `${target2 ? `wrote ${target2}` : "nothing to compose"}; rule-only (rag off)
@@ -43862,8 +44193,8 @@ function createRagCommand() {
43862
44193
  const packEntries = usesContextPack ? distillSlices(slices, {
43863
44194
  readFile: (path11) => {
43864
44195
  try {
43865
- return readFileSync103(
43866
- isAbsolute11(path11) ? path11 : join207(options.projectRoot, path11),
44196
+ return readFileSync104(
44197
+ isAbsolute11(path11) ? path11 : join208(options.projectRoot, path11),
43867
44198
  "utf8"
43868
44199
  );
43869
44200
  } catch {
@@ -43879,6 +44210,7 @@ function createRagCommand() {
43879
44210
  driftSection = composeBaseDriftSection(loadBaseDrift(options.projectRoot));
43880
44211
  }
43881
44212
  const target = await refreshRuleContext(options.projectRoot, {
44213
+ existingSurfaceSection,
43882
44214
  memorySection,
43883
44215
  retrievalSection,
43884
44216
  driftSection,
@@ -43984,7 +44316,7 @@ function createRagCommand() {
43984
44316
  }
43985
44317
  if (options.baseline) {
43986
44318
  const baselineSnapshot = JSON.parse(
43987
- readFileSync103(options.baseline, "utf8")
44319
+ readFileSync104(options.baseline, "utf8")
43988
44320
  );
43989
44321
  comparison = compareConfigurations(baselineSnapshot, candidateSnapshot, mode);
43990
44322
  }
@@ -44166,8 +44498,8 @@ function createRagEvidenceCommand() {
44166
44498
  // src/cli/commands/refresh.ts
44167
44499
  init_esm_shims();
44168
44500
  import { Command as Command30 } from "commander";
44169
- import { existsSync as existsSync108, readFileSync as readFileSync104, writeFileSync as writeFileSync45 } from "fs";
44170
- import { join as join208 } from "path";
44501
+ import { existsSync as existsSync108, readFileSync as readFileSync105, writeFileSync as writeFileSync45 } from "fs";
44502
+ import { join as join209 } from "path";
44171
44503
  init_paths();
44172
44504
  init_project_profile2();
44173
44505
  init_service();
@@ -44297,7 +44629,7 @@ async function refreshProviderEntries(projectRoot) {
44297
44629
  removeObsoleteContractDocs(projectRoot);
44298
44630
  for (const type of ADAPTER_TYPES) {
44299
44631
  const adapter = AdapterFactory.create(type);
44300
- const configPath = join208(projectRoot, adapter.getConfigPath());
44632
+ const configPath = join209(projectRoot, adapter.getConfigPath());
44301
44633
  if (!existsSync108(configPath)) {
44302
44634
  continue;
44303
44635
  }
@@ -44310,14 +44642,14 @@ async function refreshProviderEntries(projectRoot) {
44310
44642
  }
44311
44643
  }
44312
44644
  function writeRefreshDrift(projectRoot, refreshDrift) {
44313
- const path11 = join208(projectRoot, PATHS.STACK_DRIFT);
44645
+ const path11 = join209(projectRoot, PATHS.STACK_DRIFT);
44314
44646
  const current = readExistingJson(path11);
44315
44647
  writeFileSync45(path11, `${JSON.stringify({ ...current ?? {}, ...refreshDrift }, null, 2)}
44316
44648
  `);
44317
44649
  }
44318
44650
  function readExistingJson(path11) {
44319
44651
  try {
44320
- return JSON.parse(readFileSync104(path11, "utf8"));
44652
+ return JSON.parse(readFileSync105(path11, "utf8"));
44321
44653
  } catch {
44322
44654
  return null;
44323
44655
  }
@@ -44326,12 +44658,12 @@ function resolveChangedFiles(projectRoot, contextChangedFiles) {
44326
44658
  if (contextChangedFiles.length > 0) {
44327
44659
  return [...new Set(contextChangedFiles)].sort();
44328
44660
  }
44329
- const trackedPath = join208(projectRoot, PATHS.CHANGED_FILES);
44661
+ const trackedPath = join209(projectRoot, PATHS.CHANGED_FILES);
44330
44662
  if (!existsSync108(trackedPath)) {
44331
44663
  return [];
44332
44664
  }
44333
44665
  try {
44334
- const parsed = JSON.parse(readFileSync104(trackedPath, "utf8"));
44666
+ const parsed = JSON.parse(readFileSync105(trackedPath, "utf8"));
44335
44667
  if (!Array.isArray(parsed)) {
44336
44668
  return [];
44337
44669
  }
@@ -44399,7 +44731,7 @@ function createRulesCommand() {
44399
44731
 
44400
44732
  // src/cli/commands/spec.ts
44401
44733
  init_esm_shims();
44402
- import { readFileSync as readFileSync105 } from "fs";
44734
+ import { readFileSync as readFileSync106 } from "fs";
44403
44735
  import { basename as basename16 } from "path";
44404
44736
  import { Command as Command33 } from "commander";
44405
44737
 
@@ -44544,7 +44876,7 @@ function createSpecCommand() {
44544
44876
  (specFile, options) => {
44545
44877
  let markdown;
44546
44878
  try {
44547
- markdown = readFileSync105(specFile, "utf8");
44879
+ markdown = readFileSync106(specFile, "utf8");
44548
44880
  } catch {
44549
44881
  console.error(`could not read spec file "${specFile}"`);
44550
44882
  process.exitCode = 1;