@dev-tren/mapd 0.21.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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/MASTER_PROMPT.md +134 -0
  3. package/README.md +494 -0
  4. package/SETUP.md +108 -0
  5. package/UAT.md +77 -0
  6. package/package.json +56 -0
  7. package/src/adapters/github-app.js +79 -0
  8. package/src/agents/anthropicClient.js +18 -0
  9. package/src/agents/llm.js +196 -0
  10. package/src/agents/modelResolver.js +87 -0
  11. package/src/agents/provider.js +222 -0
  12. package/src/chat/commandRunner.js +86 -0
  13. package/src/chat/commands.js +275 -0
  14. package/src/chat/intent.js +87 -0
  15. package/src/chat/llmIntent.js +118 -0
  16. package/src/chat/repl.js +471 -0
  17. package/src/cli.js +1408 -0
  18. package/src/config/index.js +197 -0
  19. package/src/config/schema.js +119 -0
  20. package/src/core/assist.js +64 -0
  21. package/src/core/audit.js +63 -0
  22. package/src/core/changes.js +110 -0
  23. package/src/core/confidence.js +0 -0
  24. package/src/core/configLint.js +141 -0
  25. package/src/core/diagnose.js +262 -0
  26. package/src/core/docs.js +140 -0
  27. package/src/core/doctor.js +134 -0
  28. package/src/core/envFiles.js +43 -0
  29. package/src/core/events.js +53 -0
  30. package/src/core/evidence.js +212 -0
  31. package/src/core/findingScoring.js +20 -0
  32. package/src/core/fix.js +192 -0
  33. package/src/core/fixApply.js +172 -0
  34. package/src/core/frameworkEntries.js +247 -0
  35. package/src/core/gates.js +209 -0
  36. package/src/core/graph.js +467 -0
  37. package/src/core/grounding.js +235 -0
  38. package/src/core/handoff.js +157 -0
  39. package/src/core/importResolver.js +218 -0
  40. package/src/core/improve.js +226 -0
  41. package/src/core/integrate.js +169 -0
  42. package/src/core/intelligence.js +212 -0
  43. package/src/core/modernize.js +370 -0
  44. package/src/core/parseCache.js +64 -0
  45. package/src/core/parser.js +536 -0
  46. package/src/core/policy.js +65 -0
  47. package/src/core/polyglot.js +333 -0
  48. package/src/core/proc.js +25 -0
  49. package/src/core/reachability.js +543 -0
  50. package/src/core/regression.js +193 -0
  51. package/src/core/resolution.js +92 -0
  52. package/src/core/retry.js +61 -0
  53. package/src/core/review.js +219 -0
  54. package/src/core/score.js +338 -0
  55. package/src/core/security.js +0 -0
  56. package/src/core/session.js +143 -0
  57. package/src/core/solutions.js +254 -0
  58. package/src/core/staleness.js +45 -0
  59. package/src/core/testGuidance.js +226 -0
  60. package/src/core/theme.js +50 -0
  61. package/src/core/trace.js +151 -0
  62. package/src/core/verify.js +123 -0
  63. package/src/core/view.js +221 -0
  64. package/src/core/viewServer.js +88 -0
  65. package/src/core/watch.js +76 -0
  66. package/src/core/workspace.js +115 -0
  67. package/src/mcp/server.js +48 -0
  68. package/src/mcp/tools.js +423 -0
  69. package/src/server.js +84 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * intent.js — deterministic, keyword/regex-based natural-language router.
