@polycode-projects/the-mechanical-code-talker 3.1.2 → 3.1.3
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/corpus/seon/relations.jsonl +2 -0
- package/package.json +1 -1
- package/src/adapters/repository-interface.mjs +3 -0
- package/src/domain/code-explorer-hints.mjs +4 -0
- package/src/domain/codeplan/graph-delta.mjs +1 -0
- package/src/domain/concept.mjs +16 -0
- package/src/domain/interpret/normalize.mjs +9 -1
- package/src/domain/module-paths.mjs +6 -4
- package/src/services/chat.mjs +26 -2
- package/src/surfaces/web/memory-ask-browser.bundle.js +44 -44
- package/src/tools/schema-docs.mjs +10 -0
|
@@ -7,3 +7,5 @@
|
|
|
7
7
|
{"relation":"touches","definition":"A touch is a commit changing a file or a symbol in the codebase.","sense":"software"}
|
|
8
8
|
{"relation":"cochange","definition":"Change-coupling is two files that tend to be changed together in the same commits.","sense":"software"}
|
|
9
9
|
{"relation":"reexports","definition":"A re-export is a module passing another module's definition through as part of its own public API.","sense":"software"}
|
|
10
|
+
{"relation":"serves","definition":"A service edge is one part of the codebase providing or backing another, such as a handler serving a route.","sense":"software"}
|
|
11
|
+
{"relation":"denotes","definition":"A denotation is a vocabulary term naming a code entity, linking the word to the thing it refers to.","sense":"software"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; indexes a repo on request (tmct index) or reads any producer's graph.",
|
|
@@ -46,6 +46,7 @@ export const MISS_REASONS = Object.freeze({
|
|
|
46
46
|
export const EDGE_KINDS = Object.freeze([
|
|
47
47
|
"imports", "calls", "callsSymbol", "defines", "tests",
|
|
48
48
|
"touches", "touchesSymbol", "contains", "inherits", "cochange", "reexports",
|
|
49
|
+
"serves", "denotes",
|
|
49
50
|
]);
|
|
50
51
|
|
|
51
52
|
/** edge-kind → the `tmct:` object property it realizes (the OWL grounding). */
|
|
@@ -61,6 +62,8 @@ export const EDGE_KIND_TO_TMCT = Object.freeze({
|
|
|
61
62
|
inherits: "tmct:extends",
|
|
62
63
|
cochange: "tmct:dependsOn",
|
|
63
64
|
reexports: "tmct:exports",
|
|
65
|
+
serves: "tmct:serves",
|
|
66
|
+
denotes: "tmct:denotes",
|
|
64
67
|
});
|
|
65
68
|
|
|
66
69
|
/** The named services, grouped as in the plan's six-group inventory. Names are
|
|
@@ -42,6 +42,8 @@ const FORWARD_TEMPLATE = Object.freeze({
|
|
|
42
42
|
contains: (f) => `what is in ${f}`,
|
|
43
43
|
defines: (f) => `what does ${f} define`,
|
|
44
44
|
inherits: (f) => `what does ${f} inherit from`,
|
|
45
|
+
serves: (f) => `what does ${f} serve`,
|
|
46
|
+
denotes: (f) => `what does ${f} denote`,
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
// Reverse reading (focus is the object): "what <kind> <focus>".
|
|
@@ -51,6 +53,8 @@ const REVERSE_TEMPLATE = Object.freeze({
|
|
|
51
53
|
tests: (f) => `what tests ${f}`,
|
|
52
54
|
inherits: (f) => `what inherits from ${f}`,
|
|
53
55
|
contains: (f) => `what contains ${f}`,
|
|
56
|
+
serves: (f) => `what serves ${f}`,
|
|
57
|
+
denotes: (f) => `what denotes ${f}`,
|
|
54
58
|
});
|
|
55
59
|
|
|
56
60
|
/** Index a payload into the counts and adjacency the hints read: class →
|
|
@@ -30,6 +30,7 @@ export const ENTITY_CLASSES = Object.freeze([
|
|
|
30
30
|
export const EDGE_PREDICATES = Object.freeze([
|
|
31
31
|
"imports", "calls", "callsSymbol", "defines", "tests",
|
|
32
32
|
"touches", "touchesSymbol", "contains", "inherits", "cochange", "reexports",
|
|
33
|
+
"serves", "denotes",
|
|
33
34
|
]);
|
|
34
35
|
|
|
35
36
|
/** The closed effect vocabulary — every way an operator's declared delta can
|
package/src/domain/concept.mjs
CHANGED
|
@@ -219,6 +219,10 @@ export const RELATION_TERM = Object.freeze({
|
|
|
219
219
|
export: "reexports", exports: "reexports", exporting: "reexports", exported: "reexports",
|
|
220
220
|
reexport: "reexports", reexports: "reexports", reexporting: "reexports",
|
|
221
221
|
"re-export": "reexports", "re-exports": "reexports", "re-exporting": "reexports",
|
|
222
|
+
// provider-declared kinds: tmct's own indexer emits neither, so a graph it
|
|
223
|
+
// built degrades to the honest two-band "no edges" answer rather than a miss.
|
|
224
|
+
serve: "serves", serves: "serves", serving: "serves", served: "serves",
|
|
225
|
+
denote: "denotes", denotes: "denotes", denoting: "denotes", denoted: "denotes",
|
|
222
226
|
});
|
|
223
227
|
|
|
224
228
|
/** concept key → the relationKind()s whose edges it enumerates. A concept can span
|
|
@@ -234,6 +238,8 @@ const RELATION_KINDS = Object.freeze({
|
|
|
234
238
|
touches: ["touches", "touchesSymbol"],
|
|
235
239
|
cochange: ["cochange"],
|
|
236
240
|
reexports: ["reexports"],
|
|
241
|
+
serves: ["serves"],
|
|
242
|
+
denotes: ["denotes"],
|
|
237
243
|
});
|
|
238
244
|
|
|
239
245
|
/** concept key → the verb phrase that renders an edge as an English sentence
|
|
@@ -248,6 +254,8 @@ const RELATION_RENDER = Object.freeze({
|
|
|
248
254
|
touches: { verb: "touches", edgeNoun: "touch" },
|
|
249
255
|
cochange: { verb: "changes together with", edgeNoun: "change-coupling" },
|
|
250
256
|
reexports: { verb: "re-exports", edgeNoun: "re-export" },
|
|
257
|
+
serves: { verb: "serves", edgeNoun: "service" },
|
|
258
|
+
denotes: { verb: "denotes", edgeNoun: "denotation" },
|
|
251
259
|
});
|
|
252
260
|
|
|
253
261
|
/** Per concept key, the candidate follow-up shapes in priority order — each
|
|
@@ -290,6 +298,14 @@ const RELATION_FOLLOWUP_SHAPES = Object.freeze({
|
|
|
290
298
|
{ side: "subj", make: (x) => `what does ${x} export` },
|
|
291
299
|
{ side: "obj", make: (x) => `where is ${x} defined` },
|
|
292
300
|
],
|
|
301
|
+
serves: [
|
|
302
|
+
{ side: "subj", make: (x) => `what does ${x} serve` },
|
|
303
|
+
{ side: "obj", make: (x) => `what serves ${x}` },
|
|
304
|
+
],
|
|
305
|
+
denotes: [
|
|
306
|
+
{ side: "subj", make: (x) => `what does ${x} denote` },
|
|
307
|
+
{ side: "obj", make: (x) => `what denotes ${x}` },
|
|
308
|
+
],
|
|
293
309
|
});
|
|
294
310
|
|
|
295
311
|
/** How many example edges the relation force shows before the remainder is held for
|
|
@@ -186,6 +186,12 @@ const EMBEDDED_MEANS_RE = /^what\s+((?:an?\s+|the\s+)?[\w'-]+(?:\s+[\w'-]+){0,2}
|
|
|
186
186
|
* untouched, a relation/interrogative remainder unwraps to itself, anything
|
|
187
187
|
* else bridges to "describe <thing>". */
|
|
188
188
|
const SHOW_GIVE_ME_RE = /^(?:show|give)\s+me\s+(?:the\s+)?(.+?)\??$/i;
|
|
189
|
+
/** An "everything I need [to <verb>|for X]" remainder always bridges to
|
|
190
|
+
* "describe <thing>", even when its purpose clause happens to contain a
|
|
191
|
+
* relation verb ("everything I need to change X") — RELATION_VERB_RE's
|
|
192
|
+
* bag-of-words probe would otherwise misread that verb as making the
|
|
193
|
+
* remainder itself an already-relational clause. */
|
|
194
|
+
const EVERYTHING_I_NEED_RE = /^everything\s+i(?:'d|\s+would)?\s+need\b/i;
|
|
189
195
|
|
|
190
196
|
/** Leading STACCATO connective before an ALREADY well-formed question ("and
|
|
191
197
|
* what imports it") -> the question alone. Gated on the remainder starting
|
|
@@ -254,7 +260,9 @@ export function applyPreambleFrames(text) {
|
|
|
254
260
|
if (m) {
|
|
255
261
|
const rest = m[1].trim();
|
|
256
262
|
if (!isListingRemainder(rest)) {
|
|
257
|
-
|
|
263
|
+
const isRelationClause = !EVERYTHING_I_NEED_RE.test(rest)
|
|
264
|
+
&& (RELATION_VERB_RE.test(rest) || INTERROGATIVE_LEAD_RE.test(rest));
|
|
265
|
+
q = isRelationClause ? rest : `describe ${rest}`;
|
|
258
266
|
}
|
|
259
267
|
}
|
|
260
268
|
m = q.match(LEADING_CONNECTIVE_RE);
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// the rankers that read what they build.
|
|
3
3
|
|
|
4
4
|
/** Does this module path (or a lowercased path-shaped label) belong to test code?
|
|
5
|
-
* Covers `test/` and `tests/` segments,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Covers `test/` and `tests/` segments, a hyphen/underscore-prefixed test directory
|
|
6
|
+
* (`behaviour-tests/`, `unit_test/`), Python's `test_*.py` convention, .NET's
|
|
7
|
+
* `*.Tests` assembly suffix, and a `.test`/`.spec` file anywhere in the tree.
|
|
8
|
+
* Case-sensitive: callers holding mixed-case paths lowercase first. */
|
|
8
9
|
export const isTestPath = (p) =>
|
|
9
|
-
/(^|\/)tests?\//.test(p) || /(^|\/)test_[^/]*\.py$/.test(p) || /\.tests(\.|$)/.test(p)
|
|
10
|
+
/(^|\/)tests?\//.test(p) || /(^|\/)test_[^/]*\.py$/.test(p) || /\.tests(\.|$)/.test(p)
|
|
11
|
+
|| /(^|\/)[^/]*[-_]tests?\//.test(p) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(p);
|
package/src/services/chat.mjs
CHANGED
|
@@ -128,6 +128,8 @@ const GOAL_BY_KIND = {
|
|
|
128
128
|
touchesSymbol: "understand commit/change history",
|
|
129
129
|
cochange: "understand change-coupling between modules",
|
|
130
130
|
reexports: "understand a module's public exports/API surface",
|
|
131
|
+
serves: "understand which component provides or backs another",
|
|
132
|
+
denotes: "understand which vocabulary term names a code entity",
|
|
131
133
|
};
|
|
132
134
|
const goalNoun = (entityType) => (entityType ? `${String(entityType).toLowerCase()}(s)` : "entities");
|
|
133
135
|
|
|
@@ -2240,6 +2242,8 @@ const MISS_EXAMPLES = {
|
|
|
2240
2242
|
define: ['"where is <name> defined"', '"where is <name> mentioned"'],
|
|
2241
2243
|
meaning: ['"what is a <ClassName>"', '"what does <term> mean"'],
|
|
2242
2244
|
count: ['"how many classes are there"', '"how many modules are there"'],
|
|
2245
|
+
serve: ['"what does <name> serve"', '"what serves <name>"'],
|
|
2246
|
+
denote: ['"what does <name> denote"', '"what denotes <name>"'],
|
|
2243
2247
|
};
|
|
2244
2248
|
const MISS_DEFAULT = ['"which modules import <name>"', '"what calls <name>"'];
|
|
2245
2249
|
|
|
@@ -2251,6 +2255,8 @@ function tailoredExamples(q) {
|
|
|
2251
2255
|
const has = (re) => re.test(q);
|
|
2252
2256
|
if (has(/\bimport/)) return MISS_EXAMPLES.import;
|
|
2253
2257
|
if (has(/\bexport/)) return MISS_EXAMPLES.export;
|
|
2258
|
+
if (has(/\bserv(?:e|es|ed|ing|ice)\b/)) return MISS_EXAMPLES.serve;
|
|
2259
|
+
if (has(/\bdenot(?:e|es|ed|ing|ation)\b/)) return MISS_EXAMPLES.denote;
|
|
2254
2260
|
if (has(/\b(?:calls?|caller|callee)\b/)) return MISS_EXAMPLES.call;
|
|
2255
2261
|
if (has(/\b(?:tests?|cover|covering|tested)\b/)) return MISS_EXAMPLES.test;
|
|
2256
2262
|
if (has(/\b(?:inherit|subclass|extends?|superclass|hierarchy|base class|parent class)\b/)) return MISS_EXAMPLES.inherit;
|
|
@@ -11009,9 +11015,18 @@ const DESCRIBE_GRAIN_WORD_RE = new RegExp(
|
|
|
11009
11015
|
* whole tail into the term), and every resolver downstream then reads
|
|
11010
11016
|
* "context" as part of the name. Closed list, and the term still has to
|
|
11011
11017
|
* resolve afterwards, so a phrase that leaves nothing resolvable simply
|
|
11012
|
-
* declines to the ordinary miss.
|
|
11018
|
+
* declines to the ordinary miss.
|
|
11019
|
+
*
|
|
11020
|
+
* The "everything i need ..." branch is the same shape with a purpose clause
|
|
11021
|
+
* instead of a bare noun: "give me everything i need to change X"/"...for X"
|
|
11022
|
+
* both reach here as "describe everything i need to change X"/"describe
|
|
11023
|
+
* everything i need for X". The "to <verb...>" purpose clause is never split
|
|
11024
|
+
* word-by-word (a multi-word clause like "to work on X" would mis-split into
|
|
11025
|
+
* a captured verb plus a dangling remainder) — the greedy `.*\s` instead
|
|
11026
|
+
* always backtracks to the LAST whitespace run in the match, so the whole
|
|
11027
|
+
* clause strips as one unit and only the trailing symbol/path survives. */
|
|
11013
11028
|
const DESCRIBE_LEADING_NOISE_RE =
|
|
11014
|
-
/^(?:the\s+)?(?:context|details|detail|info|information|background)\s+(?:for|on|about|of|around)\s
|
|
11029
|
+
/^(?:(?:the\s+)?(?:context|details|detail|info|information|background)\s+(?:for|on|about|of|around)\s+|everything\s+i(?:'d|\s+would)?\s+need\s+(?:to\s+.*\s|for\s+))/i;
|
|
11015
11030
|
function stripDescribeLeadingNoise(term) {
|
|
11016
11031
|
const stripped = String(term || "").replace(DESCRIBE_LEADING_NOISE_RE, "").trim();
|
|
11017
11032
|
return stripped || String(term || "").trim();
|
|
@@ -13355,6 +13370,15 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
13355
13370
|
const described = await describeWrapperAnswer(query, { config, source, focus: newFocus, graph, tel });
|
|
13356
13371
|
if (described) {
|
|
13357
13372
|
answer = described.text; via = described.miss ? "miss" : "describe"; recordMiss = !!described.miss;
|
|
13373
|
+
// The composed engine's failed parse above (`via` was still "composed"
|
|
13374
|
+
// to reach this lane at all) can have deduced a goal/canonical for a
|
|
13375
|
+
// DIFFERENT reading it never resolved — e.g. "give me everything i need
|
|
13376
|
+
// to change X" bag-of-words-matches "change" as a touches verb and
|
|
13377
|
+
// deduces subject="describe everything i need". This rescue answers a
|
|
13378
|
+
// different question, so that stale interpretation must not survive
|
|
13379
|
+
// into its answer (same staleness the FUZZY-VERB DECLINE lane guards
|
|
13380
|
+
// against, below).
|
|
13381
|
+
canonical = null; deduced = null;
|
|
13358
13382
|
note(trace, "lane: (4d) DESCRIBE-WRAPPER RESCUE — a polite wrapper around \"describe/tell me about <symbol>\" resolved via /describe, tried last after every other lane declined");
|
|
13359
13383
|
note(trace, "goal: get a symbol's definition/kind/relations (phrased conversationally)");
|
|
13360
13384
|
// Carry the resolved entity forward as the new focus, same class-gated
|