@snipcodeit/mgw 0.4.0 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index$1 = require('../index-B-_JvYpz.cjs');
3
+ var index$1 = require('../index-CHrVAIMY.cjs');
4
4
  var require$$0 = require('child_process');
5
5
  var require$$1 = require('path');
6
6
  var require$$3 = require('os');
@@ -145,6 +145,500 @@ function requirePipeline () {
145
145
  return pipeline;
146
146
  }
147
147
 
148
+ var agentErrors;
149
+ var hasRequiredAgentErrors;
150
+
151
+ function requireAgentErrors () {
152
+ if (hasRequiredAgentErrors) return agentErrors;
153
+ hasRequiredAgentErrors = 1;
154
+ const { MgwError } = index$1.requireErrors();
155
+ const SEVERITY_LEVELS = Object.freeze({
156
+ critical: { name: "critical", weight: 4, description: "Agent produced dangerous or misleading results" },
157
+ high: { name: "high", weight: 3, description: "Agent failed to produce usable output" },
158
+ medium: { name: "medium", weight: 2, description: "Agent partially succeeded but gaps remain" },
159
+ low: { name: "low", weight: 1, description: "Minor issue, likely recoverable automatically" }
160
+ });
161
+ const AGENT_FAILURE_TYPES = Object.freeze({
162
+ timeout: Object.freeze({
163
+ code: "AGENT_ERR_TIMEOUT",
164
+ name: "timeout",
165
+ severity: "high",
166
+ description: "Agent exceeded turn limit or wall-clock timeout without completing its task",
167
+ recovery: "Retry with reduced scope \u2014 split the task into smaller sub-tasks or increase the turn budget",
168
+ retryable: true
169
+ }),
170
+ "malformed-output": Object.freeze({
171
+ code: "AGENT_ERR_MALFORMED_OUTPUT",
172
+ name: "malformed-output",
173
+ severity: "high",
174
+ description: "Agent returned output that cannot be parsed or does not match the expected format",
175
+ recovery: "Retry with explicit format instructions \u2014 add structured output examples to the prompt",
176
+ retryable: true
177
+ }),
178
+ "partial-completion": Object.freeze({
179
+ code: "AGENT_ERR_PARTIAL_COMPLETION",
180
+ name: "partial-completion",
181
+ severity: "medium",
182
+ description: "Agent completed some but not all assigned tasks \u2014 partial artifacts exist",
183
+ recovery: "Spawn a continuation agent for the remaining tasks using the partial output as context",
184
+ retryable: true
185
+ }),
186
+ hallucination: Object.freeze({
187
+ code: "AGENT_ERR_HALLUCINATION",
188
+ name: "hallucination",
189
+ severity: "critical",
190
+ description: "Agent claimed success but required artifacts are missing or do not match expectations",
191
+ recovery: "Reject all results and retry with a verification-first approach \u2014 require artifact proof before completion",
192
+ retryable: false
193
+ }),
194
+ "permission-denied": Object.freeze({
195
+ code: "AGENT_ERR_PERMISSION_DENIED",
196
+ name: "permission-denied",
197
+ severity: "high",
198
+ description: "Agent was blocked by a pre-commit hook, sandbox restriction, or file permission",
199
+ recovery: "Escalate to human operator \u2014 review sandbox configuration and hook settings",
200
+ retryable: false
201
+ })
202
+ });
203
+ const _codeToType = /* @__PURE__ */ new Map();
204
+ for (const [key, def] of Object.entries(AGENT_FAILURE_TYPES)) {
205
+ _codeToType.set(def.code, { key, ...def });
206
+ }
207
+ class AgentFailureError extends MgwError {
208
+ /**
209
+ * @param {string} message - Human-readable error description
210
+ * @param {object} [opts]
211
+ * @param {string} [opts.agentType] - GSD agent type (gsd-planner, gsd-executor, etc.)
212
+ * @param {string} [opts.failureType] - Failure type key from AGENT_FAILURE_TYPES
213
+ * @param {string[]} [opts.artifacts] - List of expected artifacts that are missing/malformed
214
+ * @param {string} [opts.stage] - Pipeline stage where failure occurred
215
+ * @param {number} [opts.issueNumber] - Related GitHub issue number
216
+ * @param {Error} [opts.cause] - Original error
217
+ */
218
+ constructor(message, opts) {
219
+ const o = opts || {};
220
+ const failureDef = o.failureType ? AGENT_FAILURE_TYPES[o.failureType] : null;
221
+ const code = failureDef ? failureDef.code : "AGENT_ERR_UNKNOWN";
222
+ super(message, { code, stage: o.stage, issueNumber: o.issueNumber, cause: o.cause });
223
+ this.name = "AgentFailureError";
224
+ this.agentType = o.agentType || null;
225
+ this.failureType = o.failureType || null;
226
+ this.artifacts = Array.isArray(o.artifacts) ? o.artifacts : [];
227
+ }
228
+ /**
229
+ * Get the full failure type definition for this error.
230
+ * @returns {object|null}
231
+ */
232
+ getFailureDefinition() {
233
+ if (!this.failureType) return null;
234
+ return AGENT_FAILURE_TYPES[this.failureType] || null;
235
+ }
236
+ /**
237
+ * Get the severity level for this error.
238
+ * @returns {string|null}
239
+ */
240
+ getSeverity() {
241
+ const def = this.getFailureDefinition();
242
+ return def ? def.severity : null;
243
+ }
244
+ /**
245
+ * Check whether this failure type is safe to auto-retry.
246
+ * @returns {boolean}
247
+ */
248
+ isRetryable() {
249
+ const def = this.getFailureDefinition();
250
+ return def ? def.retryable : false;
251
+ }
252
+ }
253
+ const CLASSIFICATION_PATTERNS = [
254
+ // Timeout patterns
255
+ { pattern: "turn limit", type: "timeout" },
256
+ { pattern: "max turns", type: "timeout" },
257
+ { pattern: "context window exhausted", type: "timeout" },
258
+ { pattern: "exceeded.*timeout", type: "timeout" },
259
+ { pattern: "agent timed out", type: "timeout" },
260
+ { pattern: "wall.?clock.*exceeded", type: "timeout" },
261
+ // Malformed output patterns
262
+ { pattern: "unparseable", type: "malformed-output" },
263
+ { pattern: "invalid json", type: "malformed-output" },
264
+ { pattern: "parse error", type: "malformed-output" },
265
+ { pattern: "unexpected token", type: "malformed-output" },
266
+ { pattern: "malformed output", type: "malformed-output" },
267
+ { pattern: "missing required field", type: "malformed-output" },
268
+ { pattern: "output format", type: "malformed-output" },
269
+ { pattern: "expected.*format", type: "malformed-output" },
270
+ // Partial completion patterns
271
+ { pattern: "partial completion", type: "partial-completion" },
272
+ { pattern: "incomplete.*tasks", type: "partial-completion" },
273
+ { pattern: "completed.*of.*tasks", type: "partial-completion" },
274
+ { pattern: "remaining tasks", type: "partial-completion" },
275
+ { pattern: "some tasks failed", type: "partial-completion" },
276
+ // Hallucination patterns (check before generic patterns)
277
+ { pattern: "artifacts? missing", type: "hallucination" },
278
+ { pattern: "claimed success.*missing", type: "hallucination" },
279
+ { pattern: "file.*not found.*after", type: "hallucination" },
280
+ { pattern: "verification failed.*not exist", type: "hallucination" },
281
+ { pattern: "hallucination", type: "hallucination" },
282
+ { pattern: "phantom", type: "hallucination" },
283
+ // Permission denied patterns
284
+ { pattern: "permission denied", type: "permission-denied" },
285
+ { pattern: "access denied", type: "permission-denied" },
286
+ { pattern: "hook.*failed", type: "permission-denied" },
287
+ { pattern: "pre.?commit.*rejected", type: "permission-denied" },
288
+ { pattern: "sandbox.*blocked", type: "permission-denied" },
289
+ { pattern: "sandbox.*violation", type: "permission-denied" },
290
+ { pattern: "eacces", type: "permission-denied" }
291
+ ];
292
+ function classifyAgentFailure(error, context) {
293
+ if (!error || typeof error !== "object") return null;
294
+ const message = (error.message || "").toLowerCase();
295
+ const code = (error.code || "").toLowerCase();
296
+ const ctx = context || {};
297
+ if (ctx.expectedArtifacts && ctx.actualArtifacts) {
298
+ const expected = new Set(ctx.expectedArtifacts);
299
+ const actual = new Set(ctx.actualArtifacts);
300
+ const missing = [...expected].filter((a) => !actual.has(a));
301
+ if (missing.length > 0 && ctx.tasksCompleted > 0) {
302
+ const def = AGENT_FAILURE_TYPES.hallucination;
303
+ return { type: "hallucination", code: def.code, severity: def.severity, confidence: "high" };
304
+ }
305
+ }
306
+ if (typeof ctx.tasksTotal === "number" && typeof ctx.tasksCompleted === "number") {
307
+ if (ctx.tasksCompleted > 0 && ctx.tasksCompleted < ctx.tasksTotal) {
308
+ const def = AGENT_FAILURE_TYPES["partial-completion"];
309
+ return { type: "partial-completion", code: def.code, severity: def.severity, confidence: "high" };
310
+ }
311
+ }
312
+ if (code === "eacces" || code === "eperm") {
313
+ const def = AGENT_FAILURE_TYPES["permission-denied"];
314
+ return { type: "permission-denied", code: def.code, severity: def.severity, confidence: "high" };
315
+ }
316
+ for (const { pattern, type } of CLASSIFICATION_PATTERNS) {
317
+ const regex = new RegExp(pattern, "i");
318
+ if (regex.test(message)) {
319
+ const def = AGENT_FAILURE_TYPES[type];
320
+ return { type, code: def.code, severity: def.severity, confidence: "medium" };
321
+ }
322
+ }
323
+ return null;
324
+ }
325
+ function getRecoveryAction(failureType) {
326
+ const def = AGENT_FAILURE_TYPES[failureType];
327
+ if (!def) return null;
328
+ return {
329
+ action: def.recovery,
330
+ retryable: def.retryable,
331
+ severity: def.severity
332
+ };
333
+ }
334
+ function isRetryable(failureType) {
335
+ const def = AGENT_FAILURE_TYPES[failureType];
336
+ return def ? def.retryable : false;
337
+ }
338
+ function compareSeverity(a, b) {
339
+ const weightA = (SEVERITY_LEVELS[a] || { weight: 0 }).weight;
340
+ const weightB = (SEVERITY_LEVELS[b] || { weight: 0 }).weight;
341
+ return weightA - weightB;
342
+ }
343
+ function getFailureByCode(errorCode) {
344
+ return _codeToType.get(errorCode) || null;
345
+ }
346
+ agentErrors = {
347
+ // Constants
348
+ AGENT_FAILURE_TYPES,
349
+ SEVERITY_LEVELS,
350
+ // Error class
351
+ AgentFailureError,
352
+ // Classification
353
+ classifyAgentFailure,
354
+ // Recovery
355
+ getRecoveryAction,
356
+ isRetryable,
357
+ // Utilities
358
+ compareSeverity,
359
+ getFailureByCode
360
+ };
361
+ return agentErrors;
362
+ }
363
+
364
+ var modelFallback;
365
+ var hasRequiredModelFallback;
366
+
367
+ function requireModelFallback () {
368
+ if (hasRequiredModelFallback) return modelFallback;
369
+ hasRequiredModelFallback = 1;
370
+ const fs = require$$2;
371
+ const path = require$$1;
372
+ const { MgwError } = index$1.requireErrors();
373
+ let classifyAgentFailure = null;
374
+ let _AGENT_FAILURE_TYPES = null;
375
+ try {
376
+ const agentErrors = requireAgentErrors();
377
+ classifyAgentFailure = agentErrors.classifyAgentFailure;
378
+ _AGENT_FAILURE_TYPES = agentErrors.AGENT_FAILURE_TYPES;
379
+ } catch (_) {
380
+ }
381
+ let classifyFailure = null;
382
+ try {
383
+ const retry = index$1.requireRetry();
384
+ classifyFailure = retry.classifyFailure;
385
+ } catch (_) {
386
+ }
387
+ const DEFAULT_FALLBACK_CHAINS = Object.freeze({
388
+ "gsd-planner": Object.freeze(["inherit", "sonnet", "haiku"]),
389
+ "gsd-executor": Object.freeze(["sonnet", "haiku"]),
390
+ "gsd-verifier": Object.freeze(["sonnet", "haiku"]),
391
+ "gsd-plan-checker": Object.freeze(["sonnet", "haiku"]),
392
+ "general-purpose": Object.freeze(["sonnet", "haiku"])
393
+ });
394
+ const NON_FALLBACK_FAILURE_TYPES = /* @__PURE__ */ new Set([
395
+ "hallucination",
396
+ // Unsafe — different model might hallucinate differently
397
+ "permission-denied"
398
+ // Environment issue — model change won't help
399
+ ]);
400
+ class ModelFallbackError extends MgwError {
401
+ /**
402
+ * @param {string} message
403
+ * @param {object} [opts]
404
+ * @param {string} [opts.failureType] - Last classified failure type
405
+ * @param {string} [opts.agentType] - GSD agent type
406
+ * @param {string[]} [opts.modelsAttempted] - Models that were tried
407
+ * @param {number} [opts.fallback_attempts] - Number of fallback attempts made
408
+ * @param {Error} [opts.cause] - Last error from final model attempt
409
+ * @param {string} [opts.stage] - Pipeline stage
410
+ * @param {number} [opts.issueNumber] - Related GitHub issue number
411
+ */
412
+ constructor(message, opts) {
413
+ const o = opts || {};
414
+ super(message, {
415
+ code: "MODEL_FALLBACK_EXHAUSTED",
416
+ stage: o.stage,
417
+ issueNumber: o.issueNumber,
418
+ cause: o.cause
419
+ });
420
+ this.name = "ModelFallbackError";
421
+ this.failureType = o.failureType || null;
422
+ this.agentType = o.agentType || null;
423
+ this.modelsAttempted = Array.isArray(o.modelsAttempted) ? o.modelsAttempted : [];
424
+ this.fallback_attempts = typeof o.fallback_attempts === "number" ? o.fallback_attempts : 0;
425
+ }
426
+ }
427
+ class ModelFallbackEngine {
428
+ /**
429
+ * @param {object} [opts]
430
+ * @param {boolean} [opts.enabled] - Master enable/disable switch (overrides config)
431
+ * @param {object} [opts.chains] - Per-agent-type chain overrides
432
+ * Format: { 'gsd-planner': ['inherit', 'sonnet'] }
433
+ * @param {string} [opts.configPath] - Path to .mgw/config.json (default: auto-detect)
434
+ */
435
+ constructor(opts) {
436
+ const o = opts || {};
437
+ const fileConfig = ModelFallbackEngine.loadConfig(o.configPath || null);
438
+ if (typeof o.enabled === "boolean") {
439
+ this._enabled = o.enabled;
440
+ } else if (typeof fileConfig.enabled === "boolean") {
441
+ this._enabled = fileConfig.enabled;
442
+ } else {
443
+ this._enabled = false;
444
+ }
445
+ this._chains = Object.assign(
446
+ {},
447
+ DEFAULT_FALLBACK_CHAINS,
448
+ fileConfig.chains || {},
449
+ o.chains || {}
450
+ );
451
+ }
452
+ // -------------------------------------------------------------------------
453
+ // Public API
454
+ // -------------------------------------------------------------------------
455
+ /**
456
+ * Check whether model fallback is enabled.
457
+ * @returns {boolean}
458
+ */
459
+ get enabled() {
460
+ return this._enabled;
461
+ }
462
+ /**
463
+ * Resolve the ordered fallback chain for an agent type.
464
+ *
465
+ * If model_fallback is disabled, returns an array with only the primary
466
+ * model (no fallback — preserves backward-compatible behavior).
467
+ *
468
+ * @param {string} agentType - GSD agent type (e.g. 'gsd-planner')
469
+ * @returns {string[]} Ordered model chain (first = primary, rest = fallbacks)
470
+ */
471
+ resolveFallbackChain(agentType) {
472
+ const chain = this._chains[agentType] || this._chains["general-purpose"] || ["sonnet"];
473
+ if (!this._enabled) {
474
+ return [chain[0]];
475
+ }
476
+ return [...chain];
477
+ }
478
+ /**
479
+ * Execute an async function with model-tier fallback.
480
+ *
481
+ * Takes a function that receives a model name and attempts execution
482
+ * with each model in the fallback chain until one succeeds or the
483
+ * chain is exhausted.
484
+ *
485
+ * @param {(modelName: string) => Promise<*>} fn - Async function that accepts model name
486
+ * @param {object} [opts]
487
+ * @param {string} [opts.agentType] - GSD agent type for chain lookup
488
+ * @param {function} [opts.onFallback] - Callback on fallback: (fromModel, toModel, attempt, error) => void
489
+ * @param {AbortSignal} [opts.signal] - AbortSignal to cancel fallback attempts
490
+ * @returns {Promise<{ result: *, model: string, fallback_attempts: number, total_models_tried: number }>}
491
+ * @throws {ModelFallbackError} If all models in chain are exhausted
492
+ * @throws {Error} Original error if failure type is non-fallback-eligible
493
+ */
494
+ async executeWithFallback(fn, opts) {
495
+ const o = opts || {};
496
+ const agentType = o.agentType || "general-purpose";
497
+ const onFallback = typeof o.onFallback === "function" ? o.onFallback : null;
498
+ const signal = o.signal || null;
499
+ const chain = this.resolveFallbackChain(agentType);
500
+ const modelsAttempted = [];
501
+ let lastError = null;
502
+ let fallbackAttempts = 0;
503
+ for (let i = 0; i < chain.length; i++) {
504
+ const model = chain[i];
505
+ if (signal && signal.aborted) {
506
+ throw lastError || new MgwError("Fallback aborted by signal", { code: "FALLBACK_ABORTED" });
507
+ }
508
+ modelsAttempted.push(model);
509
+ try {
510
+ const result = await fn(model);
511
+ return {
512
+ result,
513
+ model,
514
+ fallback_attempts: fallbackAttempts,
515
+ total_models_tried: modelsAttempted.length
516
+ };
517
+ } catch (err) {
518
+ lastError = err;
519
+ const failureType = this._classifyForFallback(err, agentType);
520
+ if (NON_FALLBACK_FAILURE_TYPES.has(failureType)) {
521
+ throw err;
522
+ }
523
+ if (i < chain.length - 1) {
524
+ fallbackAttempts++;
525
+ if (onFallback) {
526
+ try {
527
+ onFallback(model, chain[i + 1], fallbackAttempts, err);
528
+ } catch (_) {
529
+ }
530
+ }
531
+ }
532
+ }
533
+ }
534
+ throw new ModelFallbackError(
535
+ `Model fallback exhausted for ${agentType}: tried ${modelsAttempted.join(", ")}`,
536
+ {
537
+ failureType: this._classifyForFallback(lastError, agentType),
538
+ agentType,
539
+ modelsAttempted,
540
+ fallback_attempts: fallbackAttempts,
541
+ cause: lastError
542
+ }
543
+ );
544
+ }
545
+ // -------------------------------------------------------------------------
546
+ // Internal: error classification for fallback decisions
547
+ // -------------------------------------------------------------------------
548
+ /**
549
+ * Classify an error to determine if model fallback should be attempted.
550
+ *
551
+ * Uses agent-errors.cjs classification first (if available), then falls
552
+ * back to generic retry.cjs classification.
553
+ *
554
+ * @param {Error} err - The error to classify
555
+ * @param {string} [agentType] - GSD agent type for context
556
+ * @returns {string} Failure type key (e.g. 'timeout', 'permanent', 'transient')
557
+ * @private
558
+ */
559
+ _classifyForFallback(err, agentType) {
560
+ if (classifyAgentFailure) {
561
+ const agentResult = classifyAgentFailure(err, { agentType });
562
+ if (agentResult && agentResult.type) {
563
+ return agentResult.type;
564
+ }
565
+ }
566
+ if (classifyFailure) {
567
+ const genericResult = classifyFailure(err);
568
+ if (genericResult && genericResult.class) {
569
+ if (genericResult.class === "transient") return "timeout";
570
+ if (genericResult.class === "needs-info") return "needs-info";
571
+ return "permanent";
572
+ }
573
+ }
574
+ return "permanent";
575
+ }
576
+ // -------------------------------------------------------------------------
577
+ // Static: config loading
578
+ // -------------------------------------------------------------------------
579
+ /**
580
+ * Load model fallback configuration from .mgw/config.json.
581
+ *
582
+ * Looks for a `retry` section with `model_fallback` and `fallback_chains`:
583
+ * ```json
584
+ * {
585
+ * "retry": {
586
+ * "model_fallback": true,
587
+ * "fallback_chains": {
588
+ * "gsd-planner": ["inherit", "sonnet"],
589
+ * "gsd-executor": ["sonnet", "haiku"]
590
+ * }
591
+ * }
592
+ * }
593
+ * ```
594
+ *
595
+ * @param {string} [configPath] - Explicit path to config.json. If null,
596
+ * searches for .mgw/config.json relative to cwd.
597
+ * @returns {{ enabled?: boolean, chains?: object }}
598
+ */
599
+ static loadConfig(configPath) {
600
+ const empty = {};
601
+ try {
602
+ const cfgPath = configPath || path.join(process.cwd(), ".mgw", "config.json");
603
+ if (!fs.existsSync(cfgPath)) return empty;
604
+ const raw = fs.readFileSync(cfgPath, "utf-8");
605
+ const config = JSON.parse(raw);
606
+ if (!config || typeof config !== "object") return empty;
607
+ const retry = config.retry;
608
+ if (!retry || typeof retry !== "object") return empty;
609
+ const result = {};
610
+ if (typeof retry.model_fallback === "boolean") {
611
+ result.enabled = retry.model_fallback;
612
+ }
613
+ if (retry.fallback_chains && typeof retry.fallback_chains === "object") {
614
+ const chains = {};
615
+ for (const [agentType, chain] of Object.entries(retry.fallback_chains)) {
616
+ if (Array.isArray(chain) && chain.every((m) => typeof m === "string")) {
617
+ chains[agentType] = chain;
618
+ }
619
+ }
620
+ if (Object.keys(chains).length > 0) {
621
+ result.chains = chains;
622
+ }
623
+ }
624
+ return result;
625
+ } catch (_) {
626
+ return empty;
627
+ }
628
+ }
629
+ }
630
+ modelFallback = {
631
+ // Constants
632
+ DEFAULT_FALLBACK_CHAINS,
633
+ NON_FALLBACK_FAILURE_TYPES,
634
+ // Error class
635
+ ModelFallbackError,
636
+ // Engine
637
+ ModelFallbackEngine
638
+ };
639
+ return modelFallback;
640
+ }
641
+
148
642
  var gsdAdapter;
