preflight-dev 3.1.0 → 3.2.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 (57) hide show
  1. package/README.md +77 -16
  2. package/dist/cli/init.js +0 -48
  3. package/dist/cli/init.js.map +1 -1
  4. package/dist/index.js +7 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/contracts.d.ts +27 -0
  7. package/dist/lib/contracts.js +309 -0
  8. package/dist/lib/contracts.js.map +1 -0
  9. package/dist/lib/patterns.d.ts +38 -0
  10. package/dist/lib/patterns.js +176 -0
  11. package/dist/lib/patterns.js.map +1 -0
  12. package/dist/lib/triage.d.ts +2 -0
  13. package/dist/lib/triage.js.map +1 -1
  14. package/dist/profiles.js +4 -0
  15. package/dist/profiles.js.map +1 -1
  16. package/dist/tools/check-patterns.d.ts +2 -0
  17. package/dist/tools/check-patterns.js +33 -0
  18. package/dist/tools/check-patterns.js.map +1 -0
  19. package/dist/tools/clarify-intent.js +9 -1
  20. package/dist/tools/clarify-intent.js.map +1 -1
  21. package/dist/tools/enrich-agent-task.js +132 -3
  22. package/dist/tools/enrich-agent-task.js.map +1 -1
  23. package/dist/tools/estimate-cost.d.ts +2 -0
  24. package/dist/tools/estimate-cost.js +261 -0
  25. package/dist/tools/estimate-cost.js.map +1 -0
  26. package/dist/tools/generate-scorecard.js +466 -14
  27. package/dist/tools/generate-scorecard.js.map +1 -1
  28. package/dist/tools/log-correction.js +7 -1
  29. package/dist/tools/log-correction.js.map +1 -1
  30. package/dist/tools/onboard-project.js +10 -1
  31. package/dist/tools/onboard-project.js.map +1 -1
  32. package/dist/tools/preflight-check.js +16 -0
  33. package/dist/tools/preflight-check.js.map +1 -1
  34. package/dist/tools/scope-work.js +6 -0
  35. package/dist/tools/scope-work.js.map +1 -1
  36. package/dist/tools/search-contracts.d.ts +2 -0
  37. package/dist/tools/search-contracts.js +46 -0
  38. package/dist/tools/search-contracts.js.map +1 -0
  39. package/dist/tools/session-stats.js +2 -0
  40. package/dist/tools/session-stats.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/index.ts +7 -0
  43. package/src/lib/contracts.ts +354 -0
  44. package/src/lib/patterns.ts +210 -0
  45. package/src/lib/triage.ts +2 -0
  46. package/src/profiles.ts +4 -0
  47. package/src/tools/check-patterns.ts +43 -0
  48. package/src/tools/clarify-intent.ts +10 -1
  49. package/src/tools/enrich-agent-task.ts +150 -3
  50. package/src/tools/estimate-cost.ts +332 -0
  51. package/src/tools/generate-scorecard.ts +541 -14
  52. package/src/tools/log-correction.ts +8 -1
  53. package/src/tools/onboard-project.ts +10 -1
  54. package/src/tools/preflight-check.ts +19 -0
  55. package/src/tools/scope-work.ts +7 -0
  56. package/src/tools/search-contracts.ts +61 -0
  57. package/src/tools/session-stats.ts +2 -0
@@ -11,6 +11,7 @@ import { findWorkspaceDocs } from "../lib/files.js";
11
11
  import { getConfig } from "../lib/config.js";
12
12
  import { searchSemantic } from "../lib/timeline-db.js";
13
13
  import { basename, join } from "path";
14
+ import { loadPatterns, matchPatterns, formatPatternMatches } from "../lib/patterns.js";
14
15
 
15
16
  // ---------------------------------------------------------------------------
16
17
  // Helpers
