midnight-mcp 0.1.9 → 0.1.11

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.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { githubClient } from "../../pipeline/index.js";
6
6
  import { releaseTracker } from "../../pipeline/releases.js";
7
- import { logger, DEFAULT_REPOSITORIES } from "../../utils/index.js";
7
+ import { logger, DEFAULT_REPOSITORIES, SelfCorrectionHints, } from "../../utils/index.js";
8
8
  import { REPO_ALIASES, EXAMPLES } from "./constants.js";
9
9
  /**
10
10
  * Resolve repository name alias to owner/repo
@@ -36,18 +36,11 @@ export async function getFile(input) {
36
36
  logger.debug("Getting file", { repo: input.repo, path: input.path });
37
37
  const repoInfo = resolveRepo(input.repo);
38
38
  if (!repoInfo) {
39
- return {
40
- error: `Unknown repository: ${input.repo}`,
41
- suggestion: `Valid repositories: ${Object.keys(REPO_ALIASES).join(", ")}`,
42
- };
39
+ return SelfCorrectionHints.UNKNOWN_REPO(input.repo, Object.keys(REPO_ALIASES));
43
40
  }
44
41
  const file = await githubClient.getFileContent(repoInfo.owner, repoInfo.repo, input.path, input.ref);
45
42
  if (!file) {
46
- return {
47
- error: `File not found: ${input.path}`,
48
- repository: `${repoInfo.owner}/${repoInfo.repo}`,
49
- suggestion: "Check the file path and try again. Use midnight:list-examples to see available example files.",
50
- };
43
+ return SelfCorrectionHints.FILE_NOT_FOUND(input.path, `${repoInfo.owner}/${repoInfo.repo}`);
51
44
  }
52
45
  return {
53
46
  content: file.content,
@@ -335,4 +328,185 @@ export async function getLatestSyntax(input) {
335
328
  note: `This is the authoritative syntax reference at version ${reference.version}. Use this to ensure contracts are compilable.`,
336
329
  };
337
330
  }
331
+ // ============================================================================
332
+ // COMPOUND TOOLS - Reduce multiple API calls to single operations
333
+ // These tools combine related operations to minimize round-trips and token usage
334
+ // ============================================================================
335
+ /**
336
+ * Compound tool: Full upgrade check
337
+ * Combines: getVersionInfo + checkBreakingChanges + getMigrationGuide
338
+ * Reduces 3 tool calls to 1, saving ~60% tokens
339
+ */
340
+ export async function upgradeCheck(input) {
341
+ const repoName = input?.repo || "compact";
342
+ const currentVersion = input.currentVersion;
343
+ logger.debug("Running compound upgrade check", {
344
+ repo: repoName,
345
+ currentVersion,
346
+ });
347
+ const resolved = resolveRepo(repoName);
348
+ if (!resolved) {
349
+ throw new Error(`Unknown repository: ${repoName}. Available: ${Object.keys(REPO_ALIASES).join(", ")}`);
350
+ }
351
+ // Fetch all data in parallel
352
+ const [versionInfo, outdatedInfo, breakingChanges] = await Promise.all([
353
+ releaseTracker.getVersionInfo(resolved.owner, resolved.repo),
354
+ releaseTracker.isOutdated(resolved.owner, resolved.repo, currentVersion),
355
+ releaseTracker.getBreakingChangesSince(resolved.owner, resolved.repo, currentVersion),
356
+ ]);
357
+ const latestVersion = versionInfo.latestStableRelease?.tag || versionInfo.latestRelease?.tag;
358
+ // Only fetch migration guide if there are breaking changes
359
+ let migrationGuide = null;
360
+ if (breakingChanges.length > 0 && latestVersion) {
361
+ migrationGuide = await releaseTracker.getMigrationGuide(resolved.owner, resolved.repo, currentVersion, latestVersion);
362
+ }
363
+ // Compute upgrade urgency
364
+ const urgency = computeUpgradeUrgency(outdatedInfo, breakingChanges.length);
365
+ return {
366
+ repository: `${resolved.owner}/${resolved.repo}`,
367
+ currentVersion,
368
+ // Version summary
369
+ version: {
370
+ latest: latestVersion || "No releases",
371
+ latestStable: versionInfo.latestStableRelease?.tag || "No stable releases",
372
+ publishedAt: versionInfo.latestRelease?.publishedAt || null,
373
+ isOutdated: outdatedInfo.isOutdated,
374
+ versionsBehind: outdatedInfo.versionsBehind,
375
+ },
376
+ // Breaking changes summary
377
+ breakingChanges: {
378
+ count: breakingChanges.length,
379
+ hasBreakingChanges: breakingChanges.length > 0,
380
+ items: breakingChanges.slice(0, 10), // Limit to avoid token bloat
381
+ },
382
+ // Migration guide (only if needed)
383
+ migration: migrationGuide
384
+ ? {
385
+ steps: migrationGuide.migrationSteps,
386
+ deprecations: migrationGuide.deprecations,
387
+ newFeatures: migrationGuide.newFeatures.slice(0, 5),
388
+ }
389
+ : null,
390
+ // Actionable recommendation
391
+ urgency,
392
+ recommendation: generateUpgradeRecommendation(urgency, breakingChanges.length, outdatedInfo),
393
+ };
394
+ }
395
+ /**
396
+ * Compound tool: Full repository context
397
+ * Combines: getVersionInfo + getLatestSyntax + listExamples (filtered)
398
+ * Provides everything needed to start working with a repo
399
+ */
400
+ export async function getFullRepoContext(input) {
401
+ const repoName = input?.repo || "compact";
402
+ logger.debug("Getting full repo context", { repo: repoName });
403
+ const resolved = resolveRepo(repoName);
404
+ if (!resolved) {
405
+ throw new Error(`Unknown repository: ${repoName}. Available: ${Object.keys(REPO_ALIASES).join(", ")}`);
406
+ }
407
+ // Fetch version info
408
+ const versionInfo = await releaseTracker.getVersionInfo(resolved.owner, resolved.repo);
409
+ const version = versionInfo.latestStableRelease?.tag ||
410
+ versionInfo.latestRelease?.tag ||
411
+ "main";
412
+ // Conditionally fetch syntax reference
413
+ let syntaxRef = null;
414
+ if (input.includeSyntax !== false) {
415
+ syntaxRef = await releaseTracker.getLatestSyntaxReference(resolved.owner, resolved.repo);
416
+ }
417
+ // Get relevant examples for this repo
418
+ let examples = [];
419
+ if (input.includeExamples !== false) {
420
+ // Filter examples relevant to this repo type
421
+ const repoType = getRepoType(repoName);
422
+ examples = EXAMPLES.filter((ex) => repoType === "all" || ex.category === repoType || repoType === "compact")
423
+ .slice(0, 5)
424
+ .map((ex) => ({
425
+ name: ex.name,
426
+ description: ex.description,
427
+ complexity: ex.complexity,
428
+ }));
429
+ }
430
+ return {
431
+ repository: `${resolved.owner}/${resolved.repo}`,
432
+ // Quick start info
433
+ quickStart: {
434
+ version,
435
+ installCommand: getInstallCommand(repoName, version),
436
+ docsUrl: `https://github.com/${resolved.owner}/${resolved.repo}`,
437
+ },
438
+ // Version context
439
+ version: {
440
+ current: version,
441
+ stable: versionInfo.latestStableRelease?.tag || null,
442
+ publishedAt: versionInfo.latestRelease?.publishedAt || null,
443
+ recentReleases: versionInfo.recentReleases.slice(0, 3).map((r) => ({
444
+ tag: r.tag,
445
+ date: r.publishedAt.split("T")[0],
446
+ })),
447
+ },
448
+ // Syntax reference (condensed)
449
+ syntax: syntaxRef
450
+ ? {
451
+ version: syntaxRef.version,
452
+ files: syntaxRef.syntaxFiles.map((f) => f.path),
453
+ // Include first file content as primary reference
454
+ primaryReference: syntaxRef.syntaxFiles[0]?.content?.slice(0, 2000) || null,
455
+ }
456
+ : null,
457
+ // Relevant examples
458
+ examples,
459
+ note: `Use this context to write ${repoName} code at version ${version}. For detailed syntax, use midnight-get-latest-syntax.`,
460
+ };
461
+ }
462
+ // Helper functions for compound tools
463
+ function computeUpgradeUrgency(outdatedInfo, breakingCount) {
464
+ if (!outdatedInfo.isOutdated)
465
+ return "none";
466
+ if (breakingCount === 0 && outdatedInfo.versionsBehind <= 2)
467
+ return "low";
468
+ if (breakingCount <= 2 && outdatedInfo.versionsBehind <= 5)
469
+ return "medium";
470
+ if (breakingCount <= 5)
471
+ return "high";
472
+ return "critical";
473
+ }
474
+ function generateUpgradeRecommendation(urgency, breakingCount, outdatedInfo) {
475
+ switch (urgency) {
476
+ case "none":
477
+ return "✅ You're on the latest version. No action needed.";
478
+ case "low":
479
+ return `📦 Minor update available (${outdatedInfo.versionsBehind} versions behind). Safe to upgrade at your convenience.`;
480
+ case "medium":
481
+ return `⚠️ Update recommended. ${breakingCount} breaking change(s) to review. Plan upgrade within 2 weeks.`;
482
+ case "high":
483
+ return `🔶 Important update. ${breakingCount} breaking changes require attention. Schedule upgrade soon.`;
484
+ case "critical":
485
+ return `🚨 Critical update needed! ${breakingCount} breaking changes and ${outdatedInfo.versionsBehind} versions behind. Upgrade immediately.`;
486
+ default:
487
+ return "Check the breaking changes and plan your upgrade.";
488
+ }
489
+ }
490
+ function getRepoType(repoName) {
491
+ const name = repoName.toLowerCase();
492
+ if (name.includes("counter"))
493
+ return "counter";
494
+ if (name.includes("bboard"))
495
+ return "bboard";
496
+ if (name.includes("token") || name.includes("dex"))
497
+ return "token";
498
+ if (name.includes("voting"))
499
+ return "voting";
500
+ return "all";
501
+ }
502
+ function getInstallCommand(repoName, version) {
503
+ const name = repoName.toLowerCase();
504
+ if (name === "compact" || name.includes("compact")) {
505
+ return `npx @aspect-sh/pnpm dlx @midnight-ntwrk/create-midnight-app@${version}`;
506
+ }
507
+ if (name === "midnight-js" || name.includes("js")) {
508
+ return `npm install @midnight-ntwrk/midnight-js@${version}`;
509
+ }
510
+ return `git clone https://github.com/midnight-ntwrk/${repoName}.git && cd ${repoName} && git checkout ${version}`;
511
+ }
338
512
  //# sourceMappingURL=handlers.js.map