3
+ * No provider call, no API key required — this is what keeps chat fully
4
+ * usable in deterministic-only mode. When a provider IS available, repl.js
5
+ * may additionally ask it to choose from the same fixed action set; this
6
+ * module is the zero-key fallback (and the first thing tried either way,
7
+ * since it's free and instant).
8
+ *
9
+ * Returns a structured action, never free text to execute:
10
+ * { type: "slash", command }
11
+ * { type: "dev-command", cmd, args }
12
+ * { type: "review-action", action: "approve"|"dismiss", id, reason }
13
+ * { type: "fix", id, autoSelectHighestSeverity, autoApply? }
14
+ * { type: "search", query }
15
+ * { type: "watch" }
16
+ * { type: "unknown" }
17
+ */
18
+
19
+ const SLASH_COMMANDS = new Set(["map", "baseline", "check", "docs", "modernize", "review", "findings", "evidence", "project", "context", "status", "diagnose", "handoff", "solutions", "score", "ceiling", "trace", "resolution", "find", "test-gaps", "test-credit", "improve", "verify", "transcript", "help", "clear", "end"]);
20
+
21
+ const RULES = [
22
+ { re: /^run (a |the )?(project )?map\b/i, action: () => ({ type: "slash", command: "/map" }) },
23
+ { re: /^(create|save) (a )?baseline\b/i, action: () => ({ type: "slash", command: "/baseline" }) },
24
+ { re: /check (this|the) project against( its| the)? baseline/i, action: () => ({ type: "slash", command: "/check" }) },
25
+ { re: /^generate (the )?docs\b/i, action: () => ({ type: "slash", command: "/docs" }) },
26
+ { re: /run (the )?modernization scan\b/i, action: () => ({ type: "slash", command: "/modernize" }) },
27
+ { re: /(show|list) (unresolved )?findings\b/i, action: () => ({ type: "slash", command: "/findings" }) },
28
+ { re: /(show|list|start) (the )?(review|approval) queue\b/i, action: () => ({ type: "slash", command: "/review" }) },
29
+ { re: /^(show|what.?s) (the )?(mapd )?status\b/i, action: () => ({ type: "slash", command: "/status" }) },
30
+ { re: /^(diagnose|explain) (the )?(mapd )?(understanding|uncertainty|blind spots|limits)\b/i, action: () => ({ type: "slash", command: "/diagnose" }) },
31
+ { re: /^(hand ?off|package (the )?(top )?findings|write a prompt for (claude code|codex))\b/i, action: () => ({ type: "slash", command: "/handoff" }) },
32
+ { re: /^(run|show( me)?|give me) (the )?(top )?solutions\b/i, action: () => ({ type: "slash", command: "/solutions" }) },
33
+
34
+ // Score Intelligence / Test Guidance / planner / verify — deterministic phrasings
35
+ { re: /(what.?s|how high).*(honest )?ceiling|how high can (the )?(score|confidence) (honestly )?(go|get)|(is|can) (the score|confidence|it) (reach|hit|get to) 1(\.0)?/i, action: () => ({ type: "slash", command: "/score", args: ["ceiling"] }) },
36
+ { re: /(explain|break ?down|what.?s (in|behind)) (the )?(score|confidence)/i, action: () => ({ type: "slash", command: "/score", args: ["explain"] }) },
37
+ { re: /(why|how) (did|has) (the )?(score|confidence) (change|move|drop|rise|go up|go down)|(score|confidence) (delta|since (the )?baseline)/i, action: () => ({ type: "slash", command: "/score", args: ["delta"] }) },
38
+ { re: /(show|list|what|which).*(test gaps|untested files|files (that )?(are )?untested|missing tests)/i, action: () => ({ type: "slash", command: "/test-gaps" }) },
39
+ { re: /(show|find|which).*(padding|fake tests?|name-only tests?)/i, action: () => ({ type: "slash", command: "/test-credit", args: ["--padding"] }) },
40
+ { re: /(show|which test).*(credits?|tests? cover(s|ing)?)/i, action: () => ({ type: "slash", command: "/test-credit" }) },
41
+ { re: /(what should i (do|work on|fix)( next)?|best.*(cleanup|work|use of (my )?time)|(give me|make) (a|an) (improve|improvement|action) plan|how (do i|to) (improve|raise) (the )?(score|confidence))/i, action: () => ({ type: "slash", command: "/improve" }) },
42
+ { re: /(run|do) (a |the )?verif(y|ication)|is (the )?project (ok|okay|good|passing|healthy|green)|are we (good|passing|green)|(run|pass) (the )?(ci )?gate/i, action: () => ({ type: "slash", command: "/verify" }) },
43
+ { re: /(save|export|write) (the |this )?(chat |conversation |session )?transcript|(save|export) (the |this )?(chat|conversation|session)/i, action: () => ({ type: "slash", command: "/transcript" }) },
44
+
45
+ { re: /(?:show|what.?s|explain)( the)? evidence (?:for|behind|on) (?:finding )?(\S+)/i, action: (m) => ({ type: "slash", command: "/evidence", args: [m[2]] }) },
46
+ { re: /why (?:is|was) (?:finding )?(\S+) (?:flagged|reported|raised)/i, action: (m) => ({ type: "slash", command: "/evidence", args: [m[1]] }) },
47
+
48
+ { re: /^trace (\S+)(?: to (\S+))?/i, action: (m) => ({ type: "slash", command: "/trace", args: [m[1], m[2]].filter(Boolean) }) },
49
+ { re: /why (?:is|isn.?t) (\S+) (?:in|part of|out of) (?:the |a )?workflow/i, action: (m) => ({ type: "slash", command: "/trace", args: [m[1]] }) },
50
+ // file-shaped subject (has a "." or "/") + a reachability question → deterministic /trace, not LLM guesswork
51
+ { re: /^(?:why|how) (?:is|isn.?t) (\S*[./]\S*) (?:reachable|unreachable|used|unused|dead|an orphan|orphaned|loaded|imported|included|excluded)\b/i, action: (m) => ({ type: "slash", command: "/trace", args: [m[1]] }) },
52
+ { re: /^(?:is|are) (\S*[./]\S*) (?:reachable|unreachable|used|unused|dead(?: code)?|an orphan|orphaned|imported|loaded)\b/i, action: (m) => ({ type: "slash", command: "/trace", args: [m[1]] }) },
53
+ { re: /(?:show|what.?s) the (?:import|call) chain (?:from|between) (\S+) (?:to|and) (\S+)/i, action: (m) => ({ type: "slash", command: "/trace", args: [m[1], m[2]] }) },
54
+ { re: /(?:what.?s|which calls are) dragging down (?:the )?(?:call )?resolution( rate)?|(?:show|list) unresolved calls/i, action: () => ({ type: "slash", command: "/resolution" }) },
55
+ { re: /^(?:find|look for|where is|where.?s) (?:code (?:related to|about) )?(.+)/i, action: (m) => ({ type: "slash", command: "/find", args: m[1].trim().split(/\s+/) }) },
56
+
57
+ { re: /approve finding (\S+)/i, action: (m) => ({ type: "review-action", action: "approve", id: m[1] }) },
58
+ { re: /dismiss finding (\S+)(?: because (.*))?/i, action: (m) => ({ type: "review-action", action: "dismiss", id: m[1], reason: m[2] ?? null }) },
59
+
60
+ { re: /fix (the )?highest[- ]severity finding/i, action: () => ({ type: "fix", id: null, autoSelectHighestSeverity: true }) },
61
+ { re: /fix finding (\S+)/i, action: (m) => ({ type: "fix", id: m[1], autoSelectHighestSeverity: false }) },
62
+ { re: /apply (a )?fix (where|wherever|if) (you )?can/i, action: () => ({ type: "fix", id: null, autoSelectHighestSeverity: true, autoApply: true }) },
63
+ { re: /apply (the )?fix (for )?finding (\S+)/i, action: (m) => ({ type: "fix", id: m[3], autoSelectHighestSeverity: false, autoApply: true }) },
64
+
65
+ { re: /^run (the )?tests\b/i, action: () => ({ type: "dev-command", cmd: "npm", args: ["test"] }) },
66
+ { re: /^run (the )?linter\b/i, action: () => ({ type: "dev-command", cmd: "npm", args: ["run", "lint"] }) },
67
+ { re: /^(build|typecheck) the project\b/i, action: (m) => ({ type: "dev-command", cmd: "npm", args: ["run", m[1].toLowerCase() === "build" ? "build" : "typecheck"] }) },
68
+ { re: /^install dependencies\b/i, action: () => ({ type: "dev-command", cmd: "npm", args: ["install"] }) },
69
+ { re: /^start (the )?dev(elopment)? server\b/i, action: () => ({ type: "dev-command", cmd: "npm", args: ["run", "dev"] }) },
70
+ { re: /(show|what).*\bgit diff\b/i, action: () => ({ type: "dev-command", cmd: "git", args: ["diff"] }) },
71
+
72
+ { re: /search for (all )?references to (.+)/i, action: (m) => ({ type: "search", query: m[2].trim() }) },
73
+ { re: /^start watching( the project)?\b/i, action: () => ({ type: "watch" }) },
74
+ ];
75
+
76
+ export function classifyIntent(text) {
77
+ const trimmed = text.trim();
78
+ if (trimmed.startsWith("/")) {
79
+ const command = trimmed.slice(1).split(/\s+/)[0].toLowerCase();
80
+ if (SLASH_COMMANDS.has(command)) return { type: "slash", command: `/${command}`, args: trimmed.split(/\s+/).slice(1) };
81
+ }
82
+ for (const rule of RULES) {
83
+ const m = rule.re.exec(trimmed);
84
+ if (m) return rule.action(m);
85
+ }
86
+ return { type: "unknown", text: trimmed };
87
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * llmIntent.js — second-tier, LLM-assisted intent classification. Only
3
+ * consulted when intent.js's deterministic regex router (tier 1) finds no
4
+ * match AND a provider is configured; with no provider, behavior is
5
+ * unchanged (falls straight to grounded Q&A, as before).
6
+ *
7
+ * Deliberately restricted to the read-only/verification command set — never
8
+ * /fix, approve/dismiss, dev-command, or watch. Those stay deterministic-only
9
+ * (exact phrasing via intent.js), so a misclassification here can at worst
10
+ * run an unwanted read-only scan, never mutate the tree, run a shell command,
11
+ * or apply a patch. This is the "no false positives that matter" boundary.
12
+ *
13
+ * Returns a structured, validated action list (never free text to execute):
14
+ * { type: "sequence", commands: ["/map", "/modernize", ...] }
15
+ * { type: "qa" }
16
+ */
17
+
18
+ export const READ_ONLY_COMMANDS = [
19
+ "/map", "/baseline", "/check", "/docs", "/modernize",
20
+ "/review", "/findings", "/project", "/context", "/status", "/diagnose", "/handoff", "/solutions",
21
+ "/score", "/ceiling", "/test-gaps", "/test-credit", "/improve", "/verify",
22
+ ];
23
+
24
+ const SYSTEM_PROMPT = `You are an intent classifier for a CLI tool called Map'd. Your ONLY job is \
25
+ to decide whether the user's message is REQUESTING one or more of a fixed set \
26
+ of read-only project commands to be run, or is instead a question/discussion \
27
+ that should be answered in prose.
28
+
29
+ Fixed commands you may select (nothing else exists):
30
+ ${READ_ONLY_COMMANDS.join(", ")}
31
+
32
+ What each does:
33
+ /map — build the project's workflow map (files, workflows, confidence)
34
+ /baseline — snapshot the current map as the regression baseline
35
+ /check — diff current map against baseline, surface regressions/errors
36
+ /modernize — scan for modernization issues (dead code, duplication, old syntax, optimization opportunities)
37
+ /docs — render project docs
38
+ /review — list the approval queue
39
+ /findings — list open findings
40
+ /project — project/workflow summary
41
+ /context — this session's conversation summary
42
+ /status — confidence + baseline + findings-count summary
43
+ /diagnose — explain current understanding limits: weak confidence signals, runtime blind spots, env contract, next actions
44
+ /handoff — package the top open findings into a ready-to-paste prompt for an external coding agent (Claude Code, Codex)
45
+ /solutions — cluster related findings into data-backed solutions ranked by real workflow blast radius (deeper analysis than /handoff, not just a repackaged list)
46
+ /score — explain the derived confidence: what each signal contributes and what each weak signal costs
47
+ /ceiling — the honest maximum confidence reachable under current constraints (no git, heuristic parsing, etc.), and why 1.0 may be unreachable
48
+ /test-gaps — list workflow files lowering testPresence: untested files and name-only "padding" tests, with suggested filenames
49
+ /test-credit — show which test really credits which source file (imports the module + uses its exports) vs name-only padding
50
+ /improve — a ranked, honest work queue: the tasks that raise confidence most per unit effort, with measured score lift and what NOT to fake
51
+ /verify — one-shot gate: config + map + doctor + baseline regression + score delta → a single pass/warn/fail verdict
52
+
53
+ Rules:
54
+ - Only select a command if the user is clearly asking for it to be RUN or \
55
+ EXECUTED right now — not just mentioning, asking about, or discussing it.
56
+ - If the message is ambiguous, phrased as a question, or asks for an opinion \
57
+ or explanation rather than an action, classify it as "qa". Never guess.
58
+ - A message may request multiple commands in one go (e.g. "run map, then \
59
+ modernize, then check for errors") — list them all, in the order implied.
60
+ - A message may also be a REFERENTIAL follow-up ("run those", "do it", "run \
61
+ them now") pointing at commands named earlier in the conversation. Below the \
62
+ user's message you may see "Recent conversation" — if it's present and the \
63
+ assistant's own prior turn named specific commands (by /name or in prose), \
64
+ resolve the reference against exactly those commands. If no prior turn named \
65
+ any commands from the fixed list, classify as "qa" rather than guessing.
66
+ - "look for errors" / "find problems" / "check for issues" / "what's breaking" means /check.
67
+ - "modernize" / "old syntax" / "dead code" / "duplication" means /modernize.
68
+ - "what should I work on" / "how do I raise the score" / "best use of my time" / "action plan" / "improve the confidence" means /improve (the ranked plan) — NOT /modernize.
69
+ - "explain the score" / "what contributes to confidence" / "why is confidence X" means /score.
70
+ - "what's the ceiling" / "how high can it honestly go" / "can it reach 1.0" means /ceiling.
71
+ - "which files are untested" / "test gaps" / "padding tests" / "fake tests" means /test-gaps (or /test-credit for the credit map).
72
+ - "is the project passing/healthy/green" / "run verification" / "run the gate" means /verify.
73
+ - "diagnose uncertainty" / "explain blind spots" / "what does Map'd not understand yet" means /diagnose.
74
+ - "write a prompt for Claude Code/Codex" / "package the findings" / "hand this off" means /handoff.
75
+ - "what's the real underlying problem" / "cluster the findings" / "what's the bigger picture" means /solutions.
76
+ - Never select anything outside the fixed list above — there is no /fix, \
77
+ /approve, /dismiss, or shell command in your vocabulary; if the user asks for \
78
+ one of those, classify as "qa" and let the deterministic router or grounded \
79
+ Q&A handle it instead.
80
+
81
+ Respond with ONLY a JSON object, no prose, no markdown fences. Examples:
82
+ {"intent": "command", "commands": ["/map", "/modernize", "/check"]}
83
+ {"intent": "qa"}`;
84
+
85
+ /**
86
+ * `provider` is the same `{available(), complete(system, user, maxTokens)}`
87
+ * shape used everywhere else. `conversationContext` (optional) is recent
88
+ * prior turns as plain text — needed to resolve referential follow-ups like
89
+ * "run those commands," which otherwise have nothing to resolve against and
90
+ * fall through to grounded Q&A (which correctly, but unhelpfully, says it
91
+ * can't run anything). Fails safe to `{ type: "qa" }` on no provider, no
92
+ * response, unparsable JSON, or a response outside the fixed vocabulary.
93
+ */
94
+ export async function classifyIntentWithProvider(text, provider, conversationContext = "") {
95
+ if (!provider?.available?.()) return { type: "qa" };
96
+
97
+ const user = conversationContext ? `Recent conversation:\n${conversationContext}\n\nUser's message: ${text}` : text;
98
+ let raw;
99
+ try {
100
+ raw = await provider.complete(SYSTEM_PROMPT, user, 300);
101
+ } catch {
102
+ return { type: "qa" };
103
+ }
104
+ if (!raw) return { type: "qa" };
105
+
106
+ let parsed;
107
+ try {
108
+ const jsonMatch = raw.match(/\{[\s\S]*\}/);
109
+ parsed = JSON.parse(jsonMatch ? jsonMatch[0] : raw);
110
+ } catch {
111
+ return { type: "qa" };
112
+ }
113
+
114
+ if (!parsed || parsed.intent !== "command" || !Array.isArray(parsed.commands)) return { type: "qa" };
115
+ const commands = [...new Set(parsed.commands.filter((c) => READ_ONLY_COMMANDS.includes(c)))];
116
+ if (!commands.length) return { type: "qa" };
117
+ return { type: "sequence", commands };
118
+ }