149
643
  var hasRequiredGsdAdapter;
150
644
 
@@ -316,6 +810,32 @@ Set GSD_TOOLS_PATH or add gsd_path to .mgw/config.json`
316
810
  }
317
811
  return { activeMilestone, currentPhase, planCount };
318
812
  }
813
+ let ModelFallbackEngine = null;
814
+ try {
815
+ const modelFallback = requireModelFallback();
816
+ ModelFallbackEngine = modelFallback.ModelFallbackEngine;
817
+ } catch (_) {
818
+ }
819
+ let _fallbackEngineSingleton = null;
820
+ function getModelFallbackEngine() {
821
+ if (!ModelFallbackEngine) return null;
822
+ if (!_fallbackEngineSingleton) {
823
+ _fallbackEngineSingleton = new ModelFallbackEngine();
824
+ }
825
+ return _fallbackEngineSingleton;
826
+ }
827
+ function resolveFallbackChain(agentType) {
828
+ const engine = getModelFallbackEngine();
829
+ if (engine && engine.enabled) {
830
+ return engine.resolveFallbackChain(agentType);
831
+ }
832
+ try {
833
+ const primary = resolveModel(agentType);
834
+ return [primary];
835
+ } catch (_) {
836
+ return ["sonnet"];
837
+ }
838
+ }
319
839
  gsdAdapter = {
320
840
  resolveGsdRoot,
321
841
  getGsdToolsPath,
@@ -326,7 +846,9 @@ Set GSD_TOOLS_PATH or add gsd_path to .mgw/config.json`
326
846
  historyDigest,