@@ -99,6 +99,29 @@ export declare const GetLatestSyntaxInputSchema: z.ZodObject<{
99
99
  }, {
100
100
  repo?: string | undefined;
101
101
  }>;
102
+ export declare const UpgradeCheckInputSchema: z.ZodObject<{
103
+ repo: z.ZodDefault<z.ZodString>;
104
+ currentVersion: z.ZodString;
105
+ }, "strip", z.ZodTypeAny, {
106
+ repo: string;
107
+ currentVersion: string;
108
+ }, {
109
+ currentVersion: string;
110
+ repo?: string | undefined;
111
+ }>;
112
+ export declare const FullRepoContextInputSchema: z.ZodObject<{
113
+ repo: z.ZodString;
114
+ includeExamples: z.ZodDefault<z.ZodBoolean>;
115
+ includeSyntax: z.ZodDefault<z.ZodBoolean>;
116
+ }, "strip", z.ZodTypeAny, {
117
+ repo: string;
118
+ includeExamples: boolean;
119
+ includeSyntax: boolean;
120
+ }, {
121
+ repo: string;
122
+ includeExamples?: boolean | undefined;
123
+ includeSyntax?: boolean | undefined;
124
+ }>;
102
125
  export type GetFileInput = z.infer<typeof GetFileInputSchema>;
