@mjasnikovs/pi-task 0.40.4 → 0.40.5
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.
|
@@ -22,10 +22,28 @@ const MIN_TOKEN_LEN = 2;
|
|
|
22
22
|
* aliased members spend the whole budget on hops.
|
|
23
23
|
*/
|
|
24
24
|
const MAX_ALIAS_HOPS = 3;
|
|
25
|
+
/**
|
|
26
|
+
* How many smallest-first candidates the value hop reads before giving up.
|
|
27
|
+
*
|
|
28
|
+
* A whole-word check cannot be pushed into SQL, so it runs over the shortest few.
|
|
29
|
+
* Only a name whose every shorter occurrence is a substring of a longer identifier
|
|
30
|
+
* needs more than a handful, and that name is not the one the query asked about.
|
|
31
|
+
*/
|
|
32
|
+
const VALUE_CHUNK_CANDIDATES = 8;
|
|
25
33
|
/** Backstop for a caller that names no ecosystem; every real one passes its own. */
|
|
26
34
|
const DEFAULT_TYPE_KEYWORDS = ['interface', 'type', 'class', 'enum'];
|
|
27
35
|
/** A member declared as a bare capitalised type: `get: HandlerInterface<…>`. */
|
|
28
36
|
const MEMBER_TYPE_RE = /^\s*(?:readonly\s+)?([A-Za-z_$][\w$]*)\??\s*:\s*([A-Z][A-Za-z0-9_]*)\s*[<;,)|&]/gm;
|
|
37
|
+
/**
|
|
38
|
+
* A token that is a symbol rather than English: capitalised, or carrying an
|
|
39
|
+
* underscore or an internal capital.
|
|
40
|
+
*
|
|
41
|
+
* `/^[A-Z]/` alone was the whole rule, and it reached none of the 17 declarations
|
|
42
|
+
* the 2026-09-06 run named and never retrieved — `safeParse`, `from_str`,
|
|
43
|
+
* `into_make_service`, `parseJSON`. Widening to every token instead would hop on
|
|
44
|
+
* `signature` and `return`, spending a slot on whichever prose chunk is shortest.
|
|
45
|
+
*/
|
|
46
|
+
const IDENTIFIER_SHAPED = /^(?:[A-Z][A-Za-z0-9_]{2,}|[a-z][A-Za-z0-9]*(?:_[A-Za-z0-9_]+|[A-Z][A-Za-z0-9_]*)[A-Za-z0-9_]*)$/;
|
|
29
47
|
const TYPE_DECL_RE = /\b(?:interface|type|class|data|newtype|struct|trait|enum)\s+([A-Z][A-Za-z0-9_]*)/g;
|
|
30
48
|
/** The `<E extends Env, BasePath extends string>` a declaration introduces itself. */
|
|
31
49
|
const TYPE_PARAMS_RE = /<([^<>]*)>/g;
|
|
@@ -128,17 +146,21 @@ function hopNames(text, tokens) {
|
|
|
128
146
|
}
|
|
129
147
|
const asked = new Set(tokens.map(t => t.toLowerCase()));
|
|
130
148
|
const out = [];
|
|
131
|
-
// A
|
|
132
|
-
//
|
|
133
|
-
//
|
|
149
|
+
// A name the QUERY itself asks about. scotty's seven failures were all of this
|
|
150
|
+
// shape: `type ActionM = ActionT IO` sits in one chunk of 312 while 67 chunks
|
|
151
|
+
// USE the name, and a chunk carrying BOTH query terms
|
|
134
152
|
// (`get :: RoutePattern -> ActionM () -> ScottyM ()`) outranks the definition
|
|
135
153
|
// every time. Reading the ranked output, all eight slots went to uses.
|
|
154
|
+
// Not capped. MAX_ALIAS_HOPS bounds hops DERIVED from a chunk, where one chunk
|
|
155
|
+
// full of aliased members could generate them without end; a query names the
|
|
156
|
+
// handful of symbols it names, and that is the bound. Capping these at 3 as well
|
|
157
|
+
// cost 4 of the 6 recoveries this hop exists for — measured on the 2026-09-06
|
|
158
|
+
// run's own 35 named declarations: 17 missed uncapped-baseline, 15 at a cap of
|
|
159
|
+
// 3, 11 at a cap of 8. The content budget is what stops it running long.
|
|
136
160
|
for (const t of tokens) {
|
|
137
|
-
if (
|
|
161
|
+
if (!IDENTIFIER_SHAPED.test(t) || declared.has(t) || out.includes(t))
|
|
138
162
|
continue;
|
|
139
163
|
out.push(t);
|
|
140
|
-
if (out.length >= MAX_ALIAS_HOPS)
|
|
141
|
-
return out;
|
|
142
164
|
}
|
|
143
165
|
for (const m of text.matchAll(MEMBER_TYPE_RE)) {
|
|
144
166
|
const [, member, typeName] = m;
|
|
@@ -166,10 +188,45 @@ function definitionChunk(cache, opts, name) {
|
|
|
166
188
|
AND (${where})
|
|
167
189
|
ORDER BY length(content) LIMIT 1`)
|
|
168
190
|
.get(opts.ecosystem, opts.name, opts.version, ...keywords.map(k => `*${k} ${name}[ <={(=]*`));
|
|
191
|
+
if (row) {
|
|
192
|
+
return {
|
|
193
|
+
filePath: row.file_path,
|
|
194
|
+
kind: row.kind,
|
|
195
|
+
content: row.content,
|
|
196
|
+
rank: row.rank
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return valueChunk(cache, opts, name);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The smallest chunk declaring `name` where `name` is a VALUE, not a type.
|
|
203
|
+
*
|
|
204
|
+
* The keyword GLOB above finds `type ActionM` and `struct Config`; nothing it can
|
|
205
|
+
* spell finds `pub fn from_str<'a, T>` or `decodeValue :: String -> …`, and those
|
|
206
|
+
* were 17 of the 35 declarations the 2026-09-06 run named and never retrieved.
|
|
207
|
+
*
|
|
208
|
+
* Smallest-first is the same reasoning as the type path, and it is what makes this
|
|
209
|
+
* safe without a per-language declaration grammar: the surface extractor emits one
|
|
210
|
+
* declaration per chunk, so the short chunk carrying the name IS its declaration and
|
|
211
|
+
* the long ones are the prose that merely mentions it.
|
|
212
|
+
*/
|
|
213
|
+
function valueChunk(cache, opts, name) {
|
|
214
|
+
const rows = cache.db
|
|
215
|
+
.prepare(`SELECT file_path, kind, content, 0 AS rank FROM chunks
|
|
216
|
+
WHERE ecosystem = ?1 AND name = ?2 AND version = ?3 AND content LIKE ?4
|
|
217
|
+
ORDER BY length(content) LIMIT ?5`)
|
|
218
|
+
.all(opts.ecosystem, opts.name, opts.version, `%${name}%`, VALUE_CHUNK_CANDIDATES);
|
|
219
|
+
// LIKE has no word boundary, so `decodeFile` matches `decodeFileStrict` — the
|
|
220
|
+
// wrong declaration, and the exact confusion these runs keep producing.
|
|
221
|
+
const whole = new RegExp(`(?<![A-Za-z0-9_])${escapeRe(name)}(?![A-Za-z0-9_])`);
|
|
222
|
+
const row = rows.find(r => whole.test(r.content));
|
|
169
223
|
if (!row)
|
|
170
224
|
return null;
|
|
171
225
|
return { filePath: row.file_path, kind: row.kind, content: row.content, rank: row.rank };
|
|
172
226
|
}
|
|
227
|
+
function escapeRe(s) {
|
|
228
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
229
|
+
}
|
|
173
230
|
export function retrieveChunks(cache, opts) {
|
|
174
231
|
const limit = opts.limit ?? DEFAULT_LIMIT;
|
|
175
232
|
const budget = opts.contentBudget ?? DEFAULT_BUDGET;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.5",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|