@snipcodeit/mgw 0.3.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.
@@ -227,13 +227,186 @@ File: `.mgw/active/<number>-<slug>.json`
227
227
  "gsd_route": null,
228
228
  "gsd_artifacts": { "type": null, "path": null },
229
229
  "pipeline_stage": "new|triaged|needs-info|needs-security-review|discussing|approved|planning|diagnosing|executing|verifying|pr-created|done|failed|blocked",
230
+ "checkpoint": null,
230
231
  "comments_posted": [],
231
232
  "linked_pr": null,
232
233
  "linked_issues": [],
233
- "linked_branches": []
234
+ "linked_branches": [],
235
+ "checkpoint": null
234
236
  }
235
237
  ```
236
238
 
239
+ ## Checkpoint Schema
240
+
241
+ The `checkpoint` field in `.mgw/active/<number>-<slug>.json` tracks fine-grained pipeline
242
+ execution progress. It enables resume after failures, context switches, or multi-session
243
+ execution. The field is `null` until pipeline execution begins (set during the triage-to-
244
+ executing transition).
245
+
246
+ ### Checkpoint Object Structure
247
+
248
+ ```json
249
+ {
250
+ "checkpoint": {
251
+ "schema_version": 1,
252
+ "pipeline_step": "triage|plan|execute|verify|pr",
253
+ "step_progress": {},
254
+ "last_agent_output": null,
255
+ "artifacts": [],
256
+ "resume": {
257
+ "action": null,
258
+ "context": {}
259
+ },
260
+ "started_at": "2026-03-06T12:00:00Z",
261
+ "updated_at": "2026-03-06T12:05:00Z",
262
+ "step_history": []
263
+ }
264
+ }
265
+ ```
266
+
267
+ ### Checkpoint Fields
268
+
269
+ | Field | Type | Default | Description |
270
+ |-------|------|---------|-------------|
271
+ | `schema_version` | integer | `1` | Schema version for forward-compatibility. Consumers check this before parsing. New fields can be added without bumping; bump only for breaking structural changes. |
272
+ | `pipeline_step` | string | `"triage"` | Current high-level pipeline step. Values: `"triage"`, `"plan"`, `"execute"`, `"verify"`, `"pr"`. Maps to GSD lifecycle stages but at a coarser grain than `pipeline_stage`. |
273
+ | `step_progress` | object | `{}` | Step-specific progress data. Shape varies by `pipeline_step` (see Step Progress Shapes below). Unknown keys are preserved on read -- consumers must not strip unrecognized fields. |
274
+ | `last_agent_output` | string\|null | `null` | File path (relative to repo root) of the last successful agent output. Updated after each agent spawn completes. Used for resume context injection. |
275
+ | `artifacts` | array | `[]` | Accumulated artifact paths produced during this pipeline run. Each entry is `{ "path": "relative/path", "type": "plan\|summary\|verification\|commit", "created_at": "ISO" }`. Append-only -- never remove entries. |
276
+ | `resume` | object | `{ "action": null, "context": {} }` | Instructions for resuming execution. `action` is a string describing what to do next (e.g., `"spawn-executor"`, `"retry-verifier"`, `"create-pr"`). `context` carries step-specific data needed for resume (e.g., `{ "phase_number": 3, "plan_path": ".planning/..." }`). |
277
+ | `started_at` | string | ISO timestamp | When checkpoint tracking began for this pipeline run. |
278
+ | `updated_at` | string | ISO timestamp | When the checkpoint was last modified. Updated on every checkpoint write. |
279
+ | `step_history` | array | `[]` | Ordered log of completed steps. Each entry: `{ "step": "plan", "completed_at": "ISO", "agent_type": "gsd-planner", "output_path": "..." }`. Append-only. |
280
+
281
+ ### Step Progress Shapes
282
+
283
+ The `step_progress` object has a different shape depending on the current `pipeline_step`.
284
+ These are the documented shapes; future pipeline steps can define their own without breaking
285
+ existing consumers (unknown keys are preserved).
286
+
287
+ **When `pipeline_step` is `"triage"`:**
288
+ ```json
289
+ {
290
+ "comment_check_done": false,
291
+ "route_selected": null
292
+ }
293
+ ```
294
+
295
+ **When `pipeline_step` is `"plan"`:**
296
+ ```json
297
+ {
298
+ "plan_path": null,
299
+ "plan_checked": false,
300
+ "revision_count": 0
301
+ }
302
+ ```
303
+
304
+ **When `pipeline_step` is `"execute"`:**
305
+ ```json
306
+ {
307
+ "gsd_phase": null,
308
+ "total_phases": null,
309
+ "current_task": null,
310
+ "tasks_completed": 0,
311
+ "tasks_total": null,
312
+ "commits": []
313
+ }
314
+ ```
315
+
316
+ **When `pipeline_step` is `"verify"`:**
317
+ ```json
318
+ {
319
+ "verification_path": null,
320
+ "must_haves_checked": false,
321
+ "artifact_check_done": false,
322
+ "keylink_check_done": false
323
+ }
324
+ ```
325
+
326
+ **When `pipeline_step` is `"pr"`:**
327
+ ```json
328
+ {
329
+ "branch_pushed": false,
330
+ "pr_number": null,
331
+ "pr_url": null
332
+ }
333
+ ```
334
+
335
+ ### Forward Compatibility Contract
336
+
337
+ 1. **New fields can be added** to the checkpoint object at any level without incrementing
338
+ `schema_version`. Consumers must tolerate unknown fields (preserve on read-modify-write,
339
+ ignore on read-only access).
340
+
341
+ 2. **New `pipeline_step` values** can be introduced freely. Existing step_progress shapes
342
+ are not affected. The `step_progress` for an unrecognized step should be treated as an
343
+ opaque object (pass through unchanged).
344
+
345
+ 3. **`schema_version` bump** is required only when an existing field changes its type,
346
+ semantics, or is removed. When bumped, `migrateProjectState()` in `lib/state.cjs` must
347
+ handle the migration.
348
+
349
+ 4. **`artifacts` and `step_history` are append-only**. Consumers should never modify or
350
+ remove entries from these arrays. They may be compacted during archival (when pipeline
351
+ reaches `done` stage and state moves to `.mgw/completed/`).
352
+
353
+ 5. **`resume.context` is opaque** to all consumers except the specific resume handler for
354
+ the given `resume.action`. This allows step-specific resume data to evolve independently.
355
+
356
+ ### Checkpoint Lifecycle
357
+
358
+ ```
359
+ triage (checkpoint initialized, pipeline_step="triage")
360
+ |
361
+ v
362
+ plan (pipeline_step="plan", step_progress tracks planning state)
363
+ |
364
+ v
365
+ execute (pipeline_step="execute", step_progress tracks GSD phase/task progress)
366
+ |
367
+ v
368
+ verify (pipeline_step="verify", step_progress tracks verification checks)
369
+ |
370
+ v
371
+ pr (pipeline_step="pr", step_progress tracks PR creation)
372
+ |
373
+ v
374
+ done (checkpoint frozen — archived to .mgw/completed/)
375
+ ```
376
+
377
+ ### Checkpoint Update Pattern
378
+
379
+ ```bash
380
+ # Update checkpoint at key pipeline stages using updateCheckpoint()
381
+ node -e "
382
+ const { updateCheckpoint } = require('./lib/state.cjs');
383
+ updateCheckpoint(${ISSUE_NUMBER}, {
384
+ pipeline_step: 'execute',
385
+ step_progress: {
386
+ gsd_phase: ${PHASE_NUMBER},
387
+ tasks_completed: ${COMPLETED},
388
+ tasks_total: ${TOTAL}
389
+ },
390
+ last_agent_output: '${OUTPUT_PATH}',
391
+ resume: {
392
+ action: 'continue-execution',
393
+ context: { phase_number: ${PHASE_NUMBER} }
394
+ }
395
+ });
396
+ "
397
+ ```
398
+
399
+ ### Consumers
400
+
401
+ | Consumer | Access Pattern |
402
+ |----------|---------------|
403
+ | run/triage.md | Initialize checkpoint at triage (`pipeline_step: "triage"`) |
404
+ | run/execute.md | Update checkpoint after each agent spawn (`pipeline_step: "plan"\|"execute"\|"verify"`) |
405
+ | run/pr-create.md | Update checkpoint at PR creation (`pipeline_step: "pr"`) |
406
+ | milestone.md | Read checkpoint to determine resume point for failed issues |
407
+ | status.md | Read checkpoint for detailed progress display |
408
+ | sync.md | Compare checkpoint state against GitHub for drift detection |
409
+
237
410
  ## Stage Flow Diagram
238
411
 
239
412
  ```