103
126
  export type ListExamplesInput = z.infer<typeof ListExamplesInputSchema>;
104
127
  export type GetLatestUpdatesInput = z.infer<typeof GetLatestUpdatesInputSchema>;
@@ -108,4 +131,6 @@ export type GetMigrationGuideInput = z.infer<typeof GetMigrationGuideInputSchema
108
131
  export type GetFileAtVersionInput = z.infer<typeof GetFileAtVersionInputSchema>;
109
132
  export type CompareSyntaxInput = z.infer<typeof CompareSyntaxInputSchema>;
110
133
  export type GetLatestSyntaxInput = z.infer<typeof GetLatestSyntaxInputSchema>;
134
+ export type UpgradeCheckInput = z.infer<typeof UpgradeCheckInputSchema>;
135
+ export type FullRepoContextInput = z.infer<typeof FullRepoContextInputSchema>;
111
136
  //# sourceMappingURL=schemas.d.ts.map
@@ -70,4 +70,22 @@ export const GetLatestSyntaxInputSchema = z.object({
70
70
  .default("compact")
71
71
  .describe("Repository name (default: 'compact')"),
72
72
  });
73
+ // Compound tool schemas - reduce multiple API calls to one
74
+ export const UpgradeCheckInputSchema = z.object({
75
+ repo: z
76
+ .string()
77
+ .default("compact")
78
+ .describe("Repository name (default: 'compact')"),
79
+ currentVersion: z
80
+ .string()
81
+ .describe("Your current version (e.g., 'v0.14.0', '0.13.5')"),
82
+ });
83
+ export const FullRepoContextInputSchema = z.object({
84
+ repo: z.string().describe("Repository name (e.g., 'compact', 'midnight-js')"),
85
+ includeExamples: z
86
+ .boolean()
87
+ .default(true)
88
+ .describe("Include example code snippets"),
89
+ includeSyntax: z.boolean().default(true).describe("Include syntax reference"),
90
+ });
73
91
  //# sourceMappingURL=schemas.js.map
