offhands 0.1.11 → 0.1.13
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/dist/daemon.mjs +112 -4
- package/package.json +1 -1
package/dist/daemon.mjs
CHANGED
|
@@ -6749,8 +6749,66 @@ var STOPWORDS = /* @__PURE__ */ new Set([
|
|
|
6749
6749
|
"into",
|
|
6750
6750
|
"if",
|
|
6751
6751
|
"then",
|
|
6752
|
-
"than"
|
|
6752
|
+
"than",
|
|
6753
|
+
// Instruction-shaped filler that says how to answer, not what to look at —
|
|
6754
|
+
// by length alone these outranked the task's real nouns ("requirements",
|
|
6755
|
+
// "mirroring" pushed "closing" out of the term list).
|
|
6756
|
+
"requirements",
|
|
6757
|
+
"requirement",
|
|
6758
|
+
"whatever",
|
|
6759
|
+
"existing",
|
|
6760
|
+
"which",
|
|
6761
|
+
"tell",
|
|
6762
|
+
"bullets",
|
|
6763
|
+
"bullet",
|
|
6764
|
+
"fewer",
|
|
6765
|
+
"five",
|
|
6766
|
+
"only",
|
|
6767
|
+
"why",
|
|
6768
|
+
"any",
|
|
6769
|
+
"following",
|
|
6770
|
+
"something",
|
|
6771
|
+
"thing",
|
|
6772
|
+
"things",
|
|
6773
|
+
"just",
|
|
6774
|
+
"about",
|
|
6775
|
+
"there",
|
|
6776
|
+
"their",
|
|
6777
|
+
"them",
|
|
6778
|
+
"they",
|
|
6779
|
+
"these",
|
|
6780
|
+
"those",
|
|
6781
|
+
"your",
|
|
6782
|
+
"back",
|
|
6783
|
+
"let",
|
|
6784
|
+
"lets",
|
|
6785
|
+
"read",
|
|
6786
|
+
"write",
|
|
6787
|
+
"show",
|
|
6788
|
+
"give",
|
|
6789
|
+
"explain",
|
|
6790
|
+
"describe",
|
|
6791
|
+
"files",
|
|
6792
|
+
"file",
|
|
6793
|
+
// Generic action verbs: in code these are everywhere (createX, deleteY), so
|
|
6794
|
+
// "do not create, edit, or delete any files" pulled in guildCreate.ts and
|
|
6795
|
+
// guildDelete.ts. The nouns in a prompt carry the signal; the verb rarely does.
|
|
6796
|
+
"create",
|
|
6797
|
+
"creates",
|
|
6798
|
+
"edit",
|
|
6799
|
+
"edits",
|
|
6800
|
+
"delete",
|
|
6801
|
+
"deletes",
|
|
6802
|
+
"change",
|
|
6803
|
+
"changes",
|
|
6804
|
+
"modify",
|
|
6805
|
+
"update",
|
|
6806
|
+
"updates",
|
|
6807
|
+
"implement",
|
|
6808
|
+
"implementation"
|
|
6753
6809
|
]);
|
|
6810
|
+
var MAX_TERMS = 24;
|
|
6811
|
+
var RIPGREP_TERMS = 8;
|
|
6754
6812
|
function extractTerms(prompt, maxTerms = 8) {
|
|
6755
6813
|
const raw = prompt.toLowerCase().split(/[^a-z0-9._-]+/).map((t2) => t2.replace(/^[._-]+|[._-]+$/g, "")).filter((t2) => t2.length >= 3 && !STOPWORDS.has(t2) && !/^\d+$/.test(t2));
|
|
6756
6814
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6813,6 +6871,10 @@ function pathWords(path) {
|
|
|
6813
6871
|
}
|
|
6814
6872
|
var NOISE_FILES = /* @__PURE__ */ new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock", "composer.lock", "cargo.lock", "poetry.lock"]);
|
|
6815
6873
|
var isNoise = (p2) => NOISE_FILES.has((p2.split("/").pop() ?? "").toLowerCase());
|
|
6874
|
+
var MAX_MATCHED_SHOWN = 14;
|
|
6875
|
+
var MAX_CONTENT_PROBES = 10;
|
|
6876
|
+
var MAX_CONTENT_MATCHES = 6;
|
|
6877
|
+
var MAX_UNMATCHED_WITH_MATCHES = 3;
|
|
6816
6878
|
var MAX_NAME_MATCHES = 10;
|
|
6817
6879
|
var MAX_DIRTY_ONLY = 8;
|
|
6818
6880
|
var MAX_RECENT = 6;
|
|
@@ -6843,7 +6905,14 @@ async function buildContext(workspace, prompt, opts = {}) {
|
|
|
6843
6905
|
if (findings.length === 0) return null;
|
|
6844
6906
|
const rank = (f2) => f2.matchScore > 0 ? f2.dirty ? 5 : 4 : f2.dirty ? 2 : 1;
|
|
6845
6907
|
findings.sort((a2, b2) => rank(b2) - rank(a2) || b2.matchScore - a2.matchScore);
|
|
6846
|
-
const
|
|
6908
|
+
const strongMatches = findings.filter((f2) => f2.matchScore > 0).length;
|
|
6909
|
+
const unmatchedCap = strongMatches >= 3 ? MAX_UNMATCHED_WITH_MATCHES : Infinity;
|
|
6910
|
+
let unmatched = 0;
|
|
6911
|
+
let matchedShown = 0;
|
|
6912
|
+
const shown = findings.filter(
|
|
6913
|
+
(f2) => f2.matchScore > 0 ? ++matchedShown <= MAX_MATCHED_SHOWN : ++unmatched <= unmatchedCap
|
|
6914
|
+
);
|
|
6915
|
+
const { text: block, selectedCount } = formatBlock(prompt, shown, gitState, maxChars);
|
|
6847
6916
|
if (selectedCount === 0) return null;
|
|
6848
6917
|
return {
|
|
6849
6918
|
block,
|
|
@@ -6916,11 +6985,13 @@ async function gatherFindings(workspace, prompt) {
|
|
|
6916
6985
|
}
|
|
6917
6986
|
const ripgrepAvailable = await detectRipgrep();
|
|
6918
6987
|
retrievalOpsRun++;
|
|
6919
|
-
const terms = extractTerms(prompt);
|
|
6988
|
+
const terms = extractTerms(prompt, MAX_TERMS);
|
|
6989
|
+
let listedFileCount = 0;
|
|
6920
6990
|
if (terms.length > 0) {
|
|
6921
6991
|
const listing = await git(workspace, "ls-files", "-co", "--exclude-standard", "-z");
|
|
6922
6992
|
retrievalOpsRun++;
|
|
6923
6993
|
const all = (listing ?? "").split("\0").filter(Boolean);
|
|
6994
|
+
listedFileCount = all.length;
|
|
6924
6995
|
if (all.length > 0 && all.length <= MAX_LS_FILES) {
|
|
6925
6996
|
const stems = /* @__PURE__ */ new Map();
|
|
6926
6997
|
const literals = [];
|
|
@@ -6971,8 +7042,45 @@ async function gatherFindings(workspace, prompt) {
|
|
|
6971
7042
|
}
|
|
6972
7043
|
}
|
|
6973
7044
|
}
|
|
7045
|
+
if (!ripgrepAvailable && terms.length > 0) {
|
|
7046
|
+
const probes = /* @__PURE__ */ new Map();
|
|
7047
|
+
for (const t2 of terms) {
|
|
7048
|
+
const s2 = stem(t2);
|
|
7049
|
+
if (s2.length >= 4 && !probes.has(s2)) probes.set(s2, t2);
|
|
7050
|
+
if (probes.size >= MAX_CONTENT_PROBES) break;
|
|
7051
|
+
}
|
|
7052
|
+
const results = await Promise.all(
|
|
7053
|
+
[...probes].map(async ([s2, wording]) => {
|
|
7054
|
+
const out = await git(workspace, "grep", "-I", "-i", "-F", "-l", "--untracked", "-e", s2);
|
|
7055
|
+
retrievalOpsRun++;
|
|
7056
|
+
return { wording, files: (out ?? "").split("\n").map((l2) => l2.trim()).filter(Boolean) };
|
|
7057
|
+
})
|
|
7058
|
+
);
|
|
7059
|
+
const corpus = Math.max(listedFileCount, 1);
|
|
7060
|
+
const contentScore = /* @__PURE__ */ new Map();
|
|
7061
|
+
for (const { wording, files } of results) {
|
|
7062
|
+
if (files.length === 0 || files.length > corpus / 2) continue;
|
|
7063
|
+
const weight = 0.5 * Math.log(1 + corpus / files.length);
|
|
7064
|
+
for (const file of files.slice(0, 300)) {
|
|
7065
|
+
const entry = contentScore.get(file) ?? { score: 0, words: [] };
|
|
7066
|
+
entry.score += weight;
|
|
7067
|
+
entry.words.push(wording);
|
|
7068
|
+
contentScore.set(file, entry);
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
7071
|
+
let taken = 0;
|
|
7072
|
+
for (const [file, { score, words }] of [...contentScore].sort((a2, b2) => b2[1].score - a2[1].score)) {
|
|
7073
|
+
if (taken >= MAX_CONTENT_MATCHES) break;
|
|
7074
|
+
if (isSecretLike(file) || !existsSync7(join6(workspace, file))) continue;
|
|
7075
|
+
const f2 = touch(file);
|
|
7076
|
+
f2.matchScore += score;
|
|
7077
|
+
if (isDirty2(file)) f2.dirty = true;
|
|
7078
|
+
note(f2, `content mentions ${words.slice(0, 3).map((w2) => `"${w2}"`).join(", ")}`);
|
|
7079
|
+
taken++;
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
6974
7082
|
if (ripgrepAvailable && terms.length > 0) {
|
|
6975
|
-
const hits = await searchTerms(workspace, terms);
|
|
7083
|
+
const hits = await searchTerms(workspace, terms.slice(0, RIPGREP_TERMS));
|
|
6976
7084
|
retrievalOpsRun++;
|
|
6977
7085
|
for (const hit of hits) {
|
|
6978
7086
|
const file = hit.file.replace(/\\/g, "/").replace(/^\.\//, "");
|