offhands 0.1.12 → 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 +107 -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,9 @@ 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;
|
|
6816
6877
|
var MAX_UNMATCHED_WITH_MATCHES = 3;
|
|
6817
6878
|
var MAX_NAME_MATCHES = 10;
|
|
6818
6879
|
var MAX_DIRTY_ONLY = 8;
|
|
@@ -6847,7 +6908,10 @@ async function buildContext(workspace, prompt, opts = {}) {
|
|
|
6847
6908
|
const strongMatches = findings.filter((f2) => f2.matchScore > 0).length;
|
|
6848
6909
|
const unmatchedCap = strongMatches >= 3 ? MAX_UNMATCHED_WITH_MATCHES : Infinity;
|
|
6849
6910
|
let unmatched = 0;
|
|
6850
|
-
|
|
6911
|
+
let matchedShown = 0;
|
|
6912
|
+
const shown = findings.filter(
|
|
6913
|
+
(f2) => f2.matchScore > 0 ? ++matchedShown <= MAX_MATCHED_SHOWN : ++unmatched <= unmatchedCap
|
|
6914
|
+
);
|
|
6851
6915
|
const { text: block, selectedCount } = formatBlock(prompt, shown, gitState, maxChars);
|
|
6852
6916
|
if (selectedCount === 0) return null;
|
|
6853
6917
|
return {
|
|
@@ -6921,11 +6985,13 @@ async function gatherFindings(workspace, prompt) {
|
|
|
6921
6985
|
}
|
|
6922
6986
|
const ripgrepAvailable = await detectRipgrep();
|
|
6923
6987
|
retrievalOpsRun++;
|
|
6924
|
-
const terms = extractTerms(prompt);
|
|
6988
|
+
const terms = extractTerms(prompt, MAX_TERMS);
|
|
6989
|
+
let listedFileCount = 0;
|
|
6925
6990
|
if (terms.length > 0) {
|
|
6926
6991
|
const listing = await git(workspace, "ls-files", "-co", "--exclude-standard", "-z");
|
|
6927
6992
|
retrievalOpsRun++;
|
|
6928
6993
|
const all = (listing ?? "").split("\0").filter(Boolean);
|
|
6994
|
+
listedFileCount = all.length;
|
|
6929
6995
|
if (all.length > 0 && all.length <= MAX_LS_FILES) {
|
|
6930
6996
|
const stems = /* @__PURE__ */ new Map();
|
|
6931
6997
|
const literals = [];
|
|
@@ -6976,8 +7042,45 @@ async function gatherFindings(workspace, prompt) {
|
|
|
6976
7042
|
}
|
|
6977
7043
|
}
|
|
6978
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
|
+
}
|
|
6979
7082
|
if (ripgrepAvailable && terms.length > 0) {
|
|
6980
|
-
const hits = await searchTerms(workspace, terms);
|
|
7083
|
+
const hits = await searchTerms(workspace, terms.slice(0, RIPGREP_TERMS));
|
|
6981
7084
|
retrievalOpsRun++;
|
|
6982
7085
|
for (const hit of hits) {
|
|
6983
7086
|
const file = hit.file.replace(/\\/g, "/").replace(/^\.\//, "");
|