@@ -2,7 +2,7 @@
2
2
  * Repository tool definitions
3
3
  * MCP tool registration for repository-related operations
4
4
  */
5
- import { getFile, listExamples, getLatestUpdates, getVersionInfo, checkBreakingChanges, getMigrationGuide, getFileAtVersion, compareSyntax, getLatestSyntax, } from "./handlers.js";
5
+ import { getFile, listExamples, getLatestUpdates, getVersionInfo, checkBreakingChanges, getMigrationGuide, getFileAtVersion, compareSyntax, getLatestSyntax, upgradeCheck, getFullRepoContext, } from "./handlers.js";
6
6
  // Tool definitions for MCP
7
7
  export const repositoryTools = [
8
8
  {
@@ -30,6 +30,7 @@ export const repositoryTools = [
30
30
  readOnlyHint: true,
31
31
  openWorldHint: true,
32
32
  title: "Get Repository File",
33
+ category: "repository",
33
34
  },
34
35
  handler: getFile,
35
36
  },
@@ -51,6 +52,7 @@ export const repositoryTools = [
51
52
  readOnlyHint: true,
52
53
  idempotentHint: true,
53
54
  title: "List Example Contracts",
55
+ category: "repository",
54
56
  },
55
57
  handler: listExamples,
56
58
  },