@@ -264,6 +437,84 @@ blocked --> triaged (re-triage after blocker resolved)
264
437
  Any stage --> failed (unrecoverable error)
265
438
  ```
266
439
 
440
+ ## Pipeline Checkpoints
441
+
442
+ Fine-grained pipeline progress tracking within `.mgw/active/<number>-<slug>.json`.
443
+ The `checkpoint` field starts as `null` and is initialized when the pipeline first
444
+ transitions past triage. Each subsequent stage writes an atomic checkpoint update.
445
+
446
+ ### Checkpoint Schema
447
+
448
+ ```json
449
+ {
450
+ "checkpoint": {
451
+ "schema_version": 1,
452
+ "pipeline_step": "triage|plan|execute|verify|pr",
453
+ "step_progress": {},
454
+ "last_agent_output": null,
455
+ "artifacts": [],
456
+ "resume": { "action": null, "context": {} },
457
+ "started_at": "ISO timestamp",
458
+ "updated_at": "ISO timestamp",
459
+ "step_history": []
460
+ }
461
+ }
462
+ ```
463
+
464
+ | Field | Type | Merge Strategy | Description |
465
+ |-------|------|---------------|-------------|
466
+ | `schema_version` | number | — | Checkpoint format version (currently 1) |
467
+ | `pipeline_step` | string | overwrite | Current pipeline step: `triage`, `plan`, `execute`, `verify`, `pr` |
468
+ | `step_progress` | object | shallow merge | Step-specific progress (e.g., `{ plan_path: "...", plan_checked: false }`) |
469
+ | `last_agent_output` | string\|null | overwrite | Path or URL of the last agent's output |
470
+ | `artifacts` | array | append-only | `[{ path, type, created_at }]` — never removed, only appended |
471
+ | `resume` | object | full replace | `{ action, context }` — what to do if pipeline restarts |
472
+ | `started_at` | string | — | ISO timestamp when checkpoint was first created |
473
+ | `updated_at` | string | auto | ISO timestamp of last update (set automatically) |
474
+ | `step_history` | array | append-only | `[{ step, completed_at, agent_type, output_path }]` — audit trail |
475
+
476
+ ### Atomic Writes
477
+
478
+ All checkpoint writes use `atomicWriteJson()` from `lib/state.cjs`:
479
+
480
+ ```bash
481
+ # atomicWriteJson(filePath, data) — write to .tmp then rename.
482
+ # POSIX rename is atomic on the same filesystem, so a crash mid-write
483
+ # never leaves a corrupt state file.
484
+ ```
485
+
486
+ The `updateCheckpoint()` function uses `atomicWriteJson()` internally. Commands
487
+ should always use `updateCheckpoint()` rather than writing checkpoints directly:
488
+
489
+ ```bash
490
+ node -e "
491
+ const { updateCheckpoint } = require('./lib/state.cjs');
492
+ updateCheckpoint(${ISSUE_NUMBER}, {
493
+ pipeline_step: 'plan',
494
+ step_progress: { plan_path: '...', plan_checked: false },
495
+ artifacts: [{ path: '...', type: 'plan', created_at: new Date().toISOString() }],
496
+ step_history: [{ step: 'plan', completed_at: new Date().toISOString(), agent_type: 'gsd-planner', output_path: '...' }],
497
+ resume: { action: 'spawn-executor', context: { quick_dir: '...' } }
498
+ });
499
+ " 2>/dev/null || true
500
+ ```
501
+
502
+ ### Checkpoint Lifecycle
503
+
504
+ | Pipeline Step | Checkpoint `pipeline_step` | Resume Action |
505
+ |--------------|---------------------------|---------------|
506
+ | Triage complete | `triage` | `begin-execution` |
507
+ | Planner complete | `plan` | `run-plan-checker` or `spawn-executor` |
508
+ | Executor complete | `execute` | `spawn-verifier` or `create-pr` |
509
+ | Verifier complete | `verify` | `create-pr` |
510
+ | PR created | `pr` | `cleanup` |
511
+
512
+ ### Migration
513
+
514
+ `migrateProjectState()` adds the `checkpoint: null` field to any issue state
515
+ files that predate checkpoint support. The field is initialized lazily — it
516
+ stays `null` until the pipeline actually runs.
517
+
267
518
  ## Slug Generation
268
519
 
269
520
  Use gsd-tools for consistent slug generation:
@@ -396,6 +647,91 @@ GSD phase directory (`.planning/phases/{NN}-{slug}/`) to operate in.
396
647
  Issues created outside of `/mgw:project` (e.g., manually filed bugs) will not have
397
648
  a `phase_number`. In this case, `/mgw:run` falls back to the quick pipeline.
398
649
 
650
+ ## Checkpoint Resume Detection
651
+
652
+ When `mgw:run` starts for an issue, the validate_and_load step checks whether a prior
653
+ pipeline run left a checkpoint with progress beyond the initial triage step. This enables
654
+ resuming interrupted sessions without re-doing completed work.
655
+
656
+ ### Resume Detection Functions (lib/state.cjs)
657
+
658
+ | Function | Signature | Returns | Description |
659
+ |----------|-----------|---------|-------------|
660
+ | `detectCheckpoint` | `(issueNumber)` | `object\|null` | Checks if active state file has a non-null checkpoint with `pipeline_step` beyond `"triage"`. Returns the checkpoint data if resumable, `null` otherwise. |
661
+ | `resumeFromCheckpoint` | `(issueNumber)` | `object\|null` | Returns checkpoint data plus computed `resumeStage`, `resumeAction`, and `completedSteps`. Maps `resume.action` to the pipeline stage to jump to. |
662
+ | `clearCheckpoint` | `(issueNumber)` | `{ cleared: boolean }` | Resets the checkpoint field to `null` in the active state file. Used for "Fresh start" option. |
663
+
664
+ ### Resume Action to Stage Mapping
665
+
666
+ The `resume.action` field in the checkpoint tells `resumeFromCheckpoint()` which pipeline
667
+ stage to jump to:
668
+
669
+ | resume.action | resumeStage | Meaning |
670
+ |---------------|-------------|---------|
671
+ | `run-plan-checker` | `planning` | Plan exists, needs quality check |
672
+ | `spawn-executor` | `executing` | Plan complete, execute next |
673
+ | `continue-execution` | `executing` | Mid-execution resume |
674
+ | `spawn-verifier` | `verifying` | Execution done, verify next |
675
+ | `create-pr` | `pr-pending` | Verification done, create PR |
676
+ | `begin-execution` | `planning` | Triage done, begin planning |
677
+ | `null` / unknown | `planning` | Safe default |
678
+
679
+ ### Resume Detection Flow
680
+
681
+ ```
682
+ mgw:run #N starts
683
+ |
684
+ v
685
+ Load state file → migrateProjectState()
686
+ |
687
+ v
688
+ detectCheckpoint(N)
689
+ |
690
+ +---> null (no checkpoint or triage-only) → proceed with normal stage routing
691
+ |
692
+ +---> checkpoint found → display state to user
693
+ |
694
+ v
695
+ AskUserQuestion: Resume / Fresh / Skip
696
+ |
697
+ +---> Resume: load checkpoint context, set RESUME_MODE=true,
698
+ | jump to resume.action stage (skip completed steps)
699
+ |
700
+ +---> Fresh: clearCheckpoint(N), reset pipeline_stage to "triaged",
701
+ | continue normal pipeline
702
+ |
703
+ +---> Skip: exit pipeline for this issue
704
+ ```
705
+
706
+ ### Pipeline Step Order
707
+
708
+ The `CHECKPOINT_STEP_ORDER` constant defines the ordered progression of checkpoint steps:
709
+
710
+ ```
711
+ triage → plan → execute → verify → pr
712
+ ```
713
+
714
+ Only checkpoints with `pipeline_step` at index > 0 (beyond `"triage"`) are considered
715
+ resumable. A checkpoint at `"triage"` means nothing meaningful has been completed yet.
716
+
717
+ ### Resume Context
718
+
719
+ When resuming, the `resume.context` object carries step-specific data needed by the
720
+ target stage. The context shape varies by `resume.action`:
721
+
722
+ | resume.action | Context fields |
723
+ |---------------|----------------|
724
+ | `spawn-executor` | `{ quick_dir, plan_num }` |
725
+ | `run-plan-checker` | `{ quick_dir, plan_num }` |
726
+ | `spawn-verifier` | `{ quick_dir, plan_num }` |
727
+ | `create-pr` | `{ quick_dir, plan_num }` |
728
+ | `continue-execution` | `{ phase_number }` |
729
+ | `begin-execution` | `{ gsd_route, branch }` |
730
+
731
+ Downstream pipeline stages read `resume.context` to pick up where the prior run left
732
+ off. For example, the executor stage uses `quick_dir` and `plan_num` to locate the
733
+ existing plan files rather than re-creating them.
734
+
399
735
  ## Consumers
400
736
 
401
737
  | Pattern | Referenced By |
@@ -410,3 +746,6 @@ a `phase_number`. In this case, `/mgw:run` falls back to the quick pipeline.
410
746
  | Project state | milestone.md, next.md, ask.md |
411
747
  | Gate result schema | issue.md (populate), run.md (validate) |
412
748
  | Board status sync | board-sync.md (utility), issue.md (triage transitions), run.md (pipeline transitions) |
749
+ | Checkpoint resume | run.md (detect + prompt), milestone.md (detect resume point for failed issues) |
750
+ | Checkpoint writes | triage.md (init), execute.md (plan/execute/verify), pr-create.md (pr) |
751
+ | Atomic writes | lib/state.cjs (`atomicWriteJson`, `updateCheckpoint`) |
package/dist/bin/mgw.cjs CHANGED
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var index = require('../index-s7v-ifd0.cjs');
4
+ var index = require('../index-CHrVAIMY.cjs');
5
5
  var require$$0 = require('commander');
6
6
  var require$$1 = require('path');
7
- var require$$0$2 = require('fs');
7
+ var require$$2 = require('fs');
8
8
  var require$$0$1 = require('child_process');
9
+ require('os');
9
10
  require('events');
10
11
 
11
12
  var mgw$1 = {};
12
13
 
13
- var version = "0.3.0";
14
+ var version = "0.6.0";
14
15
  var require$$11 = {
15
16
  version: version};
16
17
 
@@ -21,9 +22,9 @@ function requireMgw () {
21
22
  hasRequiredMgw = 1;
22
23
  const { Command } = require$$0;
23
24
  const path = require$$1;
24
- const fs = require$$0$2;
25
+ const fs = require$$2;
25
26
  const { execSync } = require$$0$1;
26
- const { assertClaudeAvailable, invokeClaude, getCommandsDir } = index.requireClaude();
27
+ const { ProviderManager } = index.requireProviderManager();
27
28
  const { log, error, formatJson, verbose } = index.requireOutput();
28
29
  const { getActiveDir, getCompletedDir, getMgwDir } = index.requireState();
29
30
  const { getIssue, listIssues } = index.requireGithub();
@@ -32,7 +33,7 @@ function requireMgw () {
32
33
  const { startTimer } = index.requireLogger();
33
34
  const pkg = require$$11;
34
35
  const program = new Command();
35
- program.name("mgw").description("GitHub issue pipeline automation \u2014 Day 1 idea to Go Live").version(pkg.version).option("--dry-run", "show what would happen without executing").option("--json", "output structured JSON").option("-v, --verbose", "show API calls and file writes").option("--debug", "full payloads and timings").option("--model <model>", "Claude model override");
36
+ program.name("mgw").description("GitHub issue pipeline automation \u2014 Day 1 idea to Go Live").version(pkg.version).option("--dry-run", "show what would happen without executing").option("--json", "output structured JSON").option("-v, --verbose", "show API calls and file writes").option("--debug", "full payloads and timings").option("--model <model>", "Claude model override").option("--provider <provider>", "AI provider override (default: claude)");
36
37
  const STAGE_LABELS = {
37
38
  run: "pipeline",
38
39
  init: "init",
@@ -44,8 +45,9 @@ function requireMgw () {
44
45
  next: "next"
45
46
  };
46
47
  async function runAiCommand(commandName, userPrompt, opts) {
47
- assertClaudeAvailable();
48
- const cmdFile = path.join(getCommandsDir(), `${commandName}.md`);
48
+ const provider = ProviderManager.getProvider(opts.provider);
49
+ provider.assertAvailable();
50
+ const cmdFile = path.join(provider.getCommandsDir(), `${commandName}.md`);
49
51
  const stageLabel = STAGE_LABELS[commandName] || commandName;
50
52
  const issueMatch = String(userPrompt).match(/^\d+/);
51
53
  const timer = startTimer({
@@ -57,7 +59,7 @@ function requireMgw () {
57
59
  spinner.start();
58
60
  let result;
59
61
  try {
60
- result = await invokeClaude(cmdFile, userPrompt, {
62
+ result = await provider.invoke(cmdFile, userPrompt, {
61
63
  model: opts.model,
62
64
  quiet: true,
63
65
  dryRun: opts.dryRun,
@@ -84,7 +86,7 @@ function requireMgw () {
84
86
  spinner.start();
85
87
  await new Promise((r) => setTimeout(r, 80));
86
88
  spinner.stop();
87
- const result = await invokeClaude(cmdFile, userPrompt, {
89
+ const result = await provider.invoke(cmdFile, userPrompt, {
88
90
  model: opts.model,
89
91
  quiet: false,
90
92
  dryRun: opts.dryRun,
@@ -433,7 +435,8 @@ function requireMgw () {
433
435
  }
434
436
  });
435
437
  program.command("help").description("Show command reference").action(function() {
436
- const helpMdPath = path.join(getCommandsDir(), "help.md");
438
+ const opts = this.optsWithGlobals();
439
+ const helpMdPath = path.join(ProviderManager.getProvider(opts.provider).getCommandsDir(), "help.md");
437
440
  let helpText;
438
441
  try {
439
442
  const raw = fs.readFileSync(helpMdPath, "utf-8");