327
847
  roadmapAnalyze,
328
848
  selectGsdRoute,
329
- getGsdState
849
+ getGsdState,
850
+ getModelFallbackEngine,
851
+ resolveFallbackChain
330
852
  };
331
853
  return gsdAdapter;
332
854
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snipcodeit/mgw",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "GitHub-native issue-to-PR automation for Claude Code, powered by Get Shit Done",
5
5
  "bin": {
6
6
  "mgw": "./dist/bin/mgw.cjs"
@@ -16,7 +16,10 @@
16
16
  "scripts": {
17
17
  "build": "pkgroll --clean-dist --src .",
18
18
  "dev": "pkgroll --watch --src .",
19
- "test": "node --test test/*.test.cjs",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:node": "node --test test/*.test.cjs",
22
+ "test:coverage": "vitest run --coverage",
20
23
  "lint": "eslint lib/ bin/ test/",
21
24
  "prepublishOnly": "npm run build",
22
25
  "completions": "node bin/generate-completions.cjs",
@@ -30,8 +33,10 @@
30
33
  },
31
34
  "devDependencies": {
32
35
  "@eslint/js": "^10.0.1",
36
+ "@vitest/coverage-v8": "^4.0.18",
33
37
  "eslint": "^10.0.2",
34
- "pkgroll": "^2.26.3"
38
+ "pkgroll": "^2.26.3",
39
+ "vitest": "^4.0.18"
35
40
  },
36
41
  "engines": {
37
42
  "node": ">=18.0.0"
@@ -56,4 +61,4 @@
56
61
  "bugs": {
57
62
  "url": "https://github.com/snipcodeit/mgw/issues"
58
63
  }
59
- }
64
+ }