@@ -76,6 +78,7 @@ export const repositoryTools = [
76
78
  readOnlyHint: true,
77
79
  openWorldHint: true,
78
80
  title: "Get Latest Updates",
81
+ category: "repository",
79
82
  },
80
83
  handler: getLatestUpdates,
81
84
  },
@@ -96,6 +99,7 @@ export const repositoryTools = [
96
99
  readOnlyHint: true,
97
100
  openWorldHint: true,
98
101
  title: "Get Version Info",
102
+ category: "versioning",
99
103
  },
100
104
  handler: getVersionInfo,
101
105
  },
@@ -120,6 +124,7 @@ export const repositoryTools = [
120
124
  readOnlyHint: true,
121
125
  openWorldHint: true,
122
126
  title: "Check Breaking Changes",
127
+ category: "versioning",
123
128
  },
124
129
  handler: checkBreakingChanges,
125
130
  },
@@ -148,6 +153,7 @@ export const repositoryTools = [
148
153
  readOnlyHint: true,
149
154
  openWorldHint: true,
150
155
  title: "Get Migration Guide",
156
+ category: "versioning",
151
157
  },
152
158
  handler: getMigrationGuide,
153
159
  },
@@ -177,6 +183,7 @@ export const repositoryTools = [
177
183
  idempotentHint: true,
178
184
  openWorldHint: true,
179
185
  title: "Get File at Version",
186
+ category: "versioning",
180
187
  },
181
188
  handler: getFileAtVersion,
182
189
  },
@@ -210,6 +217,7 @@ export const repositoryTools = [
210
217
  idempotentHint: true,
211
218
  openWorldHint: true,
212
219
  title: "Compare Syntax Between Versions",
220
+ category: "versioning",
213
221
  },
214
222
  handler: compareSyntax,
215
223
  },
@@ -230,8 +238,120 @@ export const repositoryTools = [
230
238
  readOnlyHint: true,
231
239
  openWorldHint: true,
232
240
  title: "Get Latest Syntax Reference",
241
+ category: "versioning",
233
242
  },
234
243
  handler: getLatestSyntax,
235
244
  },
