@debugai/mcp 1.0.0 → 2.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/README.md CHANGED
@@ -14,6 +14,19 @@ This package is the same server, standalone. No VS Code required.
14
14
  2. Copy your API key (`dbg_...`) from [debugai.io/dashboard](https://debugai.io/dashboard).
15
15
  3. Add the server to your MCP client (snippets below). Node 18+ required.
16
16
 
17
+ ### Set the key once for every client (optional)
18
+
19
+ Instead of repeating the key in each client's `env` block, write it to
20
+ `~/.debugai/config.json`:
21
+
22
+ ```json
23
+ { "api_key": "dbg_your_key_here" }
24
+ ```
25
+
26
+ Every MCP client launching `npx -y @debugai/mcp` picks it up — you can then
27
+ drop the `env` block from the snippets below entirely. An explicit
28
+ `DEBUGAI_API_KEY` env var still wins over the file.
29
+
17
30
  ### Claude Code
18
31
 
19
32
  ```bash
@@ -93,7 +106,7 @@ You don't need this package. The
93
106
  registers the MCP server automatically (VS Code 1.101+) and adds one-click
94
107
  fix apply, proactive scan, and codebase indexing on top.
95
108
 
96
- ## The tool
109
+ ## The tools
97
110
 
98
111
  ### `debug_error`
99
112
 
@@ -106,10 +119,35 @@ Give it an error, get an analysis.
106
119
  | `codeSnippet` | no | Code around the failing line, if the agent has it. |
107
120
  | `filePath` | no | Path to the file that threw. |
108
121
 
109
- Returns the root cause, up to 3 fixes ranked by confidence (with code
110
- patches), the detected framework, and whether the answer came from cache.
111
- Read-only: it never touches your files. Applying a fix is your agent's
112
- (and your) call.
122
+ Returns the root cause, up to 3 fixes ranked by confidence, the detected
123
+ framework, and whether the answer came from cache. Since 2.0 each fix also
124
+ carries, where derivable: `edits` (exact old/new strings your agent's edit
125
+ tool can apply directly), `unified_diff`, and `verify_with` (a syntax-level
126
+ check command to run after applying). Read-only: it never touches your
127
+ files. Applying a fix is your agent's (and your) call.
128
+
129
+ Every fix is labeled with its verification state, and there are three of
130
+ them, not two: **verified** (a mechanical check passed — currently
131
+ parse/import classes), **failed check** (confidence capped hard), or **not
132
+ verified** (the confidence number is the model's own estimate — nothing
133
+ checked it). We label the third case instead of hiding it.
134
+
135
+ ### `report_outcome`
136
+
137
+ Tell DebugAI whether an applied fix actually worked.
138
+
139
+ | Input | Required | Description |
140
+ |-------|----------|-------------|
141
+ | `debugLogId` | yes | The `debug_log_id` from the `debug_error` response. |
142
+ | `result` | yes | `worked` or `failed`. |
143
+ | `fixRank` | no | Which ranked fix was applied (1-3). |
144
+ | `newError` | no | If it failed: the error you saw after applying. |
145
+
146
+ Confirmed rank-1 fixes are remembered per project (the next hit on the same
147
+ error starts from the confirmed fix); failed-fix follow-ups are the
148
+ feedback that improves future answers. Agents are asked to call this once
149
+ per applied fix — same pipeline human feedback flows through in the VS Code
150
+ extension.
113
151
 
114
152
  Example, in Claude Code:
115
153
 
@@ -123,9 +161,10 @@ Example, in Claude Code:
123
161
 
124
162
  | Variable | Default | Description |
125
163
  |----------|---------|-------------|
126
- | `DEBUGAI_API_KEY` | (none) | Your API key. Required for real analyses. |
127
- | `DEBUGAI_API_BASE` | DebugAI production | Override for self-hosted or staging setups. |
164
+ | `DEBUGAI_API_KEY` | (none) | Your API key. Falls back to `api_key` in the config file. |
165
+ | `DEBUGAI_API_BASE` | DebugAI production | Override for self-hosted or staging setups. Falls back to `api_base` in the config file. |
128
166
  | `DEBUGAI_TIMEOUT_MS` | `150000` | Per-request deadline. Deep analyses can take 30-90s. |
167
+ | `DEBUGAI_CONFIG_PATH` | `~/.debugai/config.json` | Alternate config file location. Rarely needed. |
129
168
 
130
169
  ## Limits and honesty
131
170
 
@@ -140,7 +179,8 @@ Example, in Claude Code:
140
179
  ## Troubleshooting
141
180
 
142
181
  - **"authentication failed"**: key missing or wrong. Check the `env` block in
143
- your client config, restart the client. Keys start with `dbg_`.
182
+ your client config or `~/.debugai/config.json`, restart the client. Keys
183
+ start with `dbg_`.
144
184
  - **Nothing happens on `npx @debugai/mcp`**: correct. It's a stdio server that
145
185
  waits for an MCP client to speak first. Run `npx @debugai/mcp --help` to
146
186
  verify the install.
package/dist/backend.d.ts CHANGED
@@ -6,6 +6,11 @@ export interface DebugRequest {
6
6
  project_id?: string;
7
7
  framework_hint?: string;
8
8
  }
9
+ export interface DebugEdit {
10
+ file: string;
11
+ old_string: string;
12
+ new_string: string;
13
+ }
9
14
  export interface DebugFix {
10
15
  rank: number;
11
16
  title: string;
@@ -13,6 +18,11 @@ export interface DebugFix {
13
18
  confidence: number;
14
19
  code?: string;
15
20
  line_hint?: string;
21
+ verified?: boolean | null;
22
+ verification_reason?: string;
23
+ edits?: DebugEdit[];
24
+ unified_diff?: string;
25
+ verify_with?: string;
16
26
  }
17
27
  export interface DebugResponse {
18
28
  root_cause: string;
@@ -26,6 +36,22 @@ export interface DebugResponse {
26
36
  pattern_matched?: string;
27
37
  remaining_today?: number;
28
38
  mock?: boolean;
39
+ schema_version?: string;
40
+ debug_log_id?: string | null;
41
+ error_signature?: string;
42
+ session_id?: string;
43
+ memory_hit?: boolean;
44
+ memory_fix_confirmed?: boolean;
45
+ }
46
+ export interface OutcomeRequest {
47
+ debug_log_id: string;
48
+ result: 'worked' | 'failed';
49
+ fix_rank?: number;
50
+ new_error?: string;
51
+ source: 'agent';
52
+ }
53
+ export interface OutcomeResponse {
54
+ success: boolean;
29
55
  }
30
56
  export interface BackendConfig {
31
57
  apiKey: string;
@@ -39,4 +65,6 @@ export interface BackendError extends Error {
39
65
  retryAfterSeconds: number;
40
66
  }
41
67
  export declare const DEFAULT_TIMEOUT_MS = 150000;
68
+ export declare const OUTCOME_TIMEOUT_MS = 15000;
42
69
  export declare function callDebugBackend(req: DebugRequest, config: BackendConfig): Promise<DebugResponse>;
70
+ export declare function callOutcomeBackend(req: OutcomeRequest, config: BackendConfig): Promise<OutcomeResponse>;
package/dist/backend.js CHANGED
@@ -1,21 +1,23 @@
1
1
  export const DEFAULT_TIMEOUT_MS = 150_000;
2
+ // Feedback writes are a fast DB insert, not an LLM call — fail fast so a
3
+ // stuck outcome report never holds an agent hostage for minutes.
4
+ export const OUTCOME_TIMEOUT_MS = 15_000;
2
5
  function makeBackendError(message, status, retryAfterSeconds = 0) {
3
6
  return Object.assign(new Error(message), { status, retryAfterSeconds });
4
7
  }
5
- export async function callDebugBackend(req, config) {
6
- const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8
+ async function postJson(path, payload, config, timeoutMs) {
7
9
  const controller = new AbortController();
8
10
  const timer = setTimeout(() => controller.abort(), timeoutMs);
9
11
  let res;
10
12
  try {
11
- res = await fetch(`${config.apiBase}/debug`, {
13
+ res = await fetch(`${config.apiBase}${path}`, {
12
14
  method: 'POST',
13
15
  headers: {
14
16
  'content-type': 'application/json',
15
17
  'x-api-key': config.apiKey,
16
18
  'user-agent': `debugai-mcp/${config.version}`,
17
19
  },
18
- body: JSON.stringify(req),
20
+ body: JSON.stringify(payload),
19
21
  signal: controller.signal,
20
22
  });
21
23
  }
@@ -36,3 +38,9 @@ export async function callDebugBackend(req, config) {
36
38
  }
37
39
  return res.json();
38
40
  }
41
+ export async function callDebugBackend(req, config) {
42
+ return postJson('/debug', req, config, config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
43
+ }
44
+ export async function callOutcomeBackend(req, config) {
45
+ return postJson('/user/debug-feedback', req, config, OUTCOME_TIMEOUT_MS);
46
+ }
@@ -0,0 +1,13 @@
1
+ export interface FileConfig {
2
+ apiKey?: string;
3
+ apiBase?: string;
4
+ }
5
+ export interface ResolvedSettings {
6
+ apiKey: string;
7
+ apiBase: string;
8
+ /** Where the key came from — used only for the startup log line. */
9
+ keySource: 'env' | 'file' | 'none';
10
+ }
11
+ export declare function configPath(env?: NodeJS.ProcessEnv): string;
12
+ export declare function loadFileConfig(env?: NodeJS.ProcessEnv, warn?: (msg: string) => void): FileConfig;
13
+ export declare function resolveSettings(defaultApiBase: string, env?: NodeJS.ProcessEnv, warn?: (msg: string) => void): ResolvedSettings;
package/dist/config.js ADDED
@@ -0,0 +1,48 @@
1
+ // Optional on-disk config so a user can set their key once instead of
2
+ // repeating it in every MCP client's env block:
3
+ //
4
+ // ~/.debugai/config.json { "api_key": "dbg_...", "api_base": "..." }
5
+ //
6
+ // Environment variables always win over the file. DEBUGAI_CONFIG_PATH
7
+ // overrides the file location (tests point it at a temp dir; users normally
8
+ // never set it).
9
+ import { readFileSync } from 'node:fs';
10
+ import { homedir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ export function configPath(env = process.env) {
13
+ const override = (env.DEBUGAI_CONFIG_PATH ?? '').trim();
14
+ return override || join(homedir(), '.debugai', 'config.json');
15
+ }
16
+ export function loadFileConfig(env = process.env, warn = (msg) => console.error(msg)) {
17
+ const path = configPath(env);
18
+ let raw;
19
+ try {
20
+ raw = readFileSync(path, 'utf8');
21
+ }
22
+ catch {
23
+ return {}; // no config file is the normal case — stay silent
24
+ }
25
+ try {
26
+ const parsed = JSON.parse(raw);
27
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
28
+ warn(`[debugai-mcp] ignoring ${path}: expected a JSON object`);
29
+ return {};
30
+ }
31
+ const record = parsed;
32
+ const str = (v) => typeof v === 'string' && v.trim() !== '' ? v.trim() : undefined;
33
+ return { apiKey: str(record.api_key), apiBase: str(record.api_base) };
34
+ }
35
+ catch {
36
+ warn(`[debugai-mcp] ignoring ${path}: malformed JSON`);
37
+ return {};
38
+ }
39
+ }
40
+ export function resolveSettings(defaultApiBase, env = process.env, warn = (msg) => console.error(msg)) {
41
+ const file = loadFileConfig(env, warn);
42
+ const envKey = (env.DEBUGAI_API_KEY ?? '').trim();
43
+ const apiKey = envKey || file.apiKey || '';
44
+ const keySource = envKey ? 'env' : file.apiKey ? 'file' : 'none';
45
+ const envBase = (env.DEBUGAI_API_BASE ?? '').trim();
46
+ const apiBase = (envBase || file.apiBase || defaultApiBase).replace(/\/+$/, '');
47
+ return { apiKey, apiBase, keySource };
48
+ }
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { dirname, join } from 'node:path';
5
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
6
  import { createServer } from './server.js';
7
7
  import { DEFAULT_TIMEOUT_MS } from './backend.js';
8
+ import { configPath, resolveSettings } from './config.js';
8
9
  const DEFAULT_API_BASE = 'https://debugai-mvp-production.up.railway.app/api';
9
10
  function packageVersion() {
10
11
  try {
@@ -18,8 +19,11 @@ function packageVersion() {
18
19
  const VERSION = packageVersion();
19
20
  const HELP = `debugai-mcp v${VERSION} — DebugAI MCP server (stdio)
20
21
 
21
- Exposes the debug_error tool to any MCP client: hand it an error or stack
22
- trace, get root cause + ranked fixes from DebugAI.
22
+ Exposes two tools to any MCP client:
23
+ debug_error hand it an error or stack trace, get root cause + ranked
24
+ fixes with machine-applicable edits (v2 contract)
25
+ report_outcome tell DebugAI whether an applied fix worked — failed-fix
26
+ follow-ups improve future answers for your codebase
23
27
 
24
28
  Usage:
25
29
  npx @debugai/mcp # start the server (stdio transport)
@@ -27,10 +31,14 @@ Usage:
27
31
  npx @debugai/mcp --help
28
32
 
29
33
  Environment:
30
- DEBUGAI_API_KEY required — your API key (dbg_...) from https://debugai.io/dashboard
34
+ DEBUGAI_API_KEY your API key (dbg_...) from https://debugai.io/dashboard
31
35
  DEBUGAI_API_BASE optional — API base URL (default: DebugAI production)
32
36
  DEBUGAI_TIMEOUT_MS optional — per-request deadline in ms (default: ${DEFAULT_TIMEOUT_MS})
33
37
 
38
+ Config file (set the key once, every MCP client picks it up):
39
+ ~/.debugai/config.json {"api_key": "dbg_..."}
40
+ Env vars win over the file. api_base is also accepted.
41
+
34
42
  This is a stdio MCP server: it is meant to be launched BY an MCP client
35
43
  (Claude Desktop, Claude Code, Cursor, Zed, ...), not run interactively.
36
44
  Config snippets: https://www.npmjs.com/package/@debugai/mcp
@@ -51,13 +59,13 @@ function main() {
51
59
  process.exitCode = 1;
52
60
  return;
53
61
  }
54
- const apiKey = (process.env.DEBUGAI_API_KEY ?? '').trim();
55
- const apiBase = (process.env.DEBUGAI_API_BASE ?? DEFAULT_API_BASE).trim().replace(/\/+$/, '');
62
+ const { apiKey, apiBase, keySource } = resolveSettings(DEFAULT_API_BASE);
56
63
  const rawTimeout = Number(process.env.DEBUGAI_TIMEOUT_MS);
57
64
  const timeoutMs = Number.isFinite(rawTimeout) && rawTimeout > 0 ? rawTimeout : DEFAULT_TIMEOUT_MS;
58
65
  if (!apiKey) {
59
- console.error('[debugai-mcp] DEBUGAI_API_KEY not set — tools will return auth errors. ' +
60
- 'Get a key at https://debugai.io/dashboard and add it to the "env" block of your MCP client config.');
66
+ console.error('[debugai-mcp] no API key found — tools will return auth errors. ' +
67
+ 'Get a key at https://debugai.io/dashboard, then either set DEBUGAI_API_KEY in your ' +
68
+ `MCP client config or write it once to ${configPath()} as {"api_key": "dbg_..."}.`);
61
69
  }
62
70
  else if (!apiKey.startsWith('dbg_')) {
63
71
  console.error('[debugai-mcp] warning: DEBUGAI_API_KEY does not look like a DebugAI key (expected dbg_ prefix).');
@@ -70,7 +78,7 @@ function main() {
70
78
  process.on('SIGINT', () => shutdown('SIGINT'));
71
79
  process.on('SIGTERM', () => shutdown('SIGTERM'));
72
80
  const transport = new StdioServerTransport();
73
- server.connect(transport).then(() => console.error(`[debugai-mcp] v${VERSION} connected on stdio (api: ${apiBase})`), (err) => {
81
+ server.connect(transport).then(() => console.error(`[debugai-mcp] v${VERSION} connected on stdio (api: ${apiBase}, key: ${keySource})`), (err) => {
74
82
  console.error('[debugai-mcp] fatal:', err);
75
83
  process.exit(1);
76
84
  });
package/dist/server.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { registerDebugError } from './tools/debugError.js';
3
+ import { registerReportOutcome } from './tools/reportOutcome.js';
3
4
  export function createServer(config) {
4
5
  const server = new McpServer({ name: 'debugai', version: config.version }, { capabilities: { tools: { listChanged: true } } });
5
6
  registerDebugError(server, config);
7
+ registerReportOutcome(server, config);
6
8
  return server;
7
9
  }
@@ -1,4 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { BackendConfig, DebugFix } from '../backend.js';
3
+ export declare function formatVerification(fix: DebugFix): string;
3
4
  export declare function formatFix(fix: DebugFix, index: number): string;
4
5
  export declare function registerDebugError(server: McpServer, config: BackendConfig): void;
@@ -1,9 +1,22 @@
1
1
  import { z } from 'zod';
2
2
  import { callDebugBackend } from '../backend.js';
3
3
  import { mapBackendErrorToToolResult } from '../errors.js';
4
+ // Tri-state verification labeling (docs/plan-v2-contract-phase1.md §1).
5
+ // The null case is rendered ON PURPOSE: a confidence number nothing checked
6
+ // must never look the same as one that was mechanically verified.
7
+ export function formatVerification(fix) {
8
+ if (fix.verified === true) {
9
+ return `✓ Verified — ${fix.verification_reason ?? 'mechanical check passed'}`;
10
+ }
11
+ if (fix.verified === false) {
12
+ return `✗ Failed mechanical check (confidence capped) — ${fix.verification_reason ?? 'check failed'}`;
13
+ }
14
+ return '· Not verified — confidence is the model\'s own estimate; nothing checked this fix.';
15
+ }
4
16
  export function formatFix(fix, index) {
5
17
  const lines = [
6
18
  `\n**Fix ${index} (${fix.confidence}% confidence)** — ${fix.title}`,
19
+ formatVerification(fix),
7
20
  fix.description,
8
21
  ];
9
22
  if (fix.code) {
@@ -12,6 +25,15 @@ export function formatFix(fix, index) {
12
25
  if (fix.line_hint) {
13
26
  lines.push(`_Location: ${fix.line_hint}_`);
14
27
  }
28
+ if (fix.edits?.length) {
29
+ lines.push('_Machine-applicable edit available: `edits` on this fix in structuredContent carries the exact old/new strings (apply with your Edit/replace tool)._');
30
+ }
31
+ if (fix.unified_diff) {
32
+ lines.push('```diff', fix.unified_diff.trimEnd(), '```');
33
+ }
34
+ if (fix.verify_with) {
35
+ lines.push(`_Syntax-level check after applying: \`${fix.verify_with}\`_`);
36
+ }
15
37
  return lines.join('\n');
16
38
  }
17
39
  export function registerDebugError(server, config) {
@@ -22,7 +44,8 @@ export function registerDebugError(server, config) {
22
44
  '"debug this stack trace", "fix this exception", "analyze this traceback", or shows a ' +
23
45
  'Traceback / TypeError / ReferenceError / AttributeError. ' +
24
46
  'Works for Python, JavaScript, TypeScript, Go, Rust. ' +
25
- 'Returns root cause explanation plus up to 3 ranked fixes with code patches.',
47
+ 'Returns root cause explanation plus up to 3 ranked fixes with machine-applicable code edits. ' +
48
+ 'After applying a fix, report whether it worked via the report_outcome tool.',
26
49
  inputSchema: {
27
50
  errorText: z
28
51
  .string()
@@ -79,16 +102,23 @@ export function registerDebugError(server, config) {
79
102
  if (badges.length) {
80
103
  sections.push(`\n---\n_${badges.join(' · ')}_`);
81
104
  }
105
+ if (result.debug_log_id) {
106
+ sections.push(`\nAfter applying a fix, call report_outcome with debugLogId "${result.debug_log_id}", ` +
107
+ 'the fixRank you applied, and result "worked" or "failed" (include newError text if it failed).');
108
+ }
82
109
  const text = sections.join('\n');
83
110
  return {
84
111
  content: [{ type: 'text', text }],
85
112
  structuredContent: {
113
+ schema_version: result.schema_version ?? '1.0',
86
114
  root_cause: result.root_cause,
87
115
  fixes: result.fixes,
88
116
  framework_detected: result.framework_detected,
89
117
  model_used: result.model_used,
90
118
  cached: result.cached ?? false,
91
119
  has_project_context: result.has_project_context ?? false,
120
+ debug_log_id: result.debug_log_id ?? null,
121
+ error_signature: result.error_signature ?? null,
92
122
  },
93
123
  };
94
124
  }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { BackendConfig } from '../backend.js';
3
+ export declare function registerReportOutcome(server: McpServer, config: BackendConfig): void;
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { callOutcomeBackend } from '../backend.js';
3
+ import { mapBackendErrorToToolResult } from '../errors.js';
4
+ export function registerReportOutcome(server, config) {
5
+ server.registerTool('report_outcome', {
6
+ title: 'Report Fix Outcome',
7
+ description: 'Report whether a DebugAI fix actually worked after you applied it. ' +
8
+ 'Call this ONCE after applying (or abandoning) a fix from debug_error, passing the ' +
9
+ 'debug_log_id from that response. If the fix failed, include the new error text — ' +
10
+ 'failed-fix follow-ups directly improve future answers for this codebase, and ' +
11
+ 'confirmed rank-1 fixes are remembered for the whole team.',
12
+ inputSchema: {
13
+ debugLogId: z
14
+ .string()
15
+ .min(1)
16
+ .describe('The debug_log_id value from the debug_error response you are reporting on.'),
17
+ result: z
18
+ .enum(['worked', 'failed'])
19
+ .describe('"worked" = the fix resolved the error; "failed" = it did not (or made things worse).'),
20
+ fixRank: z
21
+ .number()
22
+ .int()
23
+ .min(1)
24
+ .max(3)
25
+ .optional()
26
+ .describe('Which ranked fix you applied (1-3). Rank 1 outcomes feed team error memory.'),
27
+ newError: z
28
+ .string()
29
+ .max(4000)
30
+ .optional()
31
+ .describe('If result is "failed": the error observed AFTER applying the fix.'),
32
+ },
33
+ annotations: {
34
+ title: 'Report Fix Outcome',
35
+ // Deliberately NO readOnlyHint — this records telemetry server-side.
36
+ idempotentHint: true,
37
+ },
38
+ }, async ({ debugLogId, result, fixRank, newError }) => {
39
+ try {
40
+ await callOutcomeBackend({
41
+ debug_log_id: debugLogId,
42
+ result,
43
+ fix_rank: fixRank,
44
+ new_error: newError,
45
+ source: 'agent',
46
+ }, config);
47
+ const ack = result === 'worked'
48
+ ? 'Outcome recorded: fix worked. Rank-1 confirmations are remembered for this project, so the next hit on this error starts from the confirmed fix.'
49
+ : 'Outcome recorded: fix failed. The follow-up error was logged and feeds directly into improving future answers. If you are still stuck, call debug_error again with the NEW error text.';
50
+ return {
51
+ content: [{ type: 'text', text: ack }],
52
+ structuredContent: { recorded: true, result },
53
+ };
54
+ }
55
+ catch (err) {
56
+ return mapBackendErrorToToolResult(err);
57
+ }
58
+ });
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugai/mcp",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "DebugAI MCP server — hand any error to DebugAI from Claude Desktop, Claude Code, Cursor, Zed, or any MCP client and get root cause + ranked fixes.",
5
5
  "license": "MIT",
6
6
  "type": "module",