@codeledger/selector 0.1.1
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/LICENSE +27 -0
- package/dist/bundle.d.ts +14 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +131 -0
- package/dist/bundle.js.map +1 -0
- package/dist/candidates.d.ts +20 -0
- package/dist/candidates.d.ts.map +1 -0
- package/dist/candidates.js +305 -0
- package/dist/candidates.js.map +1 -0
- package/dist/confidence.d.ts +11 -0
- package/dist/confidence.d.ts.map +1 -0
- package/dist/confidence.js +86 -0
- package/dist/confidence.js.map +1 -0
- package/dist/deprecation.d.ts +7 -0
- package/dist/deprecation.d.ts.map +1 -0
- package/dist/deprecation.js +57 -0
- package/dist/deprecation.js.map +1 -0
- package/dist/excerpt.d.ts +8 -0
- package/dist/excerpt.d.ts.map +1 -0
- package/dist/excerpt.js +175 -0
- package/dist/excerpt.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/scorer.d.ts +24 -0
- package/dist/scorer.d.ts.map +1 -0
- package/dist/scorer.js +214 -0
- package/dist/scorer.js.map +1 -0
- package/dist/stop-rule.d.ts +9 -0
- package/dist/stop-rule.d.ts.map +1 -0
- package/dist/stop-rule.js +33 -0
- package/dist/stop-rule.js.map +1 -0
- package/dist/stubs.d.ts +9 -0
- package/dist/stubs.d.ts.map +1 -0
- package/dist/stubs.js +161 -0
- package/dist/stubs.js.map +1 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Intelligent Context AI, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Note: This license applies to the CLI wrapper, types, repository scanning,
|
|
26
|
+
instrumentation, harness, and report packages (the "Plugin"). The scoring
|
|
27
|
+
engine (packages/core-engine/bin/) is licensed separately under LICENSE-CORE.
|
package/dist/bundle.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Budget, ContextBundle, RepoIndex, SelectorConfig } from '@codeledger/types';
|
|
2
|
+
import { type LedgerStats } from './scorer.js';
|
|
3
|
+
export interface BuildBundleOptions {
|
|
4
|
+
taskText: string;
|
|
5
|
+
repoIndex: RepoIndex;
|
|
6
|
+
selectorConfig: SelectorConfig;
|
|
7
|
+
budget?: Budget;
|
|
8
|
+
sufficiencyThreshold?: number;
|
|
9
|
+
ledgerStats?: LedgerStats;
|
|
10
|
+
/** When true, include per-file scoring breakdown in the bundle */
|
|
11
|
+
explain?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function buildBundle(opts: BuildBundleOptions): ContextBundle;
|
|
14
|
+
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EAEN,aAAa,EAEb,SAAS,EACT,cAAc,EACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAOnE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAoLnE"}
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { generateCandidates, tokenizeTask } from './candidates.js';
|
|
3
|
+
import { scoreAllCandidates } from './scorer.js';
|
|
4
|
+
import { shouldStop, estimateTokens } from './stop-rule.js';
|
|
5
|
+
import { extractExcerpt } from './excerpt.js';
|
|
6
|
+
import { assessConfidence } from './confidence.js';
|
|
7
|
+
import { generateInterfaceStubs } from './stubs.js';
|
|
8
|
+
import { scanDeprecations } from './deprecation.js';
|
|
9
|
+
export function buildBundle(opts) {
|
|
10
|
+
const { taskText, repoIndex, selectorConfig, budget = selectorConfig.default_budget, sufficiencyThreshold = selectorConfig.sufficiency_threshold, ledgerStats, explain = false, } = opts;
|
|
11
|
+
const keywords = tokenizeTask(taskText);
|
|
12
|
+
// Step 1: Generate candidates (includes IDF token weights + fan-out)
|
|
13
|
+
const { candidates: candidateSet, tokenWeights, fanoutFiles } = generateCandidates(taskText, repoIndex, selectorConfig);
|
|
14
|
+
// Step 2: Score all candidates (with IDF weights + fan-out bonus)
|
|
15
|
+
const scored = scoreAllCandidates(candidateSet, keywords, repoIndex, selectorConfig.weights, ledgerStats, tokenWeights, fanoutFiles);
|
|
16
|
+
// Step 3: Compute max cumulative score for threshold
|
|
17
|
+
const maxCumulative = scored.reduce((sum, f) => sum + Math.max(0, f.score), 0);
|
|
18
|
+
// Step 3.5: Reserve slots for test pairing under tight budgets.
|
|
19
|
+
// Under generous budgets, test pairing uses leftover space. Under tight
|
|
20
|
+
// budgets (max_files <= 15), reserve 2 slots so tests aren't crowded out.
|
|
21
|
+
const tightBudget = (budget.max_files ?? Infinity) <= 15;
|
|
22
|
+
const reservedTestSlots = tightBudget ? 2 : 0;
|
|
23
|
+
const mainBudget = {
|
|
24
|
+
...budget,
|
|
25
|
+
max_files: budget.max_files ? budget.max_files - reservedTestSlots : undefined,
|
|
26
|
+
};
|
|
27
|
+
// Step 4: Iterate with stop rule (using reduced budget to leave test slots)
|
|
28
|
+
const state = {
|
|
29
|
+
files: [],
|
|
30
|
+
totalTokens: 0,
|
|
31
|
+
cumulativeScore: 0,
|
|
32
|
+
};
|
|
33
|
+
// Collect explain data if requested
|
|
34
|
+
const explainData = {};
|
|
35
|
+
for (const scoredFile of scored) {
|
|
36
|
+
if (shouldStop(state, mainBudget, sufficiencyThreshold, maxCumulative)) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
// Extract excerpt
|
|
40
|
+
const excerpt = extractExcerpt(repoIndex.root, scoredFile.path, keywords, selectorConfig.excerpt_full_file_max_lines, selectorConfig.excerpt_window_lines);
|
|
41
|
+
const tokenEst = estimateTokens(excerpt.lineCount);
|
|
42
|
+
// Check if adding this file would blow the budget
|
|
43
|
+
if (budget.tokens && state.totalTokens + tokenEst > budget.tokens * 1.1) {
|
|
44
|
+
// Allow 10% overshoot, then stop
|
|
45
|
+
if (state.files.length > 0)
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
// Scan for deprecated API patterns
|
|
49
|
+
const deprecationWarnings = scanDeprecations(excerpt.content, selectorConfig.deprecation_rules);
|
|
50
|
+
const bundleFile = {
|
|
51
|
+
path: scoredFile.path,
|
|
52
|
+
score: Math.round(scoredFile.score * 1000) / 1000,
|
|
53
|
+
reasons: scoredFile.reasons,
|
|
54
|
+
excerpt_spans: excerpt.spans,
|
|
55
|
+
content: excerpt.content,
|
|
56
|
+
token_estimate: tokenEst,
|
|
57
|
+
deprecation_warnings: deprecationWarnings.length > 0 ? deprecationWarnings : undefined,
|
|
58
|
+
};
|
|
59
|
+
state.files.push(bundleFile);
|
|
60
|
+
state.totalTokens += tokenEst;
|
|
61
|
+
state.cumulativeScore += Math.max(0, scoredFile.score);
|
|
62
|
+
if (explain) {
|
|
63
|
+
explainData[scoredFile.path] = scoredFile.features;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Step 5: Deterministic test pairing — if a source file was selected and
|
|
67
|
+
// it has a mapped test, auto-include the test unless we're at the hard cap.
|
|
68
|
+
// Pairs are sorted by source file score so the most relevant tests fill
|
|
69
|
+
// reserved slots first. This prevents test starvation under tight budgets.
|
|
70
|
+
const selectedPaths = new Set(state.files.map((f) => f.path));
|
|
71
|
+
const sourceScoreMap = new Map(state.files.map((f) => [f.path, f.score]));
|
|
72
|
+
const testPairCandidates = [];
|
|
73
|
+
for (const mapping of repoIndex.test_map) {
|
|
74
|
+
if (selectedPaths.has(mapping.source_file) && !selectedPaths.has(mapping.test_file)) {
|
|
75
|
+
testPairCandidates.push({
|
|
76
|
+
testPath: mapping.test_file,
|
|
77
|
+
sourceScore: sourceScoreMap.get(mapping.source_file) ?? 0,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Sort by source score descending — pair tests for highest-scored files first
|
|
82
|
+
testPairCandidates.sort((a, b) => b.sourceScore - a.sourceScore);
|
|
83
|
+
for (const { testPath } of testPairCandidates) {
|
|
84
|
+
if (budget.max_files && state.files.length >= budget.max_files)
|
|
85
|
+
break;
|
|
86
|
+
const excerpt = extractExcerpt(repoIndex.root, testPath, keywords, selectorConfig.excerpt_full_file_max_lines, selectorConfig.excerpt_window_lines);
|
|
87
|
+
const tokenEst = estimateTokens(excerpt.lineCount);
|
|
88
|
+
if (budget.tokens && state.totalTokens + tokenEst > budget.tokens * 1.1)
|
|
89
|
+
continue;
|
|
90
|
+
// Find the scored entry if it exists, otherwise create a minimal one
|
|
91
|
+
const scoredEntry = scored.find((s) => s.path === testPath);
|
|
92
|
+
state.files.push({
|
|
93
|
+
path: testPath,
|
|
94
|
+
score: scoredEntry ? Math.round(scoredEntry.score * 1000) / 1000 : 0,
|
|
95
|
+
reasons: scoredEntry?.reasons ?? ['test_relevant'],
|
|
96
|
+
excerpt_spans: excerpt.spans,
|
|
97
|
+
content: excerpt.content,
|
|
98
|
+
token_estimate: tokenEst,
|
|
99
|
+
});
|
|
100
|
+
state.totalTokens += tokenEst;
|
|
101
|
+
}
|
|
102
|
+
// Step 6: Generate interface stubs for unselected dependencies.
|
|
103
|
+
// These give the agent the type signatures of files adjacent to the bundle
|
|
104
|
+
// without the full token cost.
|
|
105
|
+
const finalSelectedPaths = new Set(state.files.map((f) => f.path));
|
|
106
|
+
const tokenBudgetRemaining = (budget.tokens ?? Infinity) - state.totalTokens;
|
|
107
|
+
const maxStubs = Math.max(0, (budget.max_files ?? 25) - state.files.length);
|
|
108
|
+
if (maxStubs > 0 && tokenBudgetRemaining > 0) {
|
|
109
|
+
const stubs = generateInterfaceStubs(repoIndex.root, finalSelectedPaths, repoIndex.dep_graph, Math.min(maxStubs, 5), // cap at 5 stubs
|
|
110
|
+
Math.min(tokenBudgetRemaining, 1000));
|
|
111
|
+
for (const stub of stubs) {
|
|
112
|
+
state.files.push(stub);
|
|
113
|
+
state.totalTokens += stub.token_estimate;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Step 7: Assess bundle confidence
|
|
117
|
+
const confidence = assessConfidence(state.files, state.cumulativeScore, keywords.length);
|
|
118
|
+
return {
|
|
119
|
+
bundle_id: `bnd_${randomUUID().slice(0, 12)}`,
|
|
120
|
+
task: taskText,
|
|
121
|
+
budget,
|
|
122
|
+
sufficiency_threshold: sufficiencyThreshold,
|
|
123
|
+
cumulative_score: Math.round(state.cumulativeScore * 1000) / 1000,
|
|
124
|
+
files: state.files,
|
|
125
|
+
total_tokens: state.totalTokens,
|
|
126
|
+
generated_at: new Date().toISOString(),
|
|
127
|
+
confidence,
|
|
128
|
+
explain: explain ? explainData : undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RepoIndex, SelectorConfig } from '@codeledger/types';
|
|
2
|
+
export interface CandidateResult {
|
|
3
|
+
candidates: Set<string>;
|
|
4
|
+
tokenWeights: Map<string, number>;
|
|
5
|
+
fanoutFiles: Set<string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function tokenizeTask(taskText: string): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Compute IDF-like token weights based on how many files each keyword matches.
|
|
10
|
+
*
|
|
11
|
+
* idf = 1 / sqrt(max(1, matchCount)), clamped to [0.35, 1.0].
|
|
12
|
+
*
|
|
13
|
+
* Additional penalties:
|
|
14
|
+
* - Short tokens (length <= 2): floor to 0.35 — "ts", "db", "id" are noise.
|
|
15
|
+
* - Generic tokens (matchCount > hotZoneCount): floor to 0.35 — "api",
|
|
16
|
+
* "service" appear everywhere and shouldn't dominate scoring.
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeTokenWeights(keywords: string[], repoIndex: RepoIndex, hotZoneCount: number): Map<string, number>;
|
|
19
|
+
export declare function generateCandidates(taskText: string, repoIndex: RepoIndex, config: SelectorConfig): CandidateResult;
|
|
20
|
+
//# sourceMappingURL=candidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidates.d.ts","sourceRoot":"","sources":["../src/candidates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,SAAS,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAoE7E,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAcvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAwCrB;AAyCD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,cAAc,GACrB,eAAe,CAoHjB"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
const STOP_WORDS = new Set([
|
|
2
|
+
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
3
|
+
'of', 'with', 'by', 'from', 'is', 'it', 'that', 'this', 'be', 'as',
|
|
4
|
+
'are', 'was', 'were', 'been', 'being', 'have', 'has', 'had', 'do',
|
|
5
|
+
'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might',
|
|
6
|
+
'shall', 'can', 'need', 'must', 'not', 'no', 'if', 'then', 'else',
|
|
7
|
+
'when', 'up', 'out', 'so', 'than', 'too', 'very', 'just', 'about',
|
|
8
|
+
'into', 'over', 'after', 'before', 'between', 'under', 'above',
|
|
9
|
+
'all', 'each', 'every', 'both', 'few', 'more', 'most', 'some', 'any',
|
|
10
|
+
'add', 'fix', 'update', 'change', 'make', 'use', 'get', 'set',
|
|
11
|
+
'find', 'check', 'look', 'see', 'know', 'think', 'want', 'try',
|
|
12
|
+
'run', 'show', 'help', 'work', 'take', 'give', 'tell', 'call',
|
|
13
|
+
'go', 'come', 'keep', 'let', 'begin', 'start', 'end', 'stop',
|
|
14
|
+
'ready', 'live', 'done', 'good', 'new', 'old', 'big', 'small',
|
|
15
|
+
]);
|
|
16
|
+
/**
|
|
17
|
+
* Words that are contextual: they act as stop words in broad/assessment tasks
|
|
18
|
+
* but are useful keywords in targeted coding tasks.
|
|
19
|
+
*
|
|
20
|
+
* "test the app for readiness" -> "test" is noise (broad assessment)
|
|
21
|
+
* "fix the test for authService" -> "test" is signal (specific target)
|
|
22
|
+
*
|
|
23
|
+
* Heuristic: if the task contains broad-assessment phrases, demote these
|
|
24
|
+
* words by treating them as stop words.
|
|
25
|
+
*/
|
|
26
|
+
const CONTEXTUAL_WORDS = new Set([
|
|
27
|
+
'test', 'tests', 'testing', 'build', 'lint', 'debug', 'deploy',
|
|
28
|
+
'review', 'audit', 'evaluate', 'assess', 'analyze', 'examine',
|
|
29
|
+
'application', 'app', 'project', 'codebase', 'code', 'repo',
|
|
30
|
+
]);
|
|
31
|
+
/** Phrases indicating a broad assessment task rather than a focused coding task.
|
|
32
|
+
* Matched with word boundaries to avoid false positives
|
|
33
|
+
* (e.g. "full" in "full-text search"). */
|
|
34
|
+
const BROAD_TASK_PHRASES = [
|
|
35
|
+
'ready for', 'go live', 'production ready', 'readiness',
|
|
36
|
+
'find out', 'evaluate', 'assess', 'audit', 'review the',
|
|
37
|
+
'status of', 'state of', 'quality of', 'health of',
|
|
38
|
+
'overall', 'end to end', 'full', 'comprehensive',
|
|
39
|
+
].map((phrase) => new RegExp(`\\b${phrase}\\b`));
|
|
40
|
+
/**
|
|
41
|
+
* Phrases that signal cross-cutting fan-out intent: the task wants to touch
|
|
42
|
+
* every file that depends on a target module.
|
|
43
|
+
* Matched with word boundaries to prevent false positives
|
|
44
|
+
* (e.g. "replace" in "irreplaceable").
|
|
45
|
+
*/
|
|
46
|
+
const FANOUT_TRIGGERS = [
|
|
47
|
+
'replace', 'everywhere', 'all files', 'every file',
|
|
48
|
+
'migrate', 'files that import', 'files that use',
|
|
49
|
+
'across all', 'update all', 'rename all',
|
|
50
|
+
].map((phrase) => new RegExp(`\\b${phrase}\\b`));
|
|
51
|
+
const FANOUT_CEILING = 50;
|
|
52
|
+
/** Default contract/schema patterns when not configured */
|
|
53
|
+
const DEFAULT_CONTRACT_PATTERNS = [
|
|
54
|
+
'.sql', '.proto', '.prisma', '.graphql', '.gql',
|
|
55
|
+
];
|
|
56
|
+
/** Path segments that indicate contract/schema files */
|
|
57
|
+
const CONTRACT_PATH_INDICATORS = [
|
|
58
|
+
'openapi', 'swagger', 'schema', 'migrations',
|
|
59
|
+
];
|
|
60
|
+
export function tokenizeTask(taskText) {
|
|
61
|
+
const lower = taskText.toLowerCase();
|
|
62
|
+
const isBroadTask = BROAD_TASK_PHRASES.some((re) => re.test(lower));
|
|
63
|
+
// Build effective stop word set: include contextual words for broad tasks
|
|
64
|
+
const effectiveStopWords = isBroadTask
|
|
65
|
+
? new Set([...STOP_WORDS, ...CONTEXTUAL_WORDS])
|
|
66
|
+
: STOP_WORDS;
|
|
67
|
+
const words = lower
|
|
68
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
69
|
+
.filter((w) => w.length > 1 && !effectiveStopWords.has(w));
|
|
70
|
+
return [...new Set(words)];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Compute IDF-like token weights based on how many files each keyword matches.
|
|
74
|
+
*
|
|
75
|
+
* idf = 1 / sqrt(max(1, matchCount)), clamped to [0.35, 1.0].
|
|
76
|
+
*
|
|
77
|
+
* Additional penalties:
|
|
78
|
+
* - Short tokens (length <= 2): floor to 0.35 — "ts", "db", "id" are noise.
|
|
79
|
+
* - Generic tokens (matchCount > hotZoneCount): floor to 0.35 — "api",
|
|
80
|
+
* "service" appear everywhere and shouldn't dominate scoring.
|
|
81
|
+
*/
|
|
82
|
+
export function computeTokenWeights(keywords, repoIndex, hotZoneCount) {
|
|
83
|
+
const weights = new Map();
|
|
84
|
+
for (const kw of keywords) {
|
|
85
|
+
let matchCount = 0;
|
|
86
|
+
for (const file of repoIndex.files) {
|
|
87
|
+
// Count matches from both path and content keywords
|
|
88
|
+
if (file.path.toLowerCase().includes(kw)) {
|
|
89
|
+
matchCount++;
|
|
90
|
+
}
|
|
91
|
+
else if (file.content_keywords?.some((ck) => ck === kw || ck === kw + 's' || kw === ck + 's')) {
|
|
92
|
+
matchCount++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Keywords matching zero files are noise — weight 0 so they don't
|
|
96
|
+
// inflate the keyword normalization denominator.
|
|
97
|
+
if (matchCount === 0) {
|
|
98
|
+
weights.set(kw, 0);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
let idf = 1 / Math.sqrt(Math.max(1, matchCount));
|
|
102
|
+
// Short tokens (≤2 chars): weight floor
|
|
103
|
+
if (kw.length <= 2) {
|
|
104
|
+
idf = Math.min(idf, 0.35);
|
|
105
|
+
}
|
|
106
|
+
// Generic token penalty: matchCount > hotZoneCount → floor
|
|
107
|
+
if (matchCount > hotZoneCount) {
|
|
108
|
+
idf = Math.min(idf, 0.35);
|
|
109
|
+
}
|
|
110
|
+
// Clamp to [0.35, 1.0]
|
|
111
|
+
idf = Math.max(0.35, Math.min(1.0, idf));
|
|
112
|
+
weights.set(kw, idf);
|
|
113
|
+
}
|
|
114
|
+
return weights;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Detect whether the task text signals cross-cutting fan-out intent.
|
|
118
|
+
*/
|
|
119
|
+
function detectFanoutIntent(taskText) {
|
|
120
|
+
const lower = taskText.toLowerCase();
|
|
121
|
+
return FANOUT_TRIGGERS.some((re) => re.test(lower));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Find files whose basename stem matches a keyword exactly (with plural
|
|
125
|
+
* tolerance). These are "strong targets" for fan-out expansion.
|
|
126
|
+
*
|
|
127
|
+
* Stem splitting: basename without extension, split on `-` and `_`.
|
|
128
|
+
* e.g. "rate-limiter.ts" → ["rate", "limiter"]
|
|
129
|
+
* "helpers.ts" → ["helpers"]
|
|
130
|
+
*/
|
|
131
|
+
function findStrongTargets(keywords, repoIndex) {
|
|
132
|
+
const targets = [];
|
|
133
|
+
for (const file of repoIndex.files) {
|
|
134
|
+
const pathLower = file.path.toLowerCase();
|
|
135
|
+
const basename = pathLower.split('/').pop()?.replace(/\.\w+$/, '') ?? '';
|
|
136
|
+
const stems = basename.split(/[-_]/);
|
|
137
|
+
for (const kw of keywords) {
|
|
138
|
+
const isStemMatch = stems.some((stem) => stem === kw || stem === kw + 's' || kw === stem + 's');
|
|
139
|
+
if (isStemMatch) {
|
|
140
|
+
targets.push(file.path);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return targets;
|
|
146
|
+
}
|
|
147
|
+
export function generateCandidates(taskText, repoIndex, config) {
|
|
148
|
+
const candidates = new Set();
|
|
149
|
+
const fanoutFiles = new Set();
|
|
150
|
+
const keywords = tokenizeTask(taskText);
|
|
151
|
+
// Compute IDF token weights
|
|
152
|
+
const tokenWeights = computeTokenWeights(keywords, repoIndex, config.hot_zone_count);
|
|
153
|
+
// 1. Keyword match: file paths + names + content keywords
|
|
154
|
+
for (const file of repoIndex.files) {
|
|
155
|
+
const pathLower = file.path.toLowerCase();
|
|
156
|
+
for (const kw of keywords) {
|
|
157
|
+
if (pathLower.includes(kw)) {
|
|
158
|
+
candidates.add(file.path);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
// Content-aware matching: check identifiers extracted during scanning
|
|
162
|
+
if (file.content_keywords?.some((ck) => ck === kw || ck === kw + 's' || kw === ck + 's')) {
|
|
163
|
+
candidates.add(file.path);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// 2. Hot zone: top N by churn — only when budget is generous.
|
|
169
|
+
// Under tight budgets (max_files <= 15), hot-zone is a precision killer
|
|
170
|
+
// because churn-heavy files crowd out task-specific ones. In that case
|
|
171
|
+
// hot-zone only contributes files that already have a keyword hit.
|
|
172
|
+
const tightMode = (config.default_budget.max_files ?? Infinity) <= 15;
|
|
173
|
+
const hotZone = repoIndex.churn.slice(0, config.hot_zone_count);
|
|
174
|
+
for (const churnEntry of hotZone) {
|
|
175
|
+
if (!repoIndex.files.some((f) => f.path === churnEntry.path))
|
|
176
|
+
continue;
|
|
177
|
+
if (tightMode && !candidates.has(churnEntry.path))
|
|
178
|
+
continue;
|
|
179
|
+
candidates.add(churnEntry.path);
|
|
180
|
+
}
|
|
181
|
+
// 3. Dependency neighborhood: expand from keyword-matched files
|
|
182
|
+
const keywordMatched = [...candidates];
|
|
183
|
+
for (const seed of keywordMatched) {
|
|
184
|
+
expandDependencies(seed, repoIndex, candidates, config.dependency_depth, config.dependency_cap_per_seed);
|
|
185
|
+
}
|
|
186
|
+
// 4. Fan-out expansion: when task describes a cross-cutting change AND a
|
|
187
|
+
// strong target file is identified, expand ALL direct dependents (depth=1)
|
|
188
|
+
// without per-seed cap. Optionally expand depth=2 if under ceiling.
|
|
189
|
+
if (detectFanoutIntent(taskText)) {
|
|
190
|
+
const strongTargets = findStrongTargets(keywords, repoIndex);
|
|
191
|
+
for (const target of strongTargets) {
|
|
192
|
+
if (candidates.size >= FANOUT_CEILING)
|
|
193
|
+
break;
|
|
194
|
+
// Depth 1: all direct dependents — always mark as fanout even if
|
|
195
|
+
// already discovered by normal dependency expansion, so they receive
|
|
196
|
+
// the fan-out scoring bonus.
|
|
197
|
+
const dependents = repoIndex.dep_graph.dependents[target] ?? [];
|
|
198
|
+
for (const dep of dependents) {
|
|
199
|
+
if (candidates.size >= FANOUT_CEILING)
|
|
200
|
+
break;
|
|
201
|
+
candidates.add(dep);
|
|
202
|
+
fanoutFiles.add(dep);
|
|
203
|
+
}
|
|
204
|
+
// Depth 2: only if under ceiling
|
|
205
|
+
if (candidates.size < FANOUT_CEILING) {
|
|
206
|
+
for (const dep of dependents) {
|
|
207
|
+
if (candidates.size >= FANOUT_CEILING)
|
|
208
|
+
break;
|
|
209
|
+
const depth2Deps = repoIndex.dep_graph.dependents[dep] ?? [];
|
|
210
|
+
for (const dep2 of depth2Deps) {
|
|
211
|
+
if (candidates.size >= FANOUT_CEILING)
|
|
212
|
+
break;
|
|
213
|
+
candidates.add(dep2);
|
|
214
|
+
fanoutFiles.add(dep2);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// 5. Test neighborhood: add test files mapped to candidates
|
|
221
|
+
const currentCandidates = [...candidates];
|
|
222
|
+
for (const mapping of repoIndex.test_map) {
|
|
223
|
+
if (currentCandidates.includes(mapping.source_file)) {
|
|
224
|
+
candidates.add(mapping.test_file);
|
|
225
|
+
}
|
|
226
|
+
if (currentCandidates.includes(mapping.test_file)) {
|
|
227
|
+
candidates.add(mapping.source_file);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// 6. Contract/schema auto-inclusion: when the bundle touches DB models,
|
|
231
|
+
// API routes, or services, auto-include related contract files (.sql,
|
|
232
|
+
// .proto, .prisma, .graphql, openapi specs) so the agent sees the
|
|
233
|
+
// actual data model instead of guessing.
|
|
234
|
+
const contractFiles = findContractFiles(repoIndex.files, config.contract_patterns);
|
|
235
|
+
if (contractFiles.length > 0 && candidates.size > 0) {
|
|
236
|
+
// Only include contracts that share a directory prefix with existing candidates
|
|
237
|
+
const candidateDirs = new Set([...candidates].map((p) => p.split('/').slice(0, -1).join('/')));
|
|
238
|
+
for (const cf of contractFiles) {
|
|
239
|
+
const cfDir = cf.split('/').slice(0, -1).join('/');
|
|
240
|
+
// Include if the contract shares a directory with any candidate,
|
|
241
|
+
// or if any candidate is in a subdirectory of the contract's dir
|
|
242
|
+
const isRelated = candidateDirs.has(cfDir) ||
|
|
243
|
+
[...candidateDirs].some((d) => d.startsWith(cfDir + '/') || cfDir.startsWith(d + '/'));
|
|
244
|
+
if (isRelated) {
|
|
245
|
+
candidates.add(cf);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { candidates, tokenWeights, fanoutFiles };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Identify contract/schema files in the repo based on extension and path patterns.
|
|
253
|
+
*/
|
|
254
|
+
function findContractFiles(files, customPatterns) {
|
|
255
|
+
const results = [];
|
|
256
|
+
for (const file of files) {
|
|
257
|
+
const ext = file.extension.toLowerCase();
|
|
258
|
+
const pathLower = file.path.toLowerCase();
|
|
259
|
+
// Match by extension
|
|
260
|
+
if (DEFAULT_CONTRACT_PATTERNS.includes(ext)) {
|
|
261
|
+
results.push(file.path);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
// Match by custom patterns
|
|
265
|
+
if (customPatterns?.some((pat) => pathLower.includes(pat.toLowerCase()))) {
|
|
266
|
+
results.push(file.path);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
// Match by path indicators (openapi.yaml, schema.json, etc.)
|
|
270
|
+
const basename = pathLower.split('/').pop() ?? '';
|
|
271
|
+
if (CONTRACT_PATH_INDICATORS.some((ind) => basename.startsWith(ind))) {
|
|
272
|
+
results.push(file.path);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return results;
|
|
276
|
+
}
|
|
277
|
+
function expandDependencies(seed, repoIndex, candidates, maxDepth, capPerSeed) {
|
|
278
|
+
let frontier = [seed];
|
|
279
|
+
let added = 0;
|
|
280
|
+
for (let depth = 0; depth < maxDepth && added < capPerSeed; depth++) {
|
|
281
|
+
const nextFrontier = [];
|
|
282
|
+
for (const file of frontier) {
|
|
283
|
+
// Add imports
|
|
284
|
+
const imports = repoIndex.dep_graph.imports[file] ?? [];
|
|
285
|
+
for (const dep of imports) {
|
|
286
|
+
if (!candidates.has(dep) && added < capPerSeed) {
|
|
287
|
+
candidates.add(dep);
|
|
288
|
+
nextFrontier.push(dep);
|
|
289
|
+
added++;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Add dependents (reverse)
|
|
293
|
+
const dependents = repoIndex.dep_graph.dependents[file] ?? [];
|
|
294
|
+
for (const dep of dependents) {
|
|
295
|
+
if (!candidates.has(dep) && added < capPerSeed) {
|
|
296
|
+
candidates.add(dep);
|
|
297
|
+
nextFrontier.push(dep);
|
|
298
|
+
added++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
frontier = nextFrontier;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=candidates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"candidates.js","sourceRoot":"","sources":["../src/candidates.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;IACnE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI;IAClE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI;IACjE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO;IACjE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;IACjE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IACjE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO;IAC9D,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK;IACpE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAC7D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK;IAC9D,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC7D,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO;CAC9D,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ;IAC9D,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;IAC7D,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM;CAC5D,CAAC,CAAC;AAEH;;2CAE2C;AAC3C,MAAM,kBAAkB,GAAa;IACnC,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,WAAW;IACvD,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY;IACvD,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW;IAClD,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe;CACjD,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,eAAe,GAAa;IAChC,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY;IAClD,SAAS,EAAE,mBAAmB,EAAE,gBAAgB;IAChD,YAAY,EAAE,YAAY,EAAE,YAAY;CACzC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC;AAEjD,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,2DAA2D;AAC3D,MAAM,yBAAyB,GAAG;IAChC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM;CAChD,CAAC;AAEF,wDAAwD;AACxD,MAAM,wBAAwB,GAAG;IAC/B,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY;CAC7C,CAAC;AAQF,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEpE,0EAA0E;IAC1E,MAAM,kBAAkB,GAAG,WAAW;QACpC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,gBAAgB,CAAC,CAAC;QAC/C,CAAC,CAAC,UAAU,CAAC;IAEf,MAAM,KAAK,GAAG,KAAK;SAChB,KAAK,CAAC,iBAAiB,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7D,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAkB,EAClB,SAAoB,EACpB,YAAoB;IAEpB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YACnC,oDAAoD;YACpD,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzC,UAAU,EAAE,CAAC;YACf,CAAC;iBAAM,IAAI,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;gBAChG,UAAU,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,kEAAkE;QAClE,iDAAiD;QACjD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;QAEjD,wCAAwC;QACxC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACnB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,2DAA2D;QAC3D,IAAI,UAAU,GAAG,YAAY,EAAE,CAAC;YAC9B,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,uBAAuB;QACvB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAEzC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CACxB,QAAkB,EAClB,SAAoB;IAEpB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAErC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAC5B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,GAAG,GAAG,CAChE,CAAC;YACF,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,SAAoB,EACpB,MAAsB;IAEtB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAExC,4BAA4B;IAC5B,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAErF,0DAA0D;IAC1D,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC3B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1B,MAAM;YACR,CAAC;YACD,sEAAsE;YACtE,IAAI,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;gBACzF,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,2EAA2E;IAC3E,0EAA0E;IAC1E,sEAAsE;IACtE,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtE,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;IAChE,KAAK,MAAM,UAAU,IAAI,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACvE,IAAI,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAC5D,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,gEAAgE;IAChE,MAAM,cAAc,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,kBAAkB,CAChB,IAAI,EACJ,SAAS,EACT,UAAU,EACV,MAAM,CAAC,gBAAgB,EACvB,MAAM,CAAC,uBAAuB,CAC/B,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,8EAA8E;IAC9E,uEAAuE;IACvE,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,aAAa,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAE7D,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;YACnC,IAAI,UAAU,CAAC,IAAI,IAAI,cAAc;gBAAE,MAAM;YAE7C,iEAAiE;YACjE,qEAAqE;YACrE,6BAA6B;YAC7B,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAChE,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,IAAI,UAAU,CAAC,IAAI,IAAI,cAAc;oBAAE,MAAM;gBAC7C,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpB,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YAED,iCAAiC;YACjC,IAAI,UAAU,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;gBACrC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;oBAC7B,IAAI,UAAU,CAAC,IAAI,IAAI,cAAc;wBAAE,MAAM;oBAC7C,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC7D,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;wBAC9B,IAAI,UAAU,CAAC,IAAI,IAAI,cAAc;4BAAE,MAAM;wBAC7C,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;wBACrB,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACxB,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,MAAM,iBAAiB,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC;QACzC,IAAI,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACpD,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAClD,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,qEAAqE;IACrE,4CAA4C;IAC5C,MAAM,aAAa,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACnF,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACpD,gFAAgF;QAChF,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAChE,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,iEAAiE;YACjE,iEAAiE;YACjE,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;gBACxC,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YACzF,IAAI,SAAS,EAAE,CAAC;gBACd,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,KAAiB,EACjB,cAAyB;IAEzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAE1C,qBAAqB;QACrB,IAAI,yBAAyB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,IAAI,cAAc,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAClD,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CACzB,IAAY,EACZ,SAAoB,EACpB,UAAuB,EACvB,QAAgB,EAChB,UAAkB;IAElB,IAAI,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,IAAI,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QACpE,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,cAAc;YACd,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACxD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,UAAU,EAAE,CAAC;oBAC/C,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACpB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACvB,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9D,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,UAAU,EAAE,CAAC;oBAC/C,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACpB,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACvB,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC;QACH,CAAC;QAED,QAAQ,GAAG,YAAY,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BundleConfidence, BundleFile } from '@codeledger/types';
|
|
2
|
+
/**
|
|
3
|
+
* Assess bundle confidence based on signal quality.
|
|
4
|
+
*
|
|
5
|
+
* A high-confidence bundle has strong keyword matches, concentrated scores,
|
|
6
|
+
* and at least one file with a keyword_match reason in the top positions.
|
|
7
|
+
* A low-confidence bundle was driven by churn/centrality noise with weak
|
|
8
|
+
* keyword signal — the developer should refine their task description.
|
|
9
|
+
*/
|
|
10
|
+
export declare function assessConfidence(files: BundleFile[], _cumulativeScore: number, taskKeywordCount: number): BundleConfidence;
|
|
11
|
+
//# sourceMappingURL=confidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confidence.d.ts","sourceRoot":"","sources":["../src/confidence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAEvF;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,gBAAgB,CA+ElB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assess bundle confidence based on signal quality.
|
|
3
|
+
*
|
|
4
|
+
* A high-confidence bundle has strong keyword matches, concentrated scores,
|
|
5
|
+
* and at least one file with a keyword_match reason in the top positions.
|
|
6
|
+
* A low-confidence bundle was driven by churn/centrality noise with weak
|
|
7
|
+
* keyword signal — the developer should refine their task description.
|
|
8
|
+
*/
|
|
9
|
+
export function assessConfidence(files, _cumulativeScore, taskKeywordCount) {
|
|
10
|
+
const reasons = [];
|
|
11
|
+
let score = 0;
|
|
12
|
+
if (files.length === 0) {
|
|
13
|
+
return { level: 'low', score: 0, reasons: ['Empty bundle — no files matched'] };
|
|
14
|
+
}
|
|
15
|
+
// Factor 1: Keyword coverage — do top files have keyword matches?
|
|
16
|
+
const topFiles = files.slice(0, 5);
|
|
17
|
+
const keywordHits = topFiles.filter((f) => f.reasons.includes('keyword_match'));
|
|
18
|
+
const keywordRatio = keywordHits.length / topFiles.length;
|
|
19
|
+
if (keywordRatio >= 0.6) {
|
|
20
|
+
score += 0.35;
|
|
21
|
+
reasons.push(`Strong keyword signal: ${keywordHits.length}/${topFiles.length} top files match task keywords`);
|
|
22
|
+
}
|
|
23
|
+
else if (keywordRatio > 0) {
|
|
24
|
+
score += 0.15;
|
|
25
|
+
reasons.push(`Weak keyword signal: only ${keywordHits.length}/${topFiles.length} top files match task keywords`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
reasons.push('No keyword matches in top 5 files — selection driven by churn/centrality');
|
|
29
|
+
}
|
|
30
|
+
// Factor 2: Score concentration — is the top file significantly better?
|
|
31
|
+
if (files.length >= 2) {
|
|
32
|
+
const topScore = files[0].score;
|
|
33
|
+
const secondScore = files[1].score;
|
|
34
|
+
if (topScore > 0 && secondScore > 0) {
|
|
35
|
+
const gap = (topScore - secondScore) / topScore;
|
|
36
|
+
if (gap >= 0.3) {
|
|
37
|
+
score += 0.2;
|
|
38
|
+
reasons.push('Clear top-ranked file with significant score gap');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
score += 0.1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Factor 3: Reason diversity — files selected for multiple reasons are stronger
|
|
46
|
+
const avgReasons = files.reduce((sum, f) => sum + f.reasons.length, 0) / files.length;
|
|
47
|
+
if (avgReasons >= 2.0) {
|
|
48
|
+
score += 0.2;
|
|
49
|
+
reasons.push('Files selected for multiple independent reasons');
|
|
50
|
+
}
|
|
51
|
+
else if (avgReasons >= 1.5) {
|
|
52
|
+
score += 0.1;
|
|
53
|
+
}
|
|
54
|
+
// Factor 4: Task specificity — more keywords = more discriminating
|
|
55
|
+
if (taskKeywordCount >= 3) {
|
|
56
|
+
score += 0.15;
|
|
57
|
+
}
|
|
58
|
+
else if (taskKeywordCount >= 2) {
|
|
59
|
+
score += 0.1;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
reasons.push('Task description has few discriminating keywords — consider being more specific');
|
|
63
|
+
}
|
|
64
|
+
// Factor 5: Bundle isn't just test files
|
|
65
|
+
const nonTestFiles = files.filter((f) => !f.reasons.every((r) => r === 'test_relevant'));
|
|
66
|
+
if (nonTestFiles.length >= files.length * 0.5) {
|
|
67
|
+
score += 0.1;
|
|
68
|
+
}
|
|
69
|
+
// Determine level
|
|
70
|
+
let level;
|
|
71
|
+
if (score >= 0.65) {
|
|
72
|
+
level = 'high';
|
|
73
|
+
}
|
|
74
|
+
else if (score >= 0.35) {
|
|
75
|
+
level = 'medium';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
level = 'low';
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
level,
|
|
82
|
+
score: Math.round(score * 100) / 100,
|
|
83
|
+
reasons,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=confidence.js.map
|