245
+ // ============================================================================
246
+ // COMPOUND TOOLS - Multi-step operations in a single call
247
+ // These reduce token usage by 50-70% compared to calling individual tools
248
+ // ============================================================================
249
+ {
250
+ name: "midnight-upgrade-check",
251
+ description: "🚀 COMPOUND TOOL: Complete upgrade analysis in ONE call. Combines version check + breaking changes + migration guide. Use this instead of calling midnight-get-version-info, midnight-check-breaking-changes, and midnight-get-migration-guide separately. Saves ~60% tokens.",
252
+ inputSchema: {
253
+ type: "object",
254
+ properties: {
255
+ repo: {
256
+ type: "string",
257
+ description: "Repository name (default: 'compact')",
258
+ },
259
+ currentVersion: {
260
+ type: "string",
261
+ description: "Your current version (e.g., 'v0.14.0', '0.13.5')",
262
+ },
263
+ },
264
+ required: ["currentVersion"],
265
+ },
266
+ outputSchema: {
267
+ type: "object",
268
+ properties: {
269
+ repository: { type: "string", description: "Full repository path" },
270
+ currentVersion: {
271
+ type: "string",
272
+ description: "Version being checked",
273
+ },
274
+ version: {
275
+ type: "object",
276
+ description: "Version summary",
277
+ properties: {
278
+ latest: { type: "string" },
279
+ latestStable: { type: "string" },
280
+ isOutdated: { type: "boolean" },
281
+ versionsBehind: { type: "number" },
282
+ },
283
+ },
284
+ breakingChanges: {
285
+ type: "object",
286
+ description: "Breaking changes summary",
287
+ properties: {
288
+ count: { type: "number" },
289
+ hasBreakingChanges: { type: "boolean" },
290
+ items: { type: "array" },
291
+ },
292
+ },
293
+ migration: { type: "object", description: "Migration guide if needed" },
294
+ urgency: {
295
+ type: "string",
296
+ description: "none|low|medium|high|critical",
297
+ },
298
+ recommendation: {
299
+ type: "string",
300
+ description: "Actionable recommendation",
301
+ },
302
+ },
303
+ },
304
+ annotations: {
305
+ readOnlyHint: true,
306
+ openWorldHint: true,
307
+ longRunningHint: true,
308
+ title: "⚡ Upgrade Check (Compound)",
309
+ category: "compound",
310
+ },
311
+ handler: upgradeCheck,
312
+ },
313
+ {
314
+ name: "midnight-get-repo-context",
315
+ description: "🚀 COMPOUND TOOL: Get everything needed to start working with a repository in ONE call. Combines version info + syntax reference + relevant examples. Use this at the start of a coding session instead of multiple individual calls. Saves ~50% tokens.",
316
+ inputSchema: {
317
+ type: "object",
318
+ properties: {
319
+ repo: {
320
+ type: "string",
321
+ description: "Repository name (e.g., 'compact', 'midnight-js')",
322
+ },
323
+ includeExamples: {
324
+ type: "boolean",
325
+ description: "Include example code snippets (default: true)",
326
+ },
327
+ includeSyntax: {
328
+ type: "boolean",
329
+ description: "Include syntax reference (default: true)",
330
+ },
331
+ },
332
+ required: ["repo"],
333
+ },
334
+ outputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ repository: { type: "string", description: "Full repository path" },
338
+ quickStart: {
339
+ type: "object",
340
+ description: "Version and install command",
341
+ },
342
+ version: { type: "object", description: "Version details" },
343
+ syntax: { type: "object", description: "Syntax reference summary" },
344
+ examples: { type: "array", description: "Relevant examples" },
345
+ },
346
+ },
347
+ annotations: {
348
+ readOnlyHint: true,
349
+ openWorldHint: true,
350
+ longRunningHint: true,
351
+ title: "⚡ Get Repo Context (Compound)",
352
+ category: "compound",
353
+ },
354
+ handler: getFullRepoContext,
355
+ },
236
356
  ];
237
357
  //# sourceMappingURL=tools.js.map
@@ -57,6 +57,7 @@ const searchToolAnnotations = {
57
57
  readOnlyHint: true,
58
58
  idempotentHint: true,
59
59
  openWorldHint: true,
60
+ category: "search",
60
61
  };
61
62
  /**
62
63
  * Validate and prepare common search parameters
@@ -31,7 +31,27 @@ export interface ToolAnnotations {
31
31
  * Human-readable title for display in UIs
32
32
  */
33
33
  title?: string;
34
+ /**
35
+ * If true, this tool performs destructive or irreversible actions
36
+ * Clients should require human confirmation before execution
37
+ */
38
+ destructiveHint?: boolean;
39
+ /**
40
+ * If true, this tool requires explicit human confirmation
41
+ * before execution (e.g., financial transactions, deletions)
42
+ */
43
+ requiresConfirmation?: boolean;
44
+ /**
45
+ * Tool category for progressive disclosure
46
+ * Allows clients to group/filter tools by domain
47
+ */
48
+ category?: ToolCategory;
34
49
  }
50
+ /**
51
+ * Tool categories for progressive disclosure
52
+ * Clients can initially show categories, then expand to individual tools
53
+ */
54
+ export type ToolCategory = "search" | "analyze" | "repository" | "versioning" | "generation" | "health" | "compound";
35
55
  /**
36
56
  * JSON Schema for structured tool outputs
37
57
  * Enables better LLM parsing and IDE integration
@@ -224,5 +224,20 @@ export const DEFAULT_REPOSITORIES = [
224
224
  patterns: ["**/*.compact", "**/*.ts", "**/*.md"],
225
225
  exclude: ["node_modules/**", "dist/**"],
226
226
  },
227
+ // Sea Battle Hackathon Winners (Feb 2025)
228
+ {
229
+ owner: "ErickRomeroDev",
230
+ repo: "naval-battle-game_v2",
231
+ branch: "main",
232
+ patterns: ["**/*.compact", "**/*.ts", "**/*.tsx", "**/*.md"],
233
+ exclude: ["node_modules/**", "dist/**"],
234
+ },
235
+ {
236
+ owner: "eddex",
237
+ repo: "midnight-sea-battle-hackathon",
238
+ branch: "main",
239
+ patterns: ["**/*.compact", "**/*.ts", "**/*.md"],
240
+ exclude: ["node_modules/**", "dist/**"],
241
+ },
227
242
  ];
228
243
  //# sourceMappingURL=config.js.map
@@ -25,6 +25,72 @@ export declare const ErrorCodes: {
25
25
  readonly PARSE_ERROR: "PARSE_ERROR";
26
26
  readonly CHROMADB_UNAVAILABLE: "CHROMADB_UNAVAILABLE";
27
27
  readonly OPENAI_UNAVAILABLE: "OPENAI_UNAVAILABLE";
28
+ readonly MISSING_PARAM: "MISSING_PARAMETER";
29
+ readonly INVALID_VERSION: "INVALID_VERSION";
30
+ readonly SAMPLING_UNAVAILABLE: "SAMPLING_UNAVAILABLE";
31
+ };
32
+ /**
33
+ * LLM-friendly error hints that help the model self-correct
34
+ * These are designed to give the AI enough context to retry with corrected input
35
+ */
36
+ export declare const SelfCorrectionHints: {
37
+ UNKNOWN_REPO: (repo: string, validRepos: string[]) => {
38
+ error: string;
39
+ code: "UNKNOWN_REPOSITORY";
40
+ suggestion: string;
41
+ correction: {
42
+ invalidValue: string;
43
+ validValues: string[];
44
+ parameterName: string;
45
+ };
46
+ };
47
+ INVALID_VERSION: (version: string, example: string) => {
48
+ error: string;
49
+ code: "INVALID_VERSION";
50
+ suggestion: string;
51
+ correction: {
52
+ invalidValue: string;
53
+ expectedFormat: string;
54
+ example: string;
55
+ };
56
+ };
57
+ MISSING_REQUIRED_PARAM: (paramName: string, toolName: string) => {
58
+ error: string;
59
+ code: "MISSING_PARAMETER";
60
+ suggestion: string;
61
+ correction: {
62
+ missingParameter: string;
63
+ tool: string;
64
+ };
65
+ };
66
+ FILE_NOT_FOUND: (path: string, repo: string, similarPaths?: string[]) => {
67
+ error: string;
68
+ code: "RESOURCE_NOT_FOUND";
69
+ suggestion: string;
70
+ correction: {
71
+ suggestions?: string[] | undefined;
72
+ invalidPath: string;
73
+ };
74
+ };
75
+ SAMPLING_NOT_AVAILABLE: (toolName: string) => {
76
+ error: string;
77
+ code: "SAMPLING_UNAVAILABLE";
78
+ suggestion: string;
79
+ alternatives: {
80
+ "midnight-generate-contract": string;
81
+ "midnight-review-contract": string;
82
+ "midnight-document-contract": string;
83
+ };
84
+ };
85
+ RATE_LIMIT: (retryAfter?: number) => {
86
+ error: string;
87
+ code: "RATE_LIMIT_EXCEEDED";
88
+ suggestion: string;
89
+ correction: {
90
+ retryAfterSeconds?: number | undefined;
91
+ action: string;
92
+ };
93
+ };
28
94
  };
29
95
  /**
30
96
  * Create user-friendly error from various error types
@@ -33,6 +33,76 @@ export const ErrorCodes = {
33
33
  PARSE_ERROR: "PARSE_ERROR",
34
34
  CHROMADB_UNAVAILABLE: "CHROMADB_UNAVAILABLE",
35
35
  OPENAI_UNAVAILABLE: "OPENAI_UNAVAILABLE",
36
+ MISSING_PARAM: "MISSING_PARAMETER",
37
+ INVALID_VERSION: "INVALID_VERSION",
38
+ SAMPLING_UNAVAILABLE: "SAMPLING_UNAVAILABLE",
39
+ };
40
+ /**
41
+ * LLM-friendly error hints that help the model self-correct
42
+ * These are designed to give the AI enough context to retry with corrected input
43
+ */
44
+ export const SelfCorrectionHints = {
45
+ UNKNOWN_REPO: (repo, validRepos) => ({
46
+ error: `Unknown repository: '${repo}'`,
47
+ code: ErrorCodes.UNKNOWN_REPO,
48
+ suggestion: `Try one of these instead: ${validRepos.slice(0, 8).join(", ")}`,
49
+ correction: {
50
+ invalidValue: repo,
51
+ validValues: validRepos,
52
+ parameterName: "repo",
53
+ },
54
+ }),
55
+ INVALID_VERSION: (version, example) => ({
56
+ error: `Invalid version format: '${version}'`,
57
+ code: ErrorCodes.INVALID_VERSION,
58
+ suggestion: `Version should be like '${example}'. Check available versions with midnight-get-version-info first.`,
59
+ correction: {
60
+ invalidValue: version,
61
+ expectedFormat: "v1.0.0 or 0.14.0",
62
+ example,
63
+ },
64
+ }),
65
+ MISSING_REQUIRED_PARAM: (paramName, toolName) => ({
66
+ error: `Missing required parameter: '${paramName}'`,
67
+ code: ErrorCodes.MISSING_PARAM,
68
+ suggestion: `The '${paramName}' parameter is required for ${toolName}. Please provide it.`,
69
+ correction: {
70
+ missingParameter: paramName,
71
+ tool: toolName,
72
+ },
73
+ }),
74
+ FILE_NOT_FOUND: (path, repo, similarPaths) => ({
75
+ error: `File not found: '${path}' in ${repo}`,
76
+ code: ErrorCodes.NOT_FOUND,
77
+ suggestion: similarPaths?.length
78
+ ? `Did you mean: ${similarPaths.join(", ")}?`
79
+ : `Check the file path. Use midnight-get-file with a different path or list directory contents first.`,
80
+ correction: {
81
+ invalidPath: path,
82
+ ...(similarPaths && { suggestions: similarPaths }),
83
+ },
84
+ }),
85
+ SAMPLING_NOT_AVAILABLE: (toolName) => ({
86
+ error: `Sampling capability not available`,
87
+ code: ErrorCodes.SAMPLING_UNAVAILABLE,
88
+ suggestion: `${toolName} requires a client that supports sampling (e.g., Claude Desktop). Use a non-AI alternative or switch clients.`,
89
+ alternatives: {
90
+ "midnight-generate-contract": "Use midnight-search-compact to find similar contracts as templates",
91
+ "midnight-review-contract": "Use midnight-analyze-contract for static analysis",
92
+ "midnight-document-contract": "Manual documentation or inline comments",
93
+ },
94
+ }),
95
+ RATE_LIMIT: (retryAfter) => ({
96
+ error: "GitHub API rate limit exceeded",
97
+ code: ErrorCodes.RATE_LIMIT,
98
+ suggestion: retryAfter
99
+ ? `Wait ${retryAfter} seconds before retrying. Or add GITHUB_TOKEN for higher limits.`
100
+ : "Add GITHUB_TOKEN to increase from 60 to 5000 requests/hour.",
101
+ correction: {
102
+ action: "wait_and_retry",
103
+ ...(retryAfter && { retryAfterSeconds: retryAfter }),
104
+ },
105
+ }),
36
106
  };
37
107
  /**
38
108
  * Create user-friendly error from various error types