@polycode-projects/the-mechanical-code-talker 0.9.11 → 1.0.0
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/ROADMAP.md +15 -0
- package/corpus/seon/relations.jsonl +1 -0
- package/data/templates/responses.jsonl +5 -3
- package/package.json +1 -1
- package/src/ask-vocab.mjs +25 -2
- package/src/ask.mjs +67 -11
- package/src/chat.mjs +855 -109
- package/src/concept.mjs +50 -9
- package/src/finish.mjs +1 -1
- package/src/interpret/fuzzy.mjs +12 -2
- package/src/interpret/normalize.mjs +90 -1
package/ROADMAP.md
CHANGED
|
@@ -890,6 +890,21 @@ stop signs (full detail + full citation lists in each owning doc):
|
|
|
890
890
|
nobody has published the specific combination with tmct's multi-trust-tier, hard-budget
|
|
891
891
|
requirement. Speculative angle: an ATMS-lite extension to `syllogise.mjs`'s currently-flat
|
|
892
892
|
provenance tag, sketched but unbuilt.
|
|
893
|
+
- **A shared ~2M-word cross-domain ontology (1M general-English base + 1M
|
|
894
|
+
technical/scientific/engineering/programming-language/slang)** — `PLAN_ontology-hierarchies.md`
|
|
895
|
+
§7, additive to (not a revision of) that doc's existing track (e), which stays about importing
|
|
896
|
+
raw WordNet into tmct's own small tier-1 corpus specifically. Walked into, not avoided: merging
|
|
897
|
+
two 1M-word vocabularies collides senses of lexically-shared words (`class`, `cache`, `thread`,
|
|
898
|
+
`wave`, `cell`, `field`, `state`, …) across general/CS/physics/biology/slang registers —
|
|
899
|
+
knowledge-based (non-neural) WSD is real but measurably weaker than supervised/neural WSD (Lesk
|
|
900
|
+
1986; Raganato, Camacho-Collados & Navigli, EACL 2017), and BabelNet (Navigli & Ponzetto,
|
|
901
|
+
*Artificial Intelligence* 193, 2012) proves automatic cross-resource sense merging at this scale
|
|
902
|
+
is achievable — but its own pipeline moved toward statistical/graph-ML methods as it scaled,
|
|
903
|
+
solves the cross-*lingual* not cross-*domain* axis, and carries a non-commercial licence, so it
|
|
904
|
+
is a precedent, not a usable vehicle. Speculative angle recorded: mutual disambiguation from
|
|
905
|
+
already-resolved neighbouring terms in tmct's own closed graph (a structurally-bounded,
|
|
906
|
+
deterministic reading of Gale/Church/Yarowsky's "one sense per discourse/collocation"
|
|
907
|
+
regularities) — not published anywhere found for this application.
|
|
893
908
|
|
|
894
909
|
Every item above is honestly labeled speculative — a direction recorded so it isn't
|
|
895
910
|
re-discovered from scratch, not a committed build plan. None of it is scheduled; the phases above
|
|
@@ -6,3 +6,4 @@
|
|
|
6
6
|
{"relation":"defines","definition":"A definition is where a name (a class, function, or variable) is introduced.","sense":"software"}
|
|
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
|
+
{"relation":"reexports","definition":"A re-export is a module passing another module's definition through as part of its own public API.","sense":"software"}
|
|
@@ -60,10 +60,12 @@
|
|
|
60
60
|
{"id":"conversational-greeting-good-evening","class":"conversational","register":"friendly","template":"Good evening. Ask me about this codebase, or /help."}
|
|
61
61
|
{"id":"conversational-thanks","class":"conversational","register":"friendly","template":"Any time. Ask another, or /help for what I can do."}
|
|
62
62
|
{"id":"conversational-farewell","class":"conversational","register":"friendly","template":"Bye — flushing the session log. Come back with a question any time."}
|
|
63
|
-
{"id":"orientation-friendly","class":"orientation","register":"friendly","template":"I answer questions about THIS codebase's structure — imports, calls, definitions,\nhistory and counts. For example:\n which modules import {example1}\n what calls {example2}\n how many classes are there\n/help for commands, /stats for an overview of the graph."}
|
|
63
|
+
{"id":"orientation-friendly","class":"orientation","register":"friendly","template":"I'm tmct — a deterministic, offline code-graph assistant (no LLM). I answer questions about THIS codebase's structure — imports, calls, definitions,\nhistory and counts. For example:\n which modules import {example1}\n what calls {example2}\n how many classes are there\n/help for commands, /stats for an overview of the graph."}
|
|
64
64
|
{"id":"miss-no-previous-answer","class":"miss","register":"friendly","template":"No previous answer to expand yet — ask me a question first, then say \"why\" or \"say more\"."}
|
|
65
|
-
{"id":"conversational-greeting-empty","class":"conversational","register":"friendly","template":"Hi.
|
|
66
|
-
{"id":"orientation-empty","class":"orientation","register":"friendly","template":"
|
|
65
|
+
{"id":"conversational-greeting-empty","class":"conversational","register":"friendly","template":"Hi. I'm tmct. {vocabHint} Point me at a repo with `--repo <path>` for code-structure questions too (imports, calls, definitions). /help for commands."}
|
|
66
|
+
{"id":"orientation-empty","class":"orientation","register":"friendly","template":"I'm tmct — a deterministic, offline chat assistant (no LLM). {vocabHint} /memory for what I remember.\nFor code structure (imports, calls, definitions) point me at a repo: `--repo <path>`, or try the shipped example `npm run example:mini`. tmct reads graphs; it doesn't index code itself.\n/help for commands."}
|
|
67
|
+
{"id":"identity-self","class":"conversational","register":"friendly","template":"I'm tmct — a deterministic, offline chat assistant. No LLM: wink-nlp parsing over a seeded ontology/lexicon, plus a code graph when you point me at a repo with `--repo <path>`. /help for commands, /stats for an overview."}
|
|
68
|
+
{"id":"identity-not-an-llm","class":"conversational","register":"friendly","template":"No — no LLM involved. tmct is deterministic: wink-nlp parsing over a graph/ontology, not a language model. /help for commands."}
|
|
67
69
|
{"id":"technical-density","class":"count","register":"technical","template":"{subject} carries {count} {noun} across {scope} — a concentration well above what a codebase of this size typically sustains ({provenance})."}
|
|
68
70
|
{"id":"technical-comparison","class":"count","register":"technical","template":"At {count} {noun}, {subject} sits {comparison} the comparable-project baseline, a divergence that reflects deliberate structure rather than measurement noise ({provenance})."}
|
|
69
71
|
{"id":"technical-superlative","class":"count","register":"technical","template":"No {noun} in {scope} is more {metric} than {subject}; it leads the next candidate by a clear margin of {count} ({provenance})."}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
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; no codebase index of its own.",
|
package/src/ask-vocab.mjs
CHANGED
|
@@ -316,6 +316,10 @@ export const MISSPELLINGS = Object.freeze({
|
|
|
316
316
|
"extands": "extends", "extneds": "extends",
|
|
317
317
|
"depnds": "depends",
|
|
318
318
|
"touchs": "touches", "tuoches": "touches", "touhced": "touched",
|
|
319
|
+
// WHERE_MARKERS typo (0.9.13 Tier-1 playtest): "defined" itself had no typo
|
|
320
|
+
// entry, so "where is it defned" fell through to the bare-object search path
|
|
321
|
+
// instead of the where-shape ("no module matching 'it defned' found").
|
|
322
|
+
"defned": "defined",
|
|
319
323
|
"chagned": "changed", "chnaged": "changed",
|
|
320
324
|
"chagnes": "changes", "chnages": "changes",
|
|
321
325
|
"calles": "calls",
|
|
@@ -323,6 +327,16 @@ export const MISSPELLINGS = Object.freeze({
|
|
|
323
327
|
"tets": "tests",
|
|
324
328
|
// grammar anchor words
|
|
325
329
|
"whcih": "which", "wich": "which", "whihc": "which",
|
|
330
|
+
// "wehre"/"whre" (0.9.13 Tier-1 playtest, "where is it defined" drill-down):
|
|
331
|
+
// the WHERE-DEFINED shape's own anchor word had NO typo tolerance at all
|
|
332
|
+
// (unlike which/what/does/the above), so a plain dropped/transposed letter
|
|
333
|
+
// fell straight through resolveObject and hit either the grammar wall or a
|
|
334
|
+
// bogus "no module matching 'it defined'" search. "were" (the missing-h
|
|
335
|
+
// homophone slip) is NOT curated here — it's a real word already load-bearing
|
|
336
|
+
// as the TEMPORAL_AUX auxiliary ("when were the modules last touched"), so
|
|
337
|
+
// that one typo is handled by its own anchored phrasing frame instead
|
|
338
|
+
// (normalize.mjs PHRASING_FRAMES) to avoid clobbering the legitimate reading.
|
|
339
|
+
"wehre": "where", "whre": "where",
|
|
326
340
|
// "wat" (chatbench cycle 2, tf-wat-calls): the internet-casual spelling of
|
|
327
341
|
// "what" — neither curated noise nor a restorable trigger typo, so "wat calls
|
|
328
342
|
// fnAlpha" used to die as "couldn't resolve one of the terms". Restored here
|
|
@@ -381,12 +395,21 @@ export const G_DROP = /\b([a-z]{3,})in'/gi;
|
|
|
381
395
|
/** Words stripped during normalization/keyword-spotting once they carry no
|
|
382
396
|
* grammatical weight for this grammar — greetings, politeness, hedges, and
|
|
383
397
|
* the discourse fillers a spoken-style question picks up. Never strips a
|
|
384
|
-
* relation verb, entity noun, or modifier (those are checked first).
|
|
398
|
+
* relation verb, entity noun, or modifier (those are checked first).
|
|
399
|
+
* Bare "you" (0.9.14 Tier-2 playtest, §3b ESL angle): a non-native word-order
|
|
400
|
+
* slip ("please you tell me what is Class", "you tell me what is Class") left
|
|
401
|
+
* a leading "you" that "could you"/"can you"/"would you" don't cover (those
|
|
402
|
+
* are anchored WRAPPERS requiring the verb-first order) — the leftover
|
|
403
|
+
* pronoun broke the bare "what is Class" no-article count reading, which
|
|
404
|
+
* (unlike the "what is a Class" meta form) requires the WHOLE normalized
|
|
405
|
+
* string to match, not just a substring. "you" carries no grammatical weight
|
|
406
|
+
* in this code-graph grammar (never a real entity/relation term), so it is
|
|
407
|
+
* safe to strip anywhere, same trade as every other word in this list. */
|
|
385
408
|
export const FILLER_WORDS = Object.freeze([
|
|
386
409
|
"um", "uh", "erm", "so", "like", "yo", "hey", "bru", "bro", "fam", "mate",
|
|
387
410
|
"please", "could you", "can you", "would you", "tell me", "i wonder",
|
|
388
411
|
"just wondering", "quickly", "real quick", "kinda", "sorta",
|
|
389
|
-
"btw", "by the way",
|
|
412
|
+
"btw", "by the way", "you",
|
|
390
413
|
]);
|
|
391
414
|
|
|
392
415
|
/** Deictic/pronoun terms that refer to a context entity rather than naming one
|
package/src/ask.mjs
CHANGED
|
@@ -280,7 +280,16 @@ const MAX_COMPOSE_DEPTH = 4;
|
|
|
280
280
|
// treats it as an ordinary object term rather than dropping it.
|
|
281
281
|
const NEST_SENTINEL = "zzinnerset";
|
|
282
282
|
// Filler words dropped at the front of a relative predicate / anaphora filter.
|
|
283
|
-
|
|
283
|
+
// "then"/"though" (Tier-2 playtest, 5th pass): a trailing discourse tag on an
|
|
284
|
+
// otherwise-bare anaphora follow-up — "how many of those THEN", "which of
|
|
285
|
+
// them THOUGH" — used to be read as an (uncompilable) filter clause instead
|
|
286
|
+
// of being dropped as filler, so the follow-up MISSED at PARSE time with a
|
|
287
|
+
// generic "the follow-up filter didn't parse" instead of reaching the
|
|
288
|
+
// friendly eval-time "needs a previous answer" nudge when there was truly no
|
|
289
|
+
// prior set (or the correct count/list when there was one) — the exact same
|
|
290
|
+
// discourse-tag tolerance WHAT_ABOUT_RE already carries for "what about X
|
|
291
|
+
// then"/"what about X though".
|
|
292
|
+
const PRED_LEAD_SKIP = new Set(["that", "which", "who", "are", "is", "was", "were", "do", "does", "also", "still", "both", "and", "then", "though"]);
|
|
284
293
|
const FRAME_WORDS = new Set(["which", "what", "who", "list", "show", "find", "give", "me", "us", "all"]);
|
|
285
294
|
|
|
286
295
|
const entityNoun = (w) => (ENTITY_TO_TYPE[w] ? { entityType: ENTITY_TO_TYPE[w], placeholder: false }
|
|
@@ -519,6 +528,19 @@ function parseTemporal(w, lc, nlp, depth = 0) {
|
|
|
519
528
|
* fires). Returns a {node:"anaphora"} (mode count|list), a miss (filter present but
|
|
520
529
|
* uncompilable), or null. */
|
|
521
530
|
function parseAnaphora(w, lc, nlp) {
|
|
531
|
+
// "which ones"/"which one" — a bare anaphoric re-LIST of the previous result set,
|
|
532
|
+
// phrased as a QUESTION rather than the imperative ("list them") or pronoun-tail
|
|
533
|
+
// ("count them") shapes the loop below already covers. Found live (0.9.14 Tier-2
|
|
534
|
+
// playtest, third pass, numeric/quantifier relation touches): after "how many
|
|
535
|
+
// modules import app/lib/a.mjs" / "which modules import app/lib/a.mjs", the
|
|
536
|
+
// completely natural follow-up "which ones" fell straight through to the generic
|
|
537
|
+
// orientation card — "ones" isn't an ANAPHORA_TRIGGERS pronoun and bare "which"
|
|
538
|
+
// isn't a LIST_TRIGGERS head, so neither existing branch below ever fires for it.
|
|
539
|
+
// Pinned to the WHOLE query (exactly two words) so it never shadows an ordinary
|
|
540
|
+
// "which one of these two functions …" clause, which has more words after "one".
|
|
541
|
+
if (lc.length === 2 && lc[0] === "which" && (lc[1] === "ones" || lc[1] === "one")) {
|
|
542
|
+
return { node: "anaphora", mode: "list", filter: { type: "all" } };
|
|
543
|
+
}
|
|
522
544
|
let p = -1;
|
|
523
545
|
let viaOf = false;
|
|
524
546
|
for (let i = 1; i < lc.length; i += 1) {
|
|
@@ -564,10 +586,16 @@ function parsePredicateFilter(words, nlp) {
|
|
|
564
586
|
* and it must NOT be mistaken for a restrictor (that's the exact bug behind "how many
|
|
565
587
|
* classes are there" → the count-restrictor miss). Combined with STOPWORDS (which
|
|
566
588
|
* already carries are/there/is/in/the/…) at the call site, so only the non-stopword
|
|
567
|
-
* extras live here. A tail with ANY word outside this ∪ STOPWORDS is a real restrictor.
|
|
589
|
+
* extras live here. A tail with ANY word outside this ∪ STOPWORDS is a real restrictor.
|
|
590
|
+
* "this"/"that" (0.9.14 Tier-2 playtest): "which classes exist IN THIS codebase"/
|
|
591
|
+
* "which methods exist in this codebase" used to miss — "codebase" alone was already
|
|
592
|
+
* filler, but the demonstrative right before it ("this"/"that") is not a STOPWORDS
|
|
593
|
+
* entry, so the tail read as non-filler and the whole thing fell through to the
|
|
594
|
+
* grammar wall instead of the bare list. */
|
|
568
595
|
const AGG_TAIL_FILLER = new Set([
|
|
569
596
|
"total", "altogether", "overall", "exist", "exists", "existing", "present",
|
|
570
597
|
"here", "now", "currently", "graph", "index", "codebase", "repo", "repository",
|
|
598
|
+
"this", "that",
|
|
571
599
|
]);
|
|
572
600
|
|
|
573
601
|
/** AGGREGATE / COUNT: "how many <entity> [<restrictor>]", "count <entity>",
|
|
@@ -605,6 +633,12 @@ const LIST_SKIP = new Set(["the", "a", "an", "all", "me", "us"]);
|
|
|
605
633
|
const LIST_TRIGGERS_SORTED = [...LIST_TRIGGERS].sort((a, b) => b.split(" ").length - a.split(" ").length);
|
|
606
634
|
// The listable node classes, named in the honest miss and the empty-index message.
|
|
607
635
|
const LISTABLE_KINDS = "functions, classes, methods, modules, attributes, variables, or commits";
|
|
636
|
+
// SCOPE PREPOSITIONS (HANDOVER item 12.2/11.1): a leading "in"/"inside"/"under" right
|
|
637
|
+
// after the entity noun (past an optional copula) is an unambiguous LOCATION-SCOPE
|
|
638
|
+
// tail, never a reverse-clause predicate object — "which modules import X" has no
|
|
639
|
+
// preposition there at all. Narrowly scoped to just these three words so the
|
|
640
|
+
// interrogative exception below can't be mistaken for a general tail-acceptance.
|
|
641
|
+
const SCOPE_PREPOSITIONS = new Set(["in", "inside", "under"]);
|
|
608
642
|
|
|
609
643
|
/** LIST: "list <kind>", "show me the <kind>s", "what are the <kind>", "list <kind> in
|
|
610
644
|
* <module>". A sibling of the count node — it enumerates the individuals of a class
|
|
@@ -616,7 +650,14 @@ const LISTABLE_KINDS = "functions, classes, methods, modules, attributes, variab
|
|
|
616
650
|
* (reusing membership/relational/boolean), and its `scoped` flag suppresses the
|
|
617
651
|
* "narrow with …" hint. An unknown kind after a clear imperative trigger ("list
|
|
618
652
|
* bananas") is an honest miss naming the listable kinds; anything less certain falls
|
|
619
|
-
* through (null) to the existing parser/cascade rather than guessing.
|
|
653
|
+
* through (null) to the existing parser/cascade rather than guessing.
|
|
654
|
+
*
|
|
655
|
+
* A single narrowly-scoped EXCEPTION to the interrogative gate (HANDOVER item
|
|
656
|
+
* 12.2/11.1): "what/which <kind> [is|are] in|inside|under <scope>" — a leading scope
|
|
657
|
+
* preposition, past an optional copula, straight after the entity noun — is routed the
|
|
658
|
+
* SAME way the imperative "list <kind> in <scope>" already is. This does NOT widen the
|
|
659
|
+
* general decline: "which modules import X"/"which functions call X" have a VERB there,
|
|
660
|
+
* not a scope preposition, so they still fall through untouched. */
|
|
620
661
|
function parseList(w, lc, nlp, depth) {
|
|
621
662
|
let i = 0;
|
|
622
663
|
let interrogative = false;
|
|
@@ -650,18 +691,33 @@ function parseList(w, lc, nlp, depth) {
|
|
|
650
691
|
i += 1;
|
|
651
692
|
const tail = w.slice(i);
|
|
652
693
|
const tailMeaningful = lc.slice(i).some((t) => !STOPWORDS.has(t) && !AGG_TAIL_FILLER.has(t));
|
|
694
|
+
// SCOPE-PREPOSITION exception (HANDOVER 12.2/11.1): past an optional copula
|
|
695
|
+
// ("is"/"are"), does the tail open with "in"/"inside"/"under"? If so this is a
|
|
696
|
+
// location-scope tail ("what modules ARE IN app/lib"), not a reverse-clause
|
|
697
|
+
// predicate — strip only the copula (the imperative form never has one: "list
|
|
698
|
+
// modules in app/lib") so the membership-tail text this builds below is byte-
|
|
699
|
+
// identical in shape to what the imperative path already builds.
|
|
700
|
+
let scopeTailLc = lc.slice(i);
|
|
701
|
+
let scopeTailWords = tail;
|
|
702
|
+
if (scopeTailLc[0] === "is" || scopeTailLc[0] === "are") {
|
|
703
|
+
scopeTailLc = scopeTailLc.slice(1);
|
|
704
|
+
scopeTailWords = scopeTailWords.slice(1);
|
|
705
|
+
}
|
|
706
|
+
const scopedException = interrogative && SCOPE_PREPOSITIONS.has(scopeTailLc[0]);
|
|
653
707
|
// The bare interrogative "what/which <kind>" is a list ONLY with an explicit
|
|
654
|
-
// list-confirming filler tail ("… are there", "… that exist")
|
|
655
|
-
// ("which functions call X") is a reverse
|
|
656
|
-
//
|
|
657
|
-
// qualifier ("which shiny methods" →
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
|
|
708
|
+
// list-confirming filler tail ("… are there", "… that exist") or the scoped
|
|
709
|
+
// exception just above: a real predicate ("which functions call X") is a reverse
|
|
710
|
+
// query, and a *bare* "which methods" is left alone deliberately — otherwise the
|
|
711
|
+
// relaxation cascade could drop an unknown qualifier ("which shiny methods" →
|
|
712
|
+
// "which methods") and silently list everything, erasing the honest "unknown
|
|
713
|
+
// qualifier" miss. Imperative triggers ("list methods") carry their own list
|
|
714
|
+
// intent, so they need no such tail.
|
|
715
|
+
if (interrogative && ((tailMeaningful && !scopedException) || tail.length === 0)) return null;
|
|
661
716
|
let base;
|
|
662
717
|
let scoped = false;
|
|
663
718
|
if (tailMeaningful) {
|
|
664
|
-
const
|
|
719
|
+
const useTail = scopedException ? scopeTailWords : tail;
|
|
720
|
+
const setAst = parseSetPhrase(`which ${[...quals, entWord, ...useTail].join(" ")}`, nlp, (depth || 0) + 1);
|
|
665
721
|
if (!setAst || setAst.node === "miss") return { node: "miss", reason: (setAst && setAst.reason) || "the list filter didn't parse" };
|
|
666
722
|
base = setAst;
|
|
667
723
|
scoped = true;
|