@@ -201,6 +202,16 @@ export function registerPreflightCheck(server: McpServer): void {
201
202
  if (force_level === "light") effectiveLevel = "ambiguous";
202
203
  if (force_level === "full") effectiveLevel = "multi-step";
203
204
 
205
+ // --- Pattern matching ---
206
+ const patterns = loadPatterns();
207
+ const patternMatches = matchPatterns(prompt, patterns);
208
+
209
+ // Boost triage level if patterns match
210
+ if (patternMatches.length > 0 && effectiveLevel === "trivial") {
211
+ effectiveLevel = "ambiguous";
212
+ triage.reasons.push(`matches ${patternMatches.length} known correction pattern(s)`);
213
+ }
214
+
204
215
  // --- Trivial ---
205
216
  if (effectiveLevel === "trivial") {
206
217
  return { content: [{ type: "text" as const, text: "✅ Preflight: clear to proceed." }] };
@@ -212,6 +223,14 @@ export function registerPreflightCheck(server: McpServer): void {
212
223
  `_Reasons: ${triage.reasons.join("; ")}_`,
213
224
  ];
214
225
 
226
+ // --- Pattern warnings ---
227
+ if (patternMatches.length > 0) {
228
+ sections.push("");
229
+ for (const p of patternMatches) {
230
+ sections.push(`⚡ Known pitfall: "${p.pattern}" (you've corrected this ${p.frequency}x before)`);
231
+ }
232
+ }
233
+
215
234
  // --- Clear: verify files ---
216
235
  if (effectiveLevel === "clear") {
217
236
  const filePaths = extractFilePaths(prompt);
@@ -8,6 +8,7 @@ import { getRelatedProjects } from "../lib/config.js";
8
8
  import { now } from "../lib/state.js";
9
9
  import { existsSync } from "fs";
10
10
  import { join, normalize, resolve, basename } from "path";
11
+ import { loadAllContracts, searchContracts, formatContracts } from "../lib/contracts.js";
11
12
 
12
13
  const STOP_WORDS = new Set([
13
14
  "the", "and", "for", "with", "from", "that", "this", "should", "would", "could",
@@ -163,6 +164,11 @@ export function registerScopeWork(server: McpServer): void {
163
164
  ? docEntries.map(([name]) => `- \`${name}\``).join("\n")
164
165
  : "- (none found)";
165
166
 
167
+ // Check contracts FIRST (fast, no vector search)
168
+ const contractDirs = [resolve(PROJECT_DIR), ...getRelatedProjects()];
169
+ const allContracts = loadAllContracts(contractDirs);
170
+ const matchedContracts = searchContracts(task, allContracts);
171
+
166
172
  // Get cross-project context
167
173
  const relatedContext = await searchRelatedProjectContext(task);
168
174
 
@@ -197,6 +203,7 @@ ${docLines}
197
203
  ${claudeMd ? "- `CLAUDE.md` exists (project instructions)" : ""}
198
204
  ${agentsMd ? "- `.claude/AGENTS.md` exists" : ""}
199
205
 
206
+ ${matchedContracts.length > 0 ? `## 📑 Matching Contracts\n${formatContracts(matchedContracts, 8)}\n` : ""}
200
207
  ${relatedContext.length > 0 ? `## 🔗 Related Project Context\n${relatedContext.join("\n")}\n` : ""}
201
208
  ---
202
209
 
@@ -0,0 +1,61 @@
1
+ import { z } from "zod";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { resolve, basename } from "path";
4
+ import { PROJECT_DIR } from "../lib/files.js";
5
+ import { getRelatedProjects } from "../lib/config.js";
6
+ import { loadAllContracts, searchContracts } from "../lib/contracts.js";
7
+
8
+ export function registerSearchContracts(server: McpServer): void {
9
+ server.tool(
10
+ "search_contracts",
11
+ "Search API contracts, types, and schemas across current and related projects. Fast lookup without vector search.",
12
+ {
13
+ query: z.string().describe("What to search for"),
14
+ scope: z.enum(["current", "related", "all"]).default("all").describe("Which projects to search"),
15
+ kind: z.enum(["interface", "type", "enum", "route", "schema", "event", "model", "all"]).default("all").describe("Filter by contract kind"),
16
+ },
17
+ async ({ query, scope, kind }) => {
18
+ const projectDirs: string[] = [];
19
+
20
+ if (scope === "current" || scope === "all") {
21
+ projectDirs.push(resolve(PROJECT_DIR));
22
+ }
23
+ if (scope === "related" || scope === "all") {
24
+ projectDirs.push(...getRelatedProjects());
25
+ }
26
+
27
+ if (projectDirs.length === 0) {
28
+ return { content: [{ type: "text" as const, text: "No projects configured. Run onboard_project first." }] };
29
+ }
30
+
31
+ let contracts = loadAllContracts(projectDirs);
32
+
33
+ // Filter by kind
34
+ if (kind !== "all") {
35
+ contracts = contracts.filter(c => c.kind === kind);
36
+ }
37
+
38
+ // Search
39
+ const results = searchContracts(query, contracts);
40
+
41
+ if (results.length === 0) {
42
+ return { content: [{ type: "text" as const, text: `No contracts matching "${query}" found across ${projectDirs.length} project(s).` }] };
43
+ }
44
+
45
+ const output = [
46
+ `## Contract Search: "${query}"`,
47
+ `Found ${results.length} matching contract(s):\n`,
48
+ ...results.slice(0, 20).map(c => {
49
+ const proj = basename(c.project);
50
+ return `### ${c.kind} \`${c.name}\` (${proj})\nFile: \`${c.file}\`\n\`\`\`\n${c.definition}\n\`\`\``;
51
+ }),
52
+ ];
53
+
54
+ if (results.length > 20) {
55
+ output.push(`\n...and ${results.length - 20} more. Narrow your query or filter by kind.`);
56
+ }
57
+
58
+ return { content: [{ type: "text" as const, text: output.join("\n") }] };
59
+ }
60
+ );
61
+ }
@@ -124,6 +124,8 @@ export function registerSessionStats(server: McpServer): void {
124
124
  ``,
125
125
  `Most active branches:`,
126
126
  topBranches || " (none)",
127
+ ``,
128
+ `💰 Run estimate_cost for detailed cost analysis.`,
127
129
  ].join("\n");
128
130
 
129